diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index f2a48741..651919f9 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -6,6 +6,50 @@ follow [https://changelog.md/](https://changelog.md/) guidelines. ## [Unreleased] +## [52.3] - 2024-10-08 + +### ADDED + +- Several background notification processing reliability improvements + +### FIXED + +- Fixed cancel payment dialog copy. +- Fixed low occurrence crash when tapping delete wallet. +- Fixed low occurrence crash when entering a payment detail from a notification or recent apps +- Fixed mempool space tx link destination (after mempool.space changed url destination) +- Released certain resources (like SQLite cursors) that weren't being released to avoid leaks. +- Fixed countDownTimer lifecycle issue (not being cancelled) in newop (send) screen + +### CHANGED + +- Upgrade Firebase to get SDK to get Release Monitoring feature. +- Upgrade Firebase dependencies using BOM (Bill Of Materials). firebase-bom:32.6.0 +- Upgrade Firebase Messaging as part of firebase-bom:32.6.0 +- Upgrade Firebase Crashlytics as part of firebase-bom:32.6.0 +- Upgrade Firebase Analytics as part of firebase-bom:32.6.0 +- Update Go version to 1.22.1 +- Update Kotlin version to 1.8.20 +- Upgrade Dagger to 2.52 +- Upgrade SQDelight to 1.5.5 +- Upgrade libwallet dependencies (like btcd) +- Removed secp256k1-zkp C code from lib wallet. Replaced it with the native Go version. +- Refactored and enhanced musig code in libwallet +- Enhanced debugging and tracing metadata for ANRs troubleshooting +- Avoid unnecessary writes to Android Keystore +- Avoid double write/delete to Android Keystore in logouts +- Reduce Butterknife usage (in slow effort to migrate Butterknife to ViewBinding) +- Simplified and cleaned libwallet build scripts +- Avoid needless requests when creating a new wallet +- Refactored (cleaned up) delete wallet logic +- Removed Stetho library (long time unused) +- Kotlinized all the repositories, part of an effort to delay access to shared prefs (lazy access). +- Introduced MockK for mocking kotlin classes, fields and properties. +- Upgrade AndroidX Navigation lib version to 2.7.7 +- Upgrade AndroidX WorkManager lib version to 2.9.0 +- Upgrade Android lifecycle aware components libs to 2.6.2 +- Removed no longer needed *-ktx dependencies + ## [52.2] - 2024-09-23 ### FIXED diff --git a/android/Dockerfile b/android/Dockerfile index 0781f849..2abb79f6 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.21.11 +ENV GO_VERSION 1.22.1 RUN apt-get update \ && apt-get install --yes --no-install-recommends \ diff --git a/android/apollo/build.gradle b/android/apollo/build.gradle index a95528c2..93c3ad7c 100644 --- a/android/apollo/build.gradle +++ b/android/apollo/build.gradle @@ -21,6 +21,8 @@ apply from: "${project.rootDir}/linters/pmd/check-android.gradle" //apply from: "${project.rootDir}/linters/findbugs/check-android.gradle" android { + namespace "io.muun.apollo.lib" + compileSdk 34 defaultConfig { @@ -70,7 +72,8 @@ ext { // TODO this currently needs to match common's retrofit version. We should refactor to avoid // this discipline on our part. We probably need to extract it to a constant in top-level gradle version_retrofit = '2.5.0' - version_workmanager = '2.7.1' + version_workmanager = '2.9.0' // Next version upgrade requires bump minSdk to 21 + version_mockk = '1.13.7' // Latest version targeting kotlin 1.8.20 } @@ -105,7 +108,7 @@ dependencies { // Firebase: // Import the Firebase BoM - api platform('com.google.firebase:firebase-bom:32.1.1') + api platform('com.google.firebase:firebase-bom:32.6.0') // When using the BoM, you don't specify versions in Firebase library dependencies // Push Notifications: @@ -119,16 +122,11 @@ dependencies { // WorkManager: (see https://github.com/muun/muun/issues/5895) // Guide: https://developer.android.com/topic/libraries/architecture/workmanager/migrating-fb api "androidx.work:work-runtime:$version_workmanager" // api as needs to be init at app.OnCreate - api "androidx.work:work-runtime-ktx:$version_workmanager" // Kotlin extensions - - api 'com.facebook.stetho:stetho:1.5.0' - api 'com.facebook.stetho:stetho-okhttp3:1.5.0' - api 'com.facebook.stetho:stetho-timber:1.5.0@aar' // Storage: implementation 'com.squareup.sqlbrite3:sqlbrite:3.2.0' - implementation "com.squareup.sqldelight:android-driver:1.5.3" - implementation "com.squareup.sqldelight:rxjava2-extensions:1.5.3" + implementation "com.squareup.sqldelight:android-driver:1.5.5" + implementation "com.squareup.sqldelight:rxjava2-extensions:1.5.5" // Networking: implementation "com.squareup.retrofit2:retrofit:$version_retrofit" @@ -162,6 +160,8 @@ dependencies { testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0" testImplementation 'org.assertj:assertj-core:3.8.0' testImplementation 'junit:junit:4.12' + testImplementation "io.mockk:mockk:$version_mockk" + // Can't use Jake Wharton's threeten lib for test. For more info see: // https://github.com/JakeWharton/ThreeTenABP/issues/47 testImplementation 'org.threeten:threetenbp:1.6.8' diff --git a/android/apollo/src/main/java/io/muun/apollo/data/db/base/BaseDao.java b/android/apollo/src/main/java/io/muun/apollo/data/db/base/BaseDao.java index 74da1927..b4106df2 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/db/base/BaseDao.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/db/base/BaseDao.java @@ -27,9 +27,13 @@ public abstract class BaseDao { protected final String tableName; + protected BriteDatabase briteDb; + protected SupportSQLiteDatabase db; + protected Database delightDb; + protected Scheduler scheduler; /** @@ -49,10 +53,13 @@ public void setDb(BriteDatabase briteDatabase, Database delightDb, final Schedul this.delightDb = delightDb; this.scheduler = scheduler; - Preconditions.checkState( - briteDatabase.query("SELECT COUNT(*) FROM " + tableName).getCount() == 1, - "expected table " + tableName + " to exist" - ); + + try (Cursor cursor = briteDatabase.query("SELECT COUNT(*) FROM " + tableName)) { + Preconditions.checkState( + cursor.getCount() == 1, + "expected table " + tableName + " to exist" + ); + } } /** @@ -182,9 +189,11 @@ public Observable store(@Nonnull ModelT element) { // FIXME: This is a racy operation. If another thread inserts any row, the id // won't match. - final Cursor query = briteDb.query("SELECT last_insert_rowid();"); - Preconditions.checkState(query.moveToFirst()); - final long insertedId = query.getLong(0); + final long insertedId; + try (Cursor query = briteDb.query("SELECT last_insert_rowid();")) { + Preconditions.checkState(query.moveToFirst()); + insertedId = query.getLong(0); + } if (insertedId < 0) { throw new RuntimeException("Error while inserting new value in the db"); } diff --git a/android/apollo/src/main/java/io/muun/apollo/data/db/base/HoustonIdDao.java b/android/apollo/src/main/java/io/muun/apollo/data/db/base/HoustonIdDao.java index 80fb36d2..8782b9b6 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/db/base/HoustonIdDao.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/db/base/HoustonIdDao.java @@ -30,19 +30,20 @@ public Observable store(@NonNull ModelT element) { return super.store(element); } - final Cursor cursor = briteDb.query( - "select id from " + tableName + " where hid = ?", - String.valueOf(element.getHid()) - ); - - if (cursor.getCount() == 0) { - return super.store(element); - } - - if (cursor.getCount() == 1) { - cursor.moveToFirst(); - element.setId(cursor.getLong(cursor.getColumnIndex("id"))); - return super.store(element); + final String id = String.valueOf(element.getHid()); + try (Cursor cursor = briteDb.query( + "select id from " + tableName + " where hid = ?", id + )) { + + if (cursor.getCount() == 0) { + return super.store(element); + } + + if (cursor.getCount() == 1) { + cursor.moveToFirst(); + element.setId(cursor.getLong(cursor.getColumnIndex("id"))); + return super.store(element); + } } return Observable.error( diff --git a/android/apollo/src/main/java/io/muun/apollo/data/di/DataComponent.java b/android/apollo/src/main/java/io/muun/apollo/data/di/DataComponent.java index fe7b81af..f8e0fd6a 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/di/DataComponent.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/di/DataComponent.java @@ -5,6 +5,7 @@ import io.muun.apollo.data.async.gcm.GcmMessageListenerService; import io.muun.apollo.data.async.tasks.MuunWorkerFactory; import io.muun.apollo.data.async.tasks.TaskScheduler; +import io.muun.apollo.data.db.DaoManager; import io.muun.apollo.data.db.contact.ContactDao; import io.muun.apollo.data.db.operation.OperationDao; import io.muun.apollo.data.db.public_profile.PublicProfileDao; @@ -110,4 +111,6 @@ public interface DataComponent extends ActionComponent { NotificationService notificationService(); Analytics analytics(); + + DaoManager daoManager(); } 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 010cc851..b814635a 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 @@ -10,7 +10,6 @@ import io.muun.apollo.domain.model.IncomingSwap import io.muun.apollo.domain.model.IncomingSwapHtlc import io.muun.apollo.domain.model.NextTransactionSize import io.muun.apollo.domain.model.Operation -import io.muun.apollo.domain.model.PaymentRequest import io.muun.apollo.domain.model.PublicProfile import io.muun.apollo.domain.model.SubmarineSwap import io.muun.apollo.domain.model.SubmarineSwapBestRouteFees @@ -44,7 +43,6 @@ import org.javamoney.moneta.Money import org.threeten.bp.ZonedDateTime import javax.money.CurrencyUnit import javax.money.Monetary -import javax.money.MonetaryAmount import kotlin.random.Random object Gen { @@ -250,7 +248,9 @@ object Gen { pair.second, transactionHash() + ":0", UtxoStatus.CONFIRMED, - pair.second + pair.second, + "m/schema:1'/recovery:1'", + 1 ) /** @@ -269,6 +269,14 @@ object Gen { fun nextTransactionSize(vararg entries: SizeForAmount, expectedDebtInSat: Long = 0) = NextTransactionSize(sizeProgression(*entries), 1, expectedDebtInSat) + /** + * Get a FeeBumpFunctions vector + */ + fun feeBumpFunctions() = + listOf( + "QsgAAAAAAAAAAAAAf4AAAD+AAABAAAAA", // [[100, 0, 0], [+Inf, 1, 2]] + "f4AAAD+AAAAAAAAA" // [[+Inf, 1, 0]]] + ) /** * Get a Transaction Hash. */ diff --git a/android/apollo/src/main/java/io/muun/apollo/data/external/Globals.kt b/android/apollo/src/main/java/io/muun/apollo/data/external/Globals.kt index db7513b9..237a5f17 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/external/Globals.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/external/Globals.kt @@ -60,21 +60,6 @@ abstract class Globals { */ abstract val deviceManufacturer: String - /** - * Get the fingerprint of the device where app is running. - */ - abstract val fingerprint: String - - /** - * Get the hardware name of the device where app is running. - */ - abstract val hardware: String - - /** - * Get the bootloader name of the device where app is running. - */ - abstract val bootloader: String - /** * Get the bitcoin network specs/parameters of the network this build is using. */ diff --git a/android/apollo/src/main/java/io/muun/apollo/data/logging/Crashlytics.kt b/android/apollo/src/main/java/io/muun/apollo/data/logging/Crashlytics.kt index 467a94c5..97c70426 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/logging/Crashlytics.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/logging/Crashlytics.kt @@ -110,9 +110,7 @@ object Crashlytics { } analyticsProvider?.report( - AnalyticsEvent.E_CRASHLYTICS_ERROR( - report.error.javaClass.simpleName + ":" + report.error.localizedMessage - ) + AnalyticsEvent.E_CRASHLYTICS_ERROR(report) ) crashlytics?.recordException(report.error) diff --git a/android/apollo/src/main/java/io/muun/apollo/data/net/ApiObjectsMapper.java b/android/apollo/src/main/java/io/muun/apollo/data/net/ApiObjectsMapper.java index 273ae331..a61a8593 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/net/ApiObjectsMapper.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/net/ApiObjectsMapper.java @@ -1,9 +1,11 @@ package io.muun.apollo.data.net; import io.muun.apollo.data.external.Globals; +import io.muun.apollo.data.os.BuildInfoProvider; import io.muun.apollo.data.os.CpuInfo; import io.muun.apollo.data.os.GooglePlayHelper; import io.muun.apollo.data.os.GooglePlayServicesHelper; +import io.muun.apollo.data.os.PackageManagerInfoProvider; import io.muun.apollo.data.serialization.dates.ApolloZonedDateTime; import io.muun.apollo.domain.libwallet.Invoice; import io.muun.apollo.domain.model.BackgroundEvent; @@ -17,6 +19,9 @@ import io.muun.apollo.domain.model.SubmarineSwapRequest; import io.muun.apollo.domain.model.SystemUserInfo; import io.muun.apollo.domain.model.user.UserProfile; +import io.muun.common.api.AndroidAppInfoJson; +import io.muun.common.api.AndroidBuildInfoJson; +import io.muun.common.api.AndroidDeviceFeaturesJson; import io.muun.common.api.AndroidSystemUserInfoJson; import io.muun.common.api.BackgroundEventJson; import io.muun.common.api.BitcoinAmountJson; @@ -41,6 +46,7 @@ import io.muun.common.api.PublicProfileJson; import io.muun.common.api.StartEmailSetupJson; import io.muun.common.api.SubmarineSwapRequestJson; +import io.muun.common.api.UnconfirmedOutpointsJson; import io.muun.common.api.UserInvoiceJson; import io.muun.common.api.UserProfileJson; import io.muun.common.crypto.ChallengePublicKey; @@ -49,6 +55,8 @@ import io.muun.common.exception.MissingCaseError; import io.muun.common.model.DebtType; import io.muun.common.model.PhoneNumber; +import io.muun.common.model.SizeForAmount; +import io.muun.common.model.UtxoStatus; import io.muun.common.model.challenge.ChallengeSetup; import io.muun.common.model.challenge.ChallengeSignature; import io.muun.common.utils.BitcoinUtils; @@ -74,7 +82,8 @@ public class ApiObjectsMapper { - @Inject ApiObjectsMapper() { + @Inject + ApiObjectsMapper() { } /** @@ -211,7 +220,16 @@ public ClientJson mapClient( @NonNull final String glEsVersion, @NonNull final CpuInfo cpuInfo, @NonNull GooglePlayServicesHelper.PlayServicesInfo playServicesInfo, - @NonNull GooglePlayHelper.PlayInfo playInfo + @NonNull GooglePlayHelper.PlayInfo playInfo, + final BuildInfoProvider.BuildInfo buildInfo, + final PackageManagerInfoProvider.AppInfo appInfo, + final PackageManagerInfoProvider.DeviceFeatures deviceFeatures, + final String signatureHash, + final int quickEmProps, + final int emArchitecture, + final String securityEnhancedBuild, + final String bridgeRootService, + final long appSize ) { return new ClientJson( ClientTypeJson.APOLLO, @@ -232,9 +250,10 @@ public ClientJson mapClient( installSourceInfo.getInstallingPackageName(), installSourceInfo.getInitiatingPackageName(), installSourceInfo.getInitiatingPackageSigningInfo(), - Globals.INSTANCE.getFingerprint(), - Globals.INSTANCE.getHardware(), - Globals.INSTANCE.getBootloader(), + //@TODO: Redundancy removal: Next 3 and also at Houston (data present in buildInfo) + buildInfo.getFingerprint(), + buildInfo.getHardware(), + buildInfo.getBootloader(), bootCount, glEsVersion, cpuInfo.getLegacyData(), @@ -244,7 +263,16 @@ public ClientJson mapClient( playServicesInfo.getVersionName(), playServicesInfo.getClientVersionCode(), playInfo.getVersionCode(), - playInfo.getVersionName() + playInfo.getVersionName(), + mapBuildInfo(buildInfo), + mapAppInfo(appInfo), + mapDeviceFeatures(deviceFeatures), + signatureHash, + mapQemuProps(quickEmProps), + emArchitecture, + mapSeLinux(securityEnhancedBuild), + mapAdbRootService(bridgeRootService), + appSize ); } @@ -279,6 +307,79 @@ private List mapSystemUsersInfo(List return result; } + private AndroidBuildInfoJson mapBuildInfo(BuildInfoProvider.BuildInfo buildInfo) { + return new AndroidBuildInfoJson( + buildInfo.getAbis(), + buildInfo.getFingerprint(), + buildInfo.getHardware(), + buildInfo.getBootloader(), + buildInfo.getManufacturer(), + buildInfo.getBrand(), + buildInfo.getDisplay(), + buildInfo.getTime(), + buildInfo.getHost(), + buildInfo.getType(), + buildInfo.getRadioVersion(), + buildInfo.getSecurityPatch(), + buildInfo.getBaseOs() + ); + } + + private AndroidAppInfoJson mapAppInfo(PackageManagerInfoProvider.AppInfo appInfo) { + return new AndroidAppInfoJson( + appInfo.getName(), + appInfo.getLabel(), + appInfo.getIcon(), + appInfo.getDebuggable(), + appInfo.getPersistent() + ); + } + + + private AndroidDeviceFeaturesJson mapDeviceFeatures( + PackageManagerInfoProvider.DeviceFeatures deviceFeatures + ) { + return new AndroidDeviceFeaturesJson( + deviceFeatures.getTouch(), + deviceFeatures.getProximity(), + deviceFeatures.getAccelerometer(), + deviceFeatures.getGyro(), + deviceFeatures.getCompass(), + deviceFeatures.getTelephony(), + deviceFeatures.getCdma(), + deviceFeatures.getGsm(), + deviceFeatures.getCameras(), + deviceFeatures.getPc(), + deviceFeatures.getPip(), + deviceFeatures.getDactylogram() + ); + } + + @Nullable + private Boolean mapAdbRootService(String signalValue) { + if (signalValue == null) { + return null; + } + return signalValue.equals("1"); + } + + @Nullable + private Boolean mapSeLinux(String signalValue) { + if (signalValue == null) { + return null; + } + return signalValue.equals("1"); + } + + @Nullable + private Boolean mapQemuProps(Integer signalValue) { + if (signalValue == null) { + return null; + } + return signalValue == 1; + } + + /** * Map a CreateFirstSession object. */ @@ -296,8 +397,16 @@ public CreateFirstSessionJson mapCreateFirstSession( @NonNull String glEsVersion, @NonNull CpuInfo cpuInfo, @NonNull GooglePlayServicesHelper.PlayServicesInfo playServicesInfo, - @NonNull GooglePlayHelper.PlayInfo playInfo - + @NonNull GooglePlayHelper.PlayInfo playInfo, + BuildInfoProvider.BuildInfo buildInfo, + PackageManagerInfoProvider.AppInfo appInfo, + PackageManagerInfoProvider.DeviceFeatures deviceFeatures, + String signatureHash, + Integer quickEmProps, + Integer emArchitecture, + String securityEnhancedBuild, + String bridgeRootService, + Long appSize ) { return new CreateFirstSessionJson( @@ -312,7 +421,16 @@ public CreateFirstSessionJson mapCreateFirstSession( glEsVersion, cpuInfo, playServicesInfo, - playInfo + playInfo, + buildInfo, + appInfo, + deviceFeatures, + signatureHash, + quickEmProps, + emArchitecture, + securityEnhancedBuild, + bridgeRootService, + appSize ), gcmToken, primaryCurrency, @@ -337,7 +455,16 @@ public CreateLoginSessionJson mapCreateLoginSession( @NonNull String glEsVersion, @NonNull CpuInfo cpuInfo, @NonNull GooglePlayServicesHelper.PlayServicesInfo playServicesInfo, - @NonNull GooglePlayHelper.PlayInfo playInfo + @NonNull GooglePlayHelper.PlayInfo playInfo, + final BuildInfoProvider.BuildInfo buildInfo, + final PackageManagerInfoProvider.AppInfo appInfo, + final PackageManagerInfoProvider.DeviceFeatures deviceFeatures, + final String signatureHash, + final Integer quickEmProps, + final Integer emArchitecture, + final String securityEnhancedBuild, + final String bridgeRootService, + final Long appSize ) { return new CreateLoginSessionJson( @@ -352,7 +479,16 @@ public CreateLoginSessionJson mapCreateLoginSession( glEsVersion, cpuInfo, playServicesInfo, - playInfo + playInfo, + buildInfo, + appInfo, + deviceFeatures, + signatureHash, + quickEmProps, + emArchitecture, + securityEnhancedBuild, + bridgeRootService, + appSize ), gcmToken, email @@ -375,7 +511,16 @@ public CreateRcLoginSessionJson mapCreateRcLoginSession( @NonNull String glEsVersion, @NonNull CpuInfo cpuInfo, @NonNull GooglePlayServicesHelper.PlayServicesInfo playServicesInfo, - @NonNull GooglePlayHelper.PlayInfo playInfo + @NonNull GooglePlayHelper.PlayInfo playInfo, + final BuildInfoProvider.BuildInfo buildInfo, + final PackageManagerInfoProvider.AppInfo appInfo, + final PackageManagerInfoProvider.DeviceFeatures deviceFeatures, + final String signatureHash, + final Integer quickEmProps, + final Integer emArchitecture, + final String securityEnhancedBuild, + final String bridgeRootService, + final Long appSize ) { return new CreateRcLoginSessionJson( @@ -390,7 +535,16 @@ public CreateRcLoginSessionJson mapCreateRcLoginSession( glEsVersion, cpuInfo, playServicesInfo, - playInfo + playInfo, + buildInfo, + appInfo, + deviceFeatures, + signatureHash, + quickEmProps, + emArchitecture, + securityEnhancedBuild, + bridgeRootService, + appSize ), gcmToken, new ChallengeKeyJson( @@ -549,7 +703,8 @@ public UserInvoiceJson mapUserInvoice(final Invoice.InvoiceSecret invoice) { * Map fulfillment data. */ public IncomingSwapFulfillmentData mapFulfillmentData( - final IncomingSwapFulfillmentDataJson json) { + final IncomingSwapFulfillmentDataJson json + ) { return new IncomingSwapFulfillmentData( Encodings.hexToBytes(json.fulfillmentTxHex), @@ -573,8 +728,10 @@ public ExportEmergencyKitJson mapEmergencyKitExport(EmergencyKitExport export) { } @Nullable - private ExportEmergencyKitJson.Method mapExportMethod(@NotNull EmergencyKitExport.Method - meth) { + private ExportEmergencyKitJson.Method mapExportMethod( + @NotNull EmergencyKitExport.Method + meth + ) { switch (meth) { case UNKNOWN: return null; @@ -588,4 +745,22 @@ private ExportEmergencyKitJson.Method mapExportMethod(@NotNull EmergencyKitExpor throw new MissingCaseError(meth); } } + + /** + * Creates a UnconfirmedOutpointsJson. + */ + @NotNull + public UnconfirmedOutpointsJson mapUnconfirmedOutpointsJson( + @NotNull List sizeProgression + ) { + final List unconfirmedUtxos = new ArrayList<>(); + + for (final SizeForAmount sizeForAmount : sizeProgression) { + if (sizeForAmount.status == UtxoStatus.UNCONFIRMED) { + unconfirmedUtxos.add(sizeForAmount.outpoint); + } + } + + return new UnconfirmedOutpointsJson(unconfirmedUtxos); + } } diff --git a/android/apollo/src/main/java/io/muun/apollo/data/net/ConnectivityInfoProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/net/ConnectivityInfoProvider.kt index 974420d0..d0080f8c 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/net/ConnectivityInfoProvider.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/net/ConnectivityInfoProvider.kt @@ -2,19 +2,31 @@ package io.muun.apollo.data.net import android.content.Context import android.net.ConnectivityManager +import android.net.LinkProperties import android.net.NetworkCapabilities import android.os.Build import androidx.annotation.RequiresApi import io.muun.apollo.data.os.Constants import io.muun.apollo.data.os.OS +import kotlinx.serialization.Serializable import javax.inject.Inject // TODO we should merge this and NetworkInfoProvider together -class ConnectivityInfoProvider @Inject constructor(context: Context) { +class ConnectivityInfoProvider @Inject constructor(private val context: Context) { private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + @Serializable + data class NetworkLink( + val interfaceName: String?, + val routesSize: Int?, + val routesInterfaces: Set?, + val hasGatewayRoute: Int, + val dnsAddresses: Set?, + val linkHttpProxyHost: String? + ) + val vpnState: Int get() { if (OS.supportsActiveNetwork()) { @@ -31,6 +43,94 @@ class ConnectivityInfoProvider @Inject constructor(context: Context) { } } + val proxyHttp: String + get() { + return System.getProperty("http.proxyHost") ?: "" + } + + val proxyHttps: String + get() { + return System.getProperty("https.proxyHost") ?: "" + } + + val proxySocks: String + get() { + return System.getProperty("socks.proxyHost") ?: "" + } + + + /** + * Retrieves the current network transport type of the device, available only for APIs >= 23. + * BackgroundExecutionMetricsProvider continues to use the NetworkInfo class for older versions, + * as it is the only way to collect this data and is not deprecated in those API levels. + * The responses remain consistent with the previous implementation, ensuring uniform data + * handling across all Android versions + */ + val currentTransportNewerApi: String + get() { + if (OS.supportsActiveNetwork()) { + val connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val activeNetwork = connectivityManager.activeNetwork ?: return "UNKNOWN" + val networkCapabilities = + connectivityManager.getNetworkCapabilities(activeNetwork) ?: return "UNKNOWN" + return when { + networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + -> "WIFI" + + networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + -> "MOBILE" + + networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) + -> "BLUETOOTH" + + networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) + -> "ETHERNET" + + networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) + -> "VPN" + + else -> "UNKNOWN" + } + } + return "UNKNOWN" + } + + val networkLink: NetworkLink? + get() { + if (!OS.supportsActiveNetwork()) + return null + + val activeNetwork = connectivityManager.activeNetwork + val linkProperties = connectivityManager.getLinkProperties(activeNetwork) + + val routesSize = linkProperties?.routes?.size + val routesInterfaces = linkProperties?.routes?.mapNotNull { it.`interface` }?.toSet() + val interfaceName = linkProperties?.interfaceName + val dnsAddresses = + linkProperties?.dnsServers?.map { it.hostAddress ?: Constants.EMPTY }?.toSet() + val linkHttpProxyHost = linkProperties?.httpProxy?.host ?: Constants.EMPTY + val hasGatewayRoute = getHasGatewayRoute(linkProperties) + return NetworkLink( + interfaceName, + routesSize, + routesInterfaces, + hasGatewayRoute, + dnsAddresses, + linkHttpProxyHost + ); + } + + private fun getHasGatewayRoute(linkProperties: LinkProperties?): Int { + if(!OS.supportsRouteHasGateway()) { + return -1 + } + return linkProperties?.routes + ?.any { route -> route.hasGateway() } + ?.let { if (it) 1 else 0 } + ?: 0 + } + @RequiresApi(Build.VERSION_CODES.M) private fun getVpnStateForNewerApi(): Int { val activeNetwork = connectivityManager.activeNetwork @@ -57,19 +157,4 @@ class ConnectivityInfoProvider @Inject constructor(context: Context) { return if (isVpnNetworkAvailable) 2 else 3 } - - val proxyHttp: String - get() { - return System.getProperty("http.proxyHost") ?: "" - } - - val proxyHttps: String - get() { - return System.getProperty("https.proxyHost") ?: "" - } - - val proxySocks: String - get() { - return System.getProperty("socks.proxyHost") ?: "" - } } \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/net/HoustonClient.java b/android/apollo/src/main/java/io/muun/apollo/data/net/HoustonClient.java index b1b88f2f..f5a7772e 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/net/HoustonClient.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/net/HoustonClient.java @@ -2,11 +2,15 @@ import io.muun.apollo.data.net.base.BaseClient; import io.muun.apollo.data.net.okio.ContentUriRequestBody; +import io.muun.apollo.data.os.BuildInfoProvider; import io.muun.apollo.data.os.CpuInfoProvider; +import io.muun.apollo.data.os.FileInfoProvider; import io.muun.apollo.data.os.GooglePlayHelper; import io.muun.apollo.data.os.GooglePlayServicesHelper; import io.muun.apollo.data.os.HardwareCapabilitiesProvider; import io.muun.apollo.data.os.OS_ExtensionsKt; +import io.muun.apollo.data.os.PackageManagerInfoProvider; +import io.muun.apollo.data.os.SystemCapabilitiesProvider; import io.muun.apollo.domain.errors.delete_wallet.NonEmptyWalletDeleteException; import io.muun.apollo.domain.errors.delete_wallet.UnsettledOperationsWalletDeleteException; import io.muun.apollo.domain.errors.newop.CyclicalSwapError; @@ -34,6 +38,7 @@ import io.muun.apollo.domain.model.PlayIntegrityToken; import io.muun.apollo.domain.model.PublicKeySet; import io.muun.apollo.domain.model.RealTimeData; +import io.muun.apollo.domain.model.RealTimeFees; import io.muun.apollo.domain.model.Sha256Hash; import io.muun.apollo.domain.model.SubmarineSwap; import io.muun.apollo.domain.model.SubmarineSwapRequest; @@ -71,6 +76,7 @@ import io.muun.common.crypto.hd.PublicKey; import io.muun.common.model.Diff; import io.muun.common.model.PhoneNumber; +import io.muun.common.model.SizeForAmount; import io.muun.common.model.VerificationType; import io.muun.common.model.challenge.Challenge; import io.muun.common.model.challenge.ChallengeSetup; @@ -112,6 +118,14 @@ public class HoustonClient extends BaseClient { private final GooglePlayHelper googlePlayHelper; + private final BuildInfoProvider buildInfoProvider; + + private final PackageManagerInfoProvider packageManagerInfoProvider; + + private final FileInfoProvider fileInfoProvider; + + private final SystemCapabilitiesProvider systemCapabilitiesProvider; + /** * Constructor. */ @@ -122,7 +136,11 @@ public HoustonClient( Context context, HardwareCapabilitiesProvider hardwareCapabilitiesProvider, GooglePlayServicesHelper googlePlayServicesHelper, - GooglePlayHelper googlePlayHelper + GooglePlayHelper googlePlayHelper, + BuildInfoProvider buildInfoProvider, + PackageManagerInfoProvider packageManagerInfoProvider, + FileInfoProvider fileInfoProvider, + SystemCapabilitiesProvider systemCapabilitiesProvider ) { super(HoustonService.class); @@ -133,6 +151,10 @@ public HoustonClient( this.hardwareCapabilitiesProvider = hardwareCapabilitiesProvider; this.googlePlayServicesHelper = googlePlayServicesHelper; this.googlePlayHelper = googlePlayHelper; + this.buildInfoProvider = buildInfoProvider; + this.packageManagerInfoProvider = packageManagerInfoProvider; + this.fileInfoProvider = fileInfoProvider; + this.systemCapabilitiesProvider = systemCapabilitiesProvider; } /** @@ -160,7 +182,16 @@ public Observable createFirstSession( hardwareCapabilitiesProvider.getGlEsVersion(), CpuInfoProvider.INSTANCE.getCpuInfo(), googlePlayServicesHelper.getPlayServicesInfo(), - googlePlayHelper.getPlayInfo() + googlePlayHelper.getPlayInfo(), + buildInfoProvider.getBuildInfo(), + packageManagerInfoProvider.getAppInfo(), + packageManagerInfoProvider.getDeviceFeatures(), + packageManagerInfoProvider.getSignatureHash(), + fileInfoProvider.getQuickEmProps(), + fileInfoProvider.getEmArchitecture(), + systemCapabilitiesProvider.getSecurityEnhancedBuild(), + systemCapabilitiesProvider.getBridgeRootService(), + fileInfoProvider.getAppSize() ); return getService().createFirstSession(params) @@ -190,7 +221,16 @@ public Observable createLoginSession( hardwareCapabilitiesProvider.getGlEsVersion(), CpuInfoProvider.INSTANCE.getCpuInfo(), googlePlayServicesHelper.getPlayServicesInfo(), - googlePlayHelper.getPlayInfo() + googlePlayHelper.getPlayInfo(), + buildInfoProvider.getBuildInfo(), + packageManagerInfoProvider.getAppInfo(), + packageManagerInfoProvider.getDeviceFeatures(), + packageManagerInfoProvider.getSignatureHash(), + fileInfoProvider.getQuickEmProps(), + fileInfoProvider.getEmArchitecture(), + systemCapabilitiesProvider.getSecurityEnhancedBuild(), + systemCapabilitiesProvider.getBridgeRootService(), + fileInfoProvider.getAppSize() ); return getService().createLoginSession(params) @@ -220,7 +260,16 @@ public Observable createRcLoginSession( hardwareCapabilitiesProvider.getGlEsVersion(), CpuInfoProvider.INSTANCE.getCpuInfo(), googlePlayServicesHelper.getPlayServicesInfo(), - googlePlayHelper.getPlayInfo() + googlePlayHelper.getPlayInfo(), + buildInfoProvider.getBuildInfo(), + packageManagerInfoProvider.getAppInfo(), + packageManagerInfoProvider.getDeviceFeatures(), + packageManagerInfoProvider.getSignatureHash(), + fileInfoProvider.getQuickEmProps(), + fileInfoProvider.getEmArchitecture(), + systemCapabilitiesProvider.getSecurityEnhancedBuild(), + systemCapabilitiesProvider.getBridgeRootService(), + fileInfoProvider.getAppSize() ); return getService().createRecoveryCodeLoginSession(session) @@ -648,6 +697,20 @@ public Observable fetchRealTimeData() { return getService().fetchRealTimeData().map(modelMapper::mapRealTimeData); } + /** + * Fetch Realtime fees data. It returns all fee related data, including + * fee bump functions. + * + * @param sizeProgression from NTS + */ + public Observable fetchRealTimeFees( + List sizeProgression + ) { + return getService() + .fetchRealTimeFees(apiMapper.mapUnconfirmedOutpointsJson(sizeProgression)) + .map(modelMapper::mapRealTimeFees); + } + /** * Request a challenge from Houston. */ diff --git a/android/apollo/src/main/java/io/muun/apollo/data/net/ModelObjectsMapper.java b/android/apollo/src/main/java/io/muun/apollo/data/net/ModelObjectsMapper.java index 12846349..6ac7b5ac 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/net/ModelObjectsMapper.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/net/ModelObjectsMapper.java @@ -23,6 +23,7 @@ import io.muun.apollo.domain.model.PublicKeySet; import io.muun.apollo.domain.model.PublicProfile; import io.muun.apollo.domain.model.RealTimeData; +import io.muun.apollo.domain.model.RealTimeFees; import io.muun.apollo.domain.model.SubmarineSwap; import io.muun.apollo.domain.model.TransactionPushed; import io.muun.apollo.domain.model.tx.PartiallySignedTransaction; @@ -51,6 +52,7 @@ import io.muun.common.api.PhoneNumberJson; import io.muun.common.api.PublicKeySetJson; import io.muun.common.api.PublicProfileJson; +import io.muun.common.api.RealTimeFeesJson; import io.muun.common.api.SizeForAmountJson; import io.muun.common.api.SubmarineSwapJson; import io.muun.common.api.TransactionPushedJson; @@ -238,7 +240,8 @@ private PublicProfile mapPublicProfile(@NotNull PublicProfileJson publicProfile) */ @NotNull private ExchangeRateWindow mapExchangeRateWindow( - @NotNull io.muun.common.api.ExchangeRateWindow window) { + @NotNull io.muun.common.api.ExchangeRateWindow window + ) { return new ExchangeRateWindow( window.id, @@ -411,6 +414,30 @@ public RealTimeData mapRealTimeData(@NotNull io.muun.common.api.RealTimeData rea ); } + /** + * Create a bag of real-time fees data provided by Houston. + */ + @NotNull + public RealTimeFees mapRealTimeFees(@NotNull RealTimeFeesJson realTimeFeesJson) { + // Convert to domain model FeeWindow + final FeeWindow feeWindow = new FeeWindow( + 1L, // It will be deleted later + mapZonedDateTime(realTimeFeesJson.computedAt), + realTimeFeesJson.targetFeeRates.confTargetToTargetFeeRateInSatPerVbyte, + realTimeFeesJson.targetFeeRates.fastConfTarget, + realTimeFeesJson.targetFeeRates.mediumConfTarget, + realTimeFeesJson.targetFeeRates.slowConfTarget + ); + + return new RealTimeFees( + realTimeFeesJson.feeBumpFunctions, + feeWindow, + realTimeFeesJson.minMempoolFeeRateInSatPerVbyte, + realTimeFeesJson.minFeeRateIncrementToReplaceByFeeInSatPerVbyte, + mapZonedDateTime(realTimeFeesJson.computedAt) + ); + } + private List mapForwadingPolicies( final List forwardingPolicies ) { @@ -474,7 +501,9 @@ private SizeForAmount mapSizeForAmount(@NotNull SizeForAmountJson sizeForAmount) sizeForAmount.sizeInBytes.intValue(), sizeForAmount.outpoint, UtxoStatus.fromJson(sizeForAmount.status), - sizeForAmount.deltaInWeightUnits + sizeForAmount.deltaInWeightUnits, + sizeForAmount.derivationPath, + sizeForAmount.addressVersion ); } diff --git a/android/apollo/src/main/java/io/muun/apollo/data/net/NetworkInfoProvider.java b/android/apollo/src/main/java/io/muun/apollo/data/net/NetworkInfoProvider.java index 2ad80aaf..fc1371ca 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/net/NetworkInfoProvider.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/net/NetworkInfoProvider.java @@ -42,9 +42,9 @@ public NetworkInfoProvider(Context context) { /** * Get the type of network of the current active network (e.g WIFI, MOBILE). + * IMPORTANT NOTE: This code is USED ONLY FOR API < 23. + * For API 23+ we are using ConnectivityManager with NetworkCapabilities#hasTransport */ - // TODO for API 23+ we should stop using NetworkInfo() and getActiveNetwork() + - // NetworkCapabilities#hasTransport public String getCurrentTransport() { final Optional activeNetworkInfo = watchNetworkInfo().toBlocking().first(); return activeNetworkInfo.map(NetworkInfo::getTypeName).orElse("UNKNOWN"); diff --git a/android/apollo/src/main/java/io/muun/apollo/data/net/TrafficStatsInfoProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/net/TrafficStatsInfoProvider.kt new file mode 100644 index 00000000..3b2785e7 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/net/TrafficStatsInfoProvider.kt @@ -0,0 +1,12 @@ +package io.muun.apollo.data.net + +import android.net.TrafficStats +import javax.inject.Inject + +class TrafficStatsInfoProvider @Inject constructor() { + + val androidMobileRxTraffic: Long + get() { + return TrafficStats.getMobileRxBytes() + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/net/base/BaseClient.kt b/android/apollo/src/main/java/io/muun/apollo/data/net/base/BaseClient.kt index 82e653dc..599c0e53 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/net/base/BaseClient.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/net/base/BaseClient.kt @@ -1,6 +1,5 @@ package io.muun.apollo.data.net.base -import com.facebook.stetho.okhttp3.StethoInterceptor import io.muun.apollo.data.external.Globals import io.muun.apollo.data.external.HoustonConfig import io.muun.apollo.data.net.base.interceptor.AuthHeaderInterceptor @@ -103,10 +102,6 @@ open class BaseClient protected constructor( .setLevel(HttpLoggingInterceptor.Level.BODY) builder.addInterceptor(loggingInterceptor) } - - if (!Globals.INSTANCE.isReleaseBuild && config.getBoolean("net.interceptors.stetho")) { - builder.addNetworkInterceptor(StethoInterceptor()) - } return builder.build() } } \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/net/base/interceptor/AuthHeaderInterceptor.kt b/android/apollo/src/main/java/io/muun/apollo/data/net/base/interceptor/AuthHeaderInterceptor.kt index 34d39b6c..f7cdb338 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/net/base/interceptor/AuthHeaderInterceptor.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/net/base/interceptor/AuthHeaderInterceptor.kt @@ -26,18 +26,26 @@ class AuthHeaderInterceptor @Inject constructor( } override fun processResponse(originalResponse: Response): Response { - // Save the token in the response header if one is found + // Save the token in the response header if one is found... val authHeaderValue = originalResponse.header(HeaderUtils.AUTHORIZATION) - HeaderUtils.getBearerTokenFromHeader(authHeaderValue) - .ifPresent { serverJwt: String -> authRepository.storeServerJwt(serverJwt) } + val requestAuthHeaderValue = originalResponse.request().header(HeaderUtils.AUTHORIZATION) + + // But avoid redundant jwt saves (involves writing to keystore) if we can + if (authHeaderValue != requestAuthHeaderValue) { + HeaderUtils.getBearerTokenFromHeader(authHeaderValue) + .ifPresent { serverJwt: String -> + authRepository.storeServerJwt(serverJwt) + } + } // We need a reliable way (across all envs: local, CI, prd, etc...) to identify the logout // requests. We could inject HoustonConfig and build the entire URL (minus port) // or... we can do this :) val url = originalResponse.request().url().url().toString() val isLogout = url.endsWith("sessions/logout") + val isDelete = url.endsWith("user/delete") - if (!isLogout) { + if (!isLogout && !isDelete) { val sessionStatusHeaderValue = originalResponse.header(HeaderUtils.SESSION_STATUS) HeaderUtils.getSessionStatusFromHeader(sessionStatusHeaderValue) .ifPresent { sessionStatus: SessionStatus -> 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 16b8e008..f0801732 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 @@ -9,8 +9,8 @@ import android.os.PowerManager import android.os.SystemClock import io.muun.apollo.data.net.ConnectivityInfoProvider import io.muun.apollo.data.net.NetworkInfoProvider +import io.muun.apollo.data.net.TrafficStatsInfoProvider import kotlinx.serialization.Serializable -import java.util.Locale import javax.inject.Inject private const val UNSUPPORTED = -1 @@ -27,6 +27,8 @@ class BackgroundExecutionMetricsProvider @Inject constructor( private val resourcesInfoProvider: ResourcesInfoProvider, private val systemCapabilitiesProvider: SystemCapabilitiesProvider, private val dateTimeZoneProvider: DateTimeZoneProvider, + private val localeInfoProvider: LocaleInfoProvider, + private val trafficStatsInfoProvider: TrafficStatsInfoProvider, ) { private val powerManager: PowerManager by lazy { @@ -49,11 +51,11 @@ class BackgroundExecutionMetricsProvider @Inject constructor( hardwareCapabilitiesProvider.getFreeRamInBytes(), telephonyInfoProvider.dataState, telephonyInfoProvider.getSimStates().toTypedArray(), - networkInfoProvider.currentTransport, + getCurrentTransport(), SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(), hardwareCapabilitiesProvider.bootCount, - Locale.getDefault().toString(), + localeInfoProvider.language, dateTimeZoneProvider.timeZoneOffsetSeconds, telephonyInfoProvider.region.orElse(""), telephonyInfoProvider.simRegion, @@ -71,7 +73,20 @@ class BackgroundExecutionMetricsProvider @Inject constructor( connectivityInfoProvider.proxySocks, dateTimeZoneProvider.autoDateTime, dateTimeZoneProvider.autoTimeZone, - dateTimeZoneProvider.timeZoneId + dateTimeZoneProvider.timeZoneId, + localeInfoProvider.dateFormat, + localeInfoProvider.regionCode, + dateTimeZoneProvider.calendarIdentifier, + trafficStatsInfoProvider.androidMobileRxTraffic, + telephonyInfoProvider.simOperatorId, + telephonyInfoProvider.simOperatorName, + telephonyInfoProvider.mobileNetworkId, + telephonyInfoProvider.mobileNetworkName, + telephonyInfoProvider.mobileRoaming, + telephonyInfoProvider.mobileDataStatus, + telephonyInfoProvider.mobileRadioType, + telephonyInfoProvider.mobileDataActivity, + connectivityInfoProvider.networkLink ) @Suppress("ArrayInDataClass") @@ -114,6 +129,19 @@ class BackgroundExecutionMetricsProvider @Inject constructor( private val autoDateTime: Int, private val autoTimeZone: Int, private val timeZoneId: String, + private val androidDateFormat: String, + private val regionCode: String, + private val androidCalendarIdentifier: String, + private val androidMobileRxTraffic: Long, + private val androidSimOperatorId: String, + private val androidSimOperatorName: String, + private val androidMobileOperatorId: String, + private val mobileOperatorName: String, + private val androidMobileRoaming: Boolean, + private val androidMobileDataStatus: Int, + private val androidMobileRadioType: Int, + private val androidMobileDataActivity: Int, + private val androidNetworkLink: ConnectivityInfoProvider.NetworkLink? ) /** @@ -195,4 +223,20 @@ class BackgroundExecutionMetricsProvider @Inject constructor( else -> "UNREADABLE" } } + + /** + * While NetworkInfo (used in networkInfoProvider) has been deprecated, its functionality + * is complemented by ConnectivityManager methods for newer APIs. Backward compatibility + * is maintained in the response values to ensure consistent data handling across all + * Android versions + */ + private fun getCurrentTransport(): String { + return if (OS.supportsActiveNetwork()) { + connectivityInfoProvider.currentTransportNewerApi + } else { + networkInfoProvider.currentTransport + } + + } + } \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/BuildInfoProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/BuildInfoProvider.kt new file mode 100644 index 00000000..b8a76f7d --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/BuildInfoProvider.kt @@ -0,0 +1,68 @@ +package io.muun.apollo.data.os + +import android.os.Build +import kotlinx.serialization.Serializable +import javax.inject.Inject + +class BuildInfoProvider @Inject constructor() { + + @Serializable + data class BuildInfo( + val abis: List, + val fingerprint: String, + val hardware: String, + val bootloader: String, + val manufacturer: String, + val brand: String, + val display: String, + val time: Long, + val host: String, + val type: String, + val radioVersion: String, + val securityPatch: String, + val baseOs: String, + ) + + val buildInfo: BuildInfo + get() { + return BuildInfo( + getABIs(), + Build.FINGERPRINT, + Build.HARDWARE, + Build.BOOTLOADER, + Build.MANUFACTURER, + Build.BRAND, + Build.DISPLAY, + Build.TIME, + Build.HOST, + Build.TYPE, + Build.getRadioVersion(), + getSecurityPatch(), + getBaseOs() + ) + } + + private fun getABIs(): List { + return if (OS.supportsBuildSupportedAbis()) { + Build.SUPPORTED_ABIS.toList() + } else { + emptyList() + } + } + + private fun getSecurityPatch(): String { + return if (OS.supportsBuildVersionSecurityPatch()) { + Build.VERSION.SECURITY_PATCH + } else { + Constants.UNKNOWN + } + } + + private fun getBaseOs(): String { + return if (OS.supportsBuildVersionBaseOs()) { + Build.VERSION.BASE_OS + } else { + Constants.UNKNOWN + } + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/Constants.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/Constants.kt index b324a3cc..78597a5c 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/os/Constants.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/Constants.kt @@ -3,4 +3,8 @@ package io.muun.apollo.data.os object Constants { const val INT_UNKNOWN = -1 const val UNKNOWN = "UNKNOWN" + const val PRESENT = 1 + const val ABSENT = 0 + const val EMPTY = "" + const val INT_EXCEPTION = -2 } \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/CpuInfoProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/CpuInfoProvider.kt index b1407987..9c917203 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/os/CpuInfoProvider.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/CpuInfoProvider.kt @@ -3,7 +3,7 @@ package io.muun.apollo.data.os import io.muun.apollo.domain.errors.CpuInfoError import timber.log.Timber import java.io.File -import java.util.* +import java.util.Scanner private const val CPU_INFO_PATH = "/proc/cpuinfo" private const val CPU_INFO_KEY_VALUE_DELIMITER = ": " @@ -53,11 +53,13 @@ object CpuInfoProvider { */ private fun getLegacyCpuInfo(): Map { val map: MutableMap = HashMap() - val s = Scanner(File(CPU_INFO_PATH)) - while (s.hasNextLine()) { - val cpuInfoValues = s.nextLine().split(CPU_INFO_KEY_VALUE_DELIMITER) - if (cpuInfoValues.size > 1) map[cpuInfoValues[0].trim { it <= ' ' }] = - cpuInfoValues[1].trim { it <= ' ' } + + Scanner(File(CPU_INFO_PATH)).use { s -> + while (s.hasNextLine()) { + val cpuInfoValues = s.nextLine().split(CPU_INFO_KEY_VALUE_DELIMITER) + if (cpuInfoValues.size > 1) map[cpuInfoValues[0].trim { it <= ' ' }] = + cpuInfoValues[1].trim { it <= ' ' } + } } return map 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 index 281b3696..f1f9bd00 100644 --- 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 @@ -2,6 +2,7 @@ package io.muun.apollo.data.os import android.content.Context import android.provider.Settings +import java.util.Calendar import java.util.TimeZone import javax.inject.Inject @@ -34,4 +35,13 @@ class DateTimeZoneProvider @Inject constructor(private val context: Context) { get() { return TimeZone.getDefault().rawOffset / 1000L } + + val calendarIdentifier: String + get() { + if (OS.supportsCalendarType()) { + val calendar = Calendar.getInstance() + return calendar.calendarType + } + return Constants.UNKNOWN + } } \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/FileInfoProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/FileInfoProvider.kt new file mode 100644 index 00000000..1b6544a3 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/FileInfoProvider.kt @@ -0,0 +1,64 @@ +package io.muun.apollo.data.os + +import android.content.Context +import android.os.Environment +import timber.log.Timber +import java.io.File +import java.io.RandomAccessFile +import javax.inject.Inject + +class FileInfoProvider @Inject constructor(private val context: Context) { + + val quickEmProps: Int + get() { + return if (File(TorHelper.process("/flfgrz/ova/drzh-cebcf")).exists()) { + Constants.PRESENT + } else { + Constants.ABSENT + } + } + + val emArchitecture: Int + get() { + val fileNames = listOf( + TorHelper.process("yvo/yvop.fb"), + TorHelper.process("yvo/yvop64.fb"), + TorHelper.process("yvo/yvoz.fb"), + ) + val fileToAccess = findExistingFile(fileNames) ?: return Constants.INT_UNKNOWN + try { + RandomAccessFile(fileToAccess, "r").use { file -> + file.seek(0x12) + val buf = ByteArray(2) + file.readFully(buf) + file.close() + //The byte `buf[0]` is the least significant byte and + //the byte `buf[1]` is the most significant byte. + return buf[0].toInt() and 0xff or (buf[1].toInt() and 0xff shl 8) + } + } catch (e: Exception) { + Timber.e("emArchitecture", e) + return Constants.INT_EXCEPTION + } + } + + val appSize: Long + get() { + val file = File(context.applicationInfo.sourceDir) + return if (file.exists()) { + file.length() + } else { + Constants.INT_UNKNOWN.toLong() + } + } + + private fun findExistingFile(fileNames: List): File? { + for (fileName in fileNames) { + val file = File(Environment.getRootDirectory(), fileName) + if (file.exists()) { + return file + } + } + return null + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/HardwareCapabilitiesProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/HardwareCapabilitiesProvider.kt index 45f9ff16..550ed214 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/os/HardwareCapabilitiesProvider.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/HardwareCapabilitiesProvider.kt @@ -221,12 +221,7 @@ class HardwareCapabilitiesProvider @Inject constructor(private val context: Cont return null } - val mediaDrm = MediaDrm(drmProviderUuid) - val deviceIdBytes = mediaDrm.getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID) - - releaseMediaDRM(mediaDrm) - - return Encodings.bytesToHex(Hashes.sha256(deviceIdBytes)) + return getDrmIdFromClosableMediaDrm(drmProviderUuid) } catch (e: Exception) { // These two drm provider often return errors though they are listed as "supported" @@ -237,6 +232,24 @@ class HardwareCapabilitiesProvider @Inject constructor(private val context: Cont } } + /** + * Abstracts basically what try-with-resources does, but since MediaDrm isn't AutoClosable in + * all our supported Android versions, we need to do this manually/ad-hoc. + * TODO: once our minSdk > 28 (OS.supportsMediaDrmClose()) we could do this with kotlin's + * try-with-resources. + */ + private fun getDrmIdFromClosableMediaDrm(drmProviderUuid: UUID): String { + var mediaDrm: MediaDrm? = null + try { + mediaDrm = MediaDrm(drmProviderUuid) + val deviceIdBytes = mediaDrm.getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID) + + return Encodings.bytesToHex(Hashes.sha256(deviceIdBytes)) + } finally { + mediaDrm?.let(::releaseMediaDRM) + } + } + private fun releaseMediaDRM(drmObject: MediaDrm) { if (OS.supportsMediaDrmClose()) { drmObject.close() diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/LocaleInfoProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/LocaleInfoProvider.kt new file mode 100644 index 00000000..3d5b2e0a --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/LocaleInfoProvider.kt @@ -0,0 +1,28 @@ +package io.muun.apollo.data.os + +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.Locale +import javax.inject.Inject + +class LocaleInfoProvider @Inject constructor() { + + val language: String + get() { + return Locale.getDefault().toString() + } + + val dateFormat: String + get() { + val locale = Locale.getDefault() + val dateFormat = + DateFormat.getDateInstance(DateFormat.SHORT, locale) as SimpleDateFormat + return dateFormat.toPattern() + } + + val regionCode: String + get() { + val locale = Locale.getDefault() + return locale.country ?: Constants.EMPTY + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/OS.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/OS.kt index 0840fbd2..f126cefe 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/os/OS.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/OS.kt @@ -47,11 +47,17 @@ object OS { /** * Whether this OS supports telephonyManager#getSimState(int), which was introduced in - * 0-8-26, and deprecated in R-11-30 in favour of telephonyManager#getActiveModemCount(). + * O-8-26, and deprecated in R-11-30 in favour of telephonyManager#getActiveModemCount(). */ fun supportsGetSimStateWithSlotIndex(): Boolean = isAndroidOOrNewer() + /** + * Whether this OS supports telephonyManager#isDataEnabled(), which was introduced in O-8-26 + */ + fun supportsIsDataEnabled(): Boolean = + isAndroidOOrNewer() + /** * Whether this OS supports telephonyManager#getActiveModemCount(), which was introduced in * R-11-30. @@ -132,6 +138,51 @@ object OS { fun supportsGetSupportedCryptoSchemes(): Boolean = isAndroidROrNewer() + /** + * Whether this OS supports Build.VERSION.SECURITY_PATCH, which was introduced in M-6-23. + */ + fun supportsBuildVersionSecurityPatch(): Boolean = + isAndroidMOrNewer() + + /** + * Whether this OS supports Build.VERSION.BASE_OS, which was introduced in M-6-23. + */ + fun supportsBuildVersionBaseOs(): Boolean = + isAndroidMOrNewer() + + /** + * Whether this OS supports Build.SUPPORTED_ABIS, which was introduced in L-5.0-21. + */ + fun supportsBuildSupportedAbis(): Boolean = + isAndroidLOrNewer() + + /** + * Whether this OS supports Feature Picture and Picture, which was introduced in N-7-24. + */ + fun supportsPIP(): Boolean = + isAndroidNOrNewer() + + /** + * Whether this OS device has biometric hardware to detect a fingerprint, which was introduced + * in M-6-23. + */ + fun supportsDactylogram(): Boolean = + isAndroidMOrNewer() + + /** + * Whether this OS supports Feature PC, which was introduced in O-8.1-27. + */ + fun supportsFeaturePC(): Boolean = + isAndroidOMr1OrNewer() + + /** + * Whether this OS supports PackageManager.GET_SIGNING_CERTIFICATES, which was introduced + * in P-9-28. + */ + fun supportsGetSigningCerts(): Boolean = + isAndroidPOrNewer() + + /** * Whether this OS supports Settings.Global.BOOT_COUNT, which was introduced in N-7-24. */ @@ -145,6 +196,13 @@ object OS { fun supportsActiveNetwork(): Boolean = isAndroidMOrNewer() + /** + * Whether this OS supports RouteInfo#hasGateway(), which was introduced + * in Q-10-29. + */ + fun supportsRouteHasGateway(): Boolean = + isAndroidQOrNewer() + /** * Whether this OS supports ConnectivityManager#getNetworkCapabilities, which was introduced * in L-5.0-21. @@ -158,6 +216,19 @@ object OS { fun supportsSupportedAbis(): Boolean = isAndroidLOrNewer() + /** + * Whether this OS supports Calendar#calendarType, which was introduced in O-8-26. + */ + fun supportsCalendarType(): Boolean = + isAndroidOOrNewer() + + /** + * Whether this OS supports {@link android.security.KeyStoreException} public methods, which + * were introduced in T-13-33. + */ + fun supportsKeystoreExceptionPublicMethods(): Boolean = + isAndroidTiramisuOrNewer() + // PRIVATE STUFF: /** @@ -187,6 +258,13 @@ object OS { private fun isAndroidQExactly() = Build.VERSION.SDK_INT == Build.VERSION_CODES.Q + /** + * Whether this OS version is Q-10-29 or newer. + */ + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.Q) + private fun isAndroidQOrNewer() = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + /** * Whether this OS version is L-5.0-21 or newer. */ @@ -222,4 +300,17 @@ object OS { private fun isAndroidNOrNewer(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N + /** + * Whether this OS version is O-8.1-27 or newer. + */ + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O_MR1) + private fun isAndroidOMr1OrNewer(): Boolean = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 + + /** + * Whether this OS version is T-13-33 or newer. + */ + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU) + private fun isAndroidTiramisuOrNewer(): Boolean = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU } \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/PackageManagerInfoProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/PackageManagerInfoProvider.kt new file mode 100644 index 00000000..02cd4947 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/PackageManagerInfoProvider.kt @@ -0,0 +1,151 @@ +package io.muun.apollo.data.os + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import kotlinx.serialization.Serializable +import io.muun.common.utils.Encodings +import io.muun.common.utils.Hashes +import javax.inject.Inject + +class PackageManagerInfoProvider @Inject constructor(private val context: Context) { + + /** + * Structured AppInfo data. + */ + @Serializable + data class AppInfo( + val name: String, + val label: String, + val icon: Int, + val debuggable: Boolean, + val persistent: Boolean, + ) + + /** + * Structured DeviceFeatures data. + */ + @Serializable + data class DeviceFeatures( + val touch: Int, + val proximity: Int, + val accelerometer: Int, + val gyro: Int, + val compass: Int, + val telephony: Int, + val cdma: Int, + val gsm: Int, + val cameras: Int, + val pc: Int, + val pip: Int, + val dactylogram: Int, + ) + + val appInfo: AppInfo + get() { + val applicationInfo = context.packageManager.getApplicationInfo(context.packageName, 0) + return AppInfo( + applicationInfo.name ?: "", + applicationInfo.loadLabel(context.packageManager).toString(), + applicationInfo.icon, + (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0, + (applicationInfo.flags and ApplicationInfo.FLAG_PERSISTENT) != 0 + ) + } + + + val deviceFeatures: DeviceFeatures + get() { + val packageManager = context.packageManager + + val touchScreen = + hasFeature(packageManager, PackageManager.FEATURE_TOUCHSCREEN) + + val sensorProximity = + hasFeature(packageManager, PackageManager.FEATURE_SENSOR_PROXIMITY) + + val sensorAccelerometer = + hasFeature(packageManager, PackageManager.FEATURE_SENSOR_ACCELEROMETER) + + val sensorGyro = + hasFeature(packageManager, PackageManager.FEATURE_SENSOR_GYROSCOPE) + + val sensorCompass = + hasFeature(packageManager, PackageManager.FEATURE_SENSOR_COMPASS) + + val telephony = + hasFeature(packageManager, PackageManager.FEATURE_TELEPHONY) + + val telephonyCDMA = + hasFeature(packageManager, PackageManager.FEATURE_TELEPHONY_CDMA) + + val telephonyGSM = + hasFeature(packageManager, PackageManager.FEATURE_TELEPHONY_GSM) + + val cameraAny = + hasFeature(packageManager, PackageManager.FEATURE_CAMERA_ANY) + + val pip = if (OS.supportsPIP()) { + hasFeature(packageManager, PackageManager.FEATURE_PICTURE_IN_PICTURE) + } else { + Constants.INT_UNKNOWN + } + + val pc = if (OS.supportsFeaturePC()) { + hasFeature(packageManager, PackageManager.FEATURE_PC) + } else { + Constants.INT_UNKNOWN + } + + val dactylogram = if (OS.supportsDactylogram()) { + hasFeature(packageManager, PackageManager.FEATURE_FINGERPRINT) + } else { + Constants.INT_UNKNOWN + } + + return DeviceFeatures( + touchScreen, + sensorProximity, + sensorAccelerometer, + sensorGyro, + sensorCompass, + telephony, + telephonyCDMA, + telephonyGSM, + cameraAny, + pc, + pip, + dactylogram + ) + } + + private fun hasFeature(packageManager: PackageManager, feature: String): Int { + return if (packageManager.hasSystemFeature(feature)) { + Constants.PRESENT + } else { + Constants.ABSENT + } + } + + val signatureHash: String + get() { + if (OS.supportsGetSigningCerts()) { + val packageInfo = context.packageManager.getPackageInfo( + context.packageName, + PackageManager.GET_SIGNING_CERTIFICATES + ) + val signingInfo = packageInfo.signingInfo + val lastSignature = if (signingInfo.hasMultipleSigners()) { + signingInfo.apkContentsSigners.lastOrNull() + } else { + signingInfo.signingCertificateHistory.lastOrNull() + } + return if (lastSignature != null) { + Encodings.bytesToHex(Hashes.sha256(lastSignature.toByteArray())) + } else { + Constants.EMPTY + } + } + return Constants.UNKNOWN + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/SystemCapabilitiesProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/SystemCapabilitiesProvider.kt index cf17c21e..b243d255 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/os/SystemCapabilitiesProvider.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/SystemCapabilitiesProvider.kt @@ -47,6 +47,17 @@ class SystemCapabilitiesProvider @Inject constructor(private val context: Contex } ?: Constants.INT_UNKNOWN } + val securityEnhancedBuild: String + get() { + return getSysProp(TorHelper.process("eb.ohvyq.fryvahk")) + } + + + val bridgeRootService: String + get() { + return getSysProp(TorHelper.process("freivpr.nqo.ebbg")) + } + @SuppressLint("PrivateApi") fun getSysProp(name: String): String { return try { diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/TelephonyInfoProvider.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/TelephonyInfoProvider.kt index ffc41a1c..8a465543 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/os/TelephonyInfoProvider.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/TelephonyInfoProvider.kt @@ -70,6 +70,54 @@ open class TelephonyInfoProvider @Inject constructor(context: Context) { } } + val simOperatorId: String + get() { + return telephonyManager.simOperator + } + + val simOperatorName: String + get() { + return telephonyManager.simOperatorName + } + + val mobileNetworkId: String + get() { + return telephonyManager.networkOperator + } + + val mobileNetworkName: String + get() { + return telephonyManager.networkOperatorName + } + + val mobileRoaming: Boolean + get() { + return telephonyManager.isNetworkRoaming + } + + val mobileDataStatus: Int + get() { + if (OS.supportsIsDataEnabled()) { + return try { + if (telephonyManager.isDataEnabled) 1 else 0 + } catch (e: SecurityException) { + -2 + } + } + return -1 + } + + val mobileRadioType: Int + get() { + return telephonyManager.phoneType + } + + val mobileDataActivity: Int + get() { + return telephonyManager.dataActivity + } + + private fun mapDataState(dataState: Int): String { when (dataState) { TelephonyManager.DATA_DISCONNECTED -> return "DATA_DISCONNECTED" @@ -77,6 +125,7 @@ open class TelephonyInfoProvider @Inject constructor(context: Context) { TelephonyManager.DATA_CONNECTED -> return "DATA_CONNECTED" TelephonyManager.DATA_SUSPENDED -> return "DATA_SUSPENDED" TelephonyManager.DATA_DISCONNECTING -> return "DATA_DISCONNECTING" + TelephonyManager.DATA_HANDOVER_IN_PROGRESS -> return "DATA_HANDOVER_IN_PROGRESS" TelephonyManager.DATA_UNKNOWN -> return "DATA_UNKNOWN" } return UNKNOWN diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/KeyStoreProvider.java b/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/KeyStoreProvider.java index ac845428..a5009004 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/KeyStoreProvider.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/KeyStoreProvider.java @@ -1,7 +1,6 @@ package io.muun.apollo.data.os.secure_storage; import io.muun.apollo.domain.errors.MuunError; -import io.muun.common.crypto.CryptographyException; import io.muun.common.utils.Preconditions; import android.annotation.TargetApi; @@ -42,10 +41,14 @@ @Singleton public class KeyStoreProvider { + private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; + private static final String MUUN_KEY_STORE_PREFIX = "muun_key_store_"; + private static final X500Principal SUBJECT = new X500Principal("CN=io.muun.apollo, O=Android Authority"); + private static final String RSA = "RSA"; // Note: this constant is used for J_MODE (api 19 to 22) only, though it may generate @@ -57,6 +60,7 @@ public class KeyStoreProvider { // need to store more than that in some places (e.g SignupDraftManager) we are doubling it to // 4096 which allow us to store up to 512 (which should be more than enough). private static final int RSA_KEY_SIZE_IN_BITS = 4096; + private static final int RSA_MAX_STORAGE_SIZE_IN_BYTES = RSA_KEY_SIZE_IN_BITS / 8; private final Context context; @@ -173,11 +177,14 @@ private void generateKeyStoreM(String keyAlias) { try { final KeyGenerator keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, - ANDROID_KEY_STORE); + ANDROID_KEY_STORE + ); keyGenerator.init( - new KeyGenParameterSpec.Builder(keyAlias, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + new KeyGenParameterSpec.Builder( + keyAlias, + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT + ) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) .setRandomizedEncryptionRequired(false) @@ -365,11 +372,4 @@ Set getAllLabels() throws MuunKeyStoreException { throw new MuunKeyStoreException(e); } } - - public static class MuunKeyStoreException extends CryptographyException { - - public MuunKeyStoreException(Throwable cause) { - super(cause); - } - } } diff --git a/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/MuunKeyStoreException.kt b/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/MuunKeyStoreException.kt new file mode 100644 index 00000000..1ec84a82 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/os/secure_storage/MuunKeyStoreException.kt @@ -0,0 +1,35 @@ +package io.muun.apollo.data.os.secure_storage + +import android.security.KeyStoreException +import io.muun.apollo.data.os.OS +import io.muun.apollo.domain.errors.MuunError +import io.muun.apollo.domain.utils.getTypedClause + +class MuunKeyStoreException(cause: Throwable) : MuunError(cause) { + + init { + val maybeKeystoreException = cause.getTypedClause() + + metadata["hasKeystoreExceptionCause"] = maybeKeystoreException.isPresent + + if (maybeKeystoreException.isPresent && OS.supportsKeystoreExceptionPublicMethods()) { + val keyStoreException = maybeKeystoreException.get() + metadata["numericErrorCode"] = keyStoreException.numericErrorCode + metadata["retryPolicy"] = mapRetryPolicy(keyStoreException.retryPolicy) + metadata["isSystemError"] = keyStoreException.isSystemError + metadata["isTransientFailure"] = keyStoreException.isTransientFailure + metadata["requiresUserAuthentication"] = keyStoreException.requiresUserAuthentication() + metadata["toString"] = keyStoreException.toString() + } + } + + private fun mapRetryPolicy(retryPolicy: Int): String { + return when (retryPolicy) { + 1 -> "RETRY_NEVER" + 2 -> "RETRY_WITH_EXPONENTIAL_BACKOFF" + 3 -> "RETRY_WHEN_CONNECTIVITY_AVAILABLE" + 4 -> "RETRY_AFTER_NEXT_REBOOT" + else -> "UNKNOWN: $retryPolicy" + } + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/ApiMigrationsVersionRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/ApiMigrationsVersionRepository.java deleted file mode 100644 index 81179c5c..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/ApiMigrationsVersionRepository.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.muun.apollo.data.preferences; - -import io.muun.apollo.data.preferences.rx.Preference; - -import android.content.Context; - -import javax.inject.Inject; - -public class ApiMigrationsVersionRepository extends BaseRepository { - - private static final String KEY_VERSION = "version"; - - private final Preference versionPreference; - - /** - * Creates a repository. - */ - @Inject - public ApiMigrationsVersionRepository(Context context, RepositoryRegistry repositoryRegistry) { - super(context, repositoryRegistry); - versionPreference = rxSharedPreferences.getInteger(KEY_VERSION); - } - - @Override - protected String getFileName() { - return "api_migrations_version"; - } - - /** - * Returns the stored version, or 0 if none is stored. - */ - public int getVersion() { - return versionPreference.get(); - } - - public void setVersion(int version) { - versionPreference.set(version); - } - -} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/ApiMigrationsVersionRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/ApiMigrationsVersionRepository.kt new file mode 100644 index 00000000..48ff2d75 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/ApiMigrationsVersionRepository.kt @@ -0,0 +1,30 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import io.muun.apollo.data.preferences.rx.Preference +import javax.inject.Inject + +class ApiMigrationsVersionRepository @Inject constructor( + context: Context, + repositoryRegistry: RepositoryRegistry, +) : BaseRepository( + context, repositoryRegistry +) { + companion object { + private const val KEY_VERSION = "version" + } + + private val versionPreference: Preference + get() = rxSharedPreferences.getInteger(KEY_VERSION) + + override val fileName get() = "api_migrations_version" + + /** + * Returns the stored version, or 0 if none is stored. + */ + var version: Int + get() = versionPreference.get()!! + set(version) { + versionPreference.set(version) + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/AppVersionRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/AppVersionRepository.kt index 68df13ac..1579249d 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/AppVersionRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/AppVersionRepository.kt @@ -6,20 +6,17 @@ import javax.inject.Inject class AppVersionRepository @Inject constructor( context: Context, - repositoryRegistry: RepositoryRegistry + repositoryRegistry: RepositoryRegistry, ) : BaseRepository(context, repositoryRegistry) { companion object { private const val KEY = "current_app_version" } - private val versionCodePreference: Preference = rxSharedPreferences.getInteger( - KEY, - 0 - ) + private val versionCodePreference: Preference + get() = rxSharedPreferences.getInteger(KEY, 0) - override fun getFileName(): String = - "current_app_version" + override val fileName get() = "current_app_version" fun update(newVersionCode: Int) { versionCodePreference.set(newVersionCode) diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/AuthRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/AuthRepository.java deleted file mode 100644 index f6f6da1a..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/AuthRepository.java +++ /dev/null @@ -1,106 +0,0 @@ -package io.muun.apollo.data.preferences; - -import io.muun.apollo.data.os.secure_storage.SecureStorageProvider; -import io.muun.apollo.data.preferences.rx.Preference; -import io.muun.common.Optional; -import io.muun.common.model.SessionStatus; -import io.muun.common.utils.Encodings; - -import android.content.Context; -import rx.Observable; - -import javax.inject.Inject; -import javax.validation.constraints.NotNull; - -public class AuthRepository extends BaseRepository { - - private static final String KEY_SERVER_JWT = "server_jwt"; - - private static final String KEY_SESSION_STATUS = "session_status"; - - private final Preference> sessionStatusPreference; - - private final SecureStorageProvider secureStorageProvider; - - /** - * Creates a repository for auth data. - */ - @Inject - public AuthRepository(Context context, - SecureStorageProvider secureStorageProvider, - RepositoryRegistry repositoryRegistry) { - super(context, repositoryRegistry); - - this.secureStorageProvider = secureStorageProvider; - this.sessionStatusPreference = rxSharedPreferences.getOptionalEnum( - KEY_SESSION_STATUS, - SessionStatus.class - ); - } - - @Override - protected String getFileName() { - return "auth"; - } - - /** - * Returns the server jwt. - */ - @NotNull - public Optional getServerJwt() { - if (!secureStorageProvider.has(KEY_SERVER_JWT)) { - return Optional.empty(); - } - - final byte[] serverJwt = secureStorageProvider.get(KEY_SERVER_JWT); - - return Optional.ofNullable(Encodings.bytesToString(serverJwt)); - } - - /** - * Store server JWT on secure storage. - */ - public void storeServerJwt(@NotNull String serverJwt) { - this.secureStorageProvider.put(KEY_SERVER_JWT, Encodings.stringToBytes(serverJwt)); - } - - /** - * Store the current session status on shared preferences. - */ - public void storeSessionStatus(SessionStatus sessionStatus) { - sessionStatusPreference.set(Optional.ofNullable(sessionStatus)); - } - - @Override - public void clear() { - super.clear(); - secureStorageProvider.delete(KEY_SERVER_JWT); - } - - /** - * Returns the session status. - */ - public Optional getSessionStatus() { - return watchSessionStatus().toBlocking().first(); - } - - /** - * Returns an observable to watch changes of the session status. - */ - public Observable> watchSessionStatus() { - return sessionStatusPreference.asObservable() - .map(value -> value != null ? value : Optional.empty()); - } - - /** - * One-time method utility for preference migration. - */ - public void moveJwtToSecureStorage() { - final Preference serverJwtPrefs = rxSharedPreferences.getString(KEY_SERVER_JWT); - if (serverJwtPrefs.isSet()) { - final String serverJwt = serverJwtPrefs.get(); - storeServerJwt(serverJwt); - serverJwtPrefs.delete(); - } - } -} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/AuthRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/AuthRepository.kt new file mode 100644 index 00000000..e1dd2b38 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/AuthRepository.kt @@ -0,0 +1,94 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import io.muun.apollo.data.os.secure_storage.SecureStorageProvider +import io.muun.apollo.data.preferences.rx.Preference +import io.muun.common.Optional +import io.muun.common.model.SessionStatus +import io.muun.common.utils.Encodings +import rx.Observable +import javax.inject.Inject + +class AuthRepository @Inject constructor( + context: Context, + repositoryRegistry: RepositoryRegistry, + private val secureStorageProvider: SecureStorageProvider, +) : BaseRepository(context, repositoryRegistry) { + + companion object { + private const val KEY_SERVER_JWT = "server_jwt" + private const val KEY_SESSION_STATUS = "session_status" + } + + private val sessionStatusPreference: Preference> + get() = rxSharedPreferences.getOptionalEnum( + KEY_SESSION_STATUS, + SessionStatus::class.java + ) + + override val fileName get() = "auth" + + /** + * Returns the server jwt. + */ + val serverJwt: Optional + get() { + if (!secureStorageProvider.has(KEY_SERVER_JWT)) { + return Optional.empty() + } + val serverJwt = secureStorageProvider[KEY_SERVER_JWT] + return Optional.ofNullable(Encodings.bytesToString(serverJwt)) + } + + /** + * Store server JWT on secure storage. + */ + fun storeServerJwt(serverJwt: String) { + secureStorageProvider.put(KEY_SERVER_JWT, Encodings.stringToBytes(serverJwt)) + } + + /** + * Store the current session status on shared preferences. + */ + fun storeSessionStatus(sessionStatus: SessionStatus) { + sessionStatusPreference.set(Optional.ofNullable(sessionStatus)) + } + + /** + * Delete session/auth related data currently stored, both in preferences as in secure storage. + * Note: this is a special casing for signup/in flow. For normal clear (e.g logout/delete + * wallet) BaseRepository#clear should be used (secureStorage gets wiped altogether + * separately). + * TODO we should probably extract server_jwt to a separate class (like we do in PinManager). + */ + fun clearSession() { + super.clear() + secureStorageProvider.delete(KEY_SERVER_JWT) + } + + /** + * Returns the session status. + */ + val sessionStatus: Optional + get() = watchSessionStatus().toBlocking().first() + + /** + * Returns an observable to watch changes of the session status. + */ + fun watchSessionStatus(): Observable> { + return sessionStatusPreference.asObservable() + .map { value -> value ?: Optional.empty() } + } + + /** + * One-time method utility for preference migration. + */ + fun moveJwtToSecureStorage() { + val serverJwtPrefs = rxSharedPreferences.getString(KEY_SERVER_JWT) + if (serverJwtPrefs.isSet) { + val serverJwt = serverJwtPrefs.get()!! + storeServerJwt(serverJwt) + serverJwtPrefs.delete() + } + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/BackgroundTimesRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/BackgroundTimesRepository.kt index 3c72d149..d66cb0c2 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/BackgroundTimesRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/BackgroundTimesRepository.kt @@ -43,14 +43,13 @@ class BackgroundTimesRepository @Inject constructor( } } - override fun getFileName(): String = - "background_times" + override val fileName get() = "background_times" - private val lastBackgroundBeginTimePreference: Preference = - rxSharedPreferences.getLong(LAST_BACKGROUND_BEGIN_TIME_KEY, null) + private val lastBackgroundBeginTimePreference: Preference + get() = rxSharedPreferences.getLong(LAST_BACKGROUND_BEGIN_TIME_KEY, null) - private val backgroundTimesPreferences: Preference> = - rxSharedPreferences.getObject( + private val backgroundTimesPreferences: Preference> + get() = rxSharedPreferences.getObject( BACKGROUND_TIMES_KEY, emptyList(), JsonListPreferenceAdapter(StoredBackgroundEvent::class.java) diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/BaseRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/BaseRepository.java deleted file mode 100644 index b222f596..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/BaseRepository.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.muun.apollo.data.preferences; - -import io.muun.apollo.data.preferences.rx.RxSharedPreferences; - -import android.content.Context; -import android.content.SharedPreferences; - -public abstract class BaseRepository { - - protected final RxSharedPreferences rxSharedPreferences; - - protected final SharedPreferences sharedPreferences; - - /** - * Creates a base preferences repository. - */ - public BaseRepository(Context context, RepositoryRegistry repositoryRegistry) { - sharedPreferences = context.getSharedPreferences( - getFileName(), - Context.MODE_PRIVATE - ); - - rxSharedPreferences = RxSharedPreferences.create(sharedPreferences); - - repositoryRegistry.load(this); - } - - protected abstract String getFileName(); - - /** - * Clears the repository. - */ - public void clear() { - sharedPreferences.edit() - .clear() - .commit(); - } - -} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/BaseRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/BaseRepository.kt new file mode 100644 index 00000000..2058d7c6 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/BaseRepository.kt @@ -0,0 +1,47 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import android.content.SharedPreferences +import androidx.annotation.VisibleForTesting +import io.muun.apollo.data.preferences.rx.RxSharedPreferences + +abstract class BaseRepository(context: Context, repositoryRegistry: RepositoryRegistry) { + + protected val sharedPreferences: SharedPreferences by lazy { + context.getSharedPreferences( + fileName, + Context.MODE_PRIVATE + ) + } + + protected val rxSharedPreferences: RxSharedPreferences by lazy { + RxSharedPreferences.create(sharedPreferences) + } + + /** + * Creates a base preferences repository. + */ + init { + @Suppress("LeakingThis") + repositoryRegistry.load(this) + } + + protected abstract val fileName: String + + /** + * Clears the repository. + */ + fun clear() { + sharedPreferences.edit() + .clear() + .commit() + } + + @VisibleForTesting + fun getSharedPreferencesForTesting() = + sharedPreferences + + @VisibleForTesting + fun getRxSharedPreferencesForTesting() = + rxSharedPreferences +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/BlockchainHeightRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/BlockchainHeightRepository.java deleted file mode 100644 index 50f57530..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/BlockchainHeightRepository.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.muun.apollo.data.preferences; - -import io.muun.apollo.data.preferences.rx.Preference; - -import android.content.Context; -import rx.Observable; - -import javax.inject.Inject; - -public class BlockchainHeightRepository extends BaseRepository { - - private static final String BLOCKCHAIN_HEIGHT = "blockchain_height"; - private final Preference blockchainHeightPreference; - - /** - * Constructor. - */ - @Inject - public BlockchainHeightRepository(Context context, RepositoryRegistry repositoryRegistry) { - super(context, repositoryRegistry); - blockchainHeightPreference = rxSharedPreferences.getInteger(BLOCKCHAIN_HEIGHT, 0); - } - - @Override - protected String getFileName() { - return "blockchain_height"; - } - - /** - * Return true if `fetch()` is safe to call. - */ - public boolean isSet() { - return blockchainHeightPreference.isSet(); - } - - /** - * Fetch an observable instance of the persisted exchange rates. - */ - public Observable fetch() { - return blockchainHeightPreference.asObservable(); - } - - public int fetchLatest() { - return fetch().toBlocking().first(); - } - - /** - * Store the current blockchain height. - */ - public void store(int blockchainHeight) { - blockchainHeightPreference.set(blockchainHeight); - } -} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/BlockchainHeightRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/BlockchainHeightRepository.kt new file mode 100644 index 00000000..11fddfb8 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/BlockchainHeightRepository.kt @@ -0,0 +1,40 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import io.muun.apollo.data.preferences.rx.Preference +import rx.Observable +import javax.inject.Inject + +// Open for mockito to mock/spy +open class BlockchainHeightRepository @Inject constructor( + context: Context, + repositoryRegistry: RepositoryRegistry, +) : BaseRepository(context, repositoryRegistry) { + + companion object { + private const val BLOCKCHAIN_HEIGHT = "blockchain_height" + } + + private val blockchainHeightPreference: Preference + get() = rxSharedPreferences.getInteger(BLOCKCHAIN_HEIGHT, 0) + + override val fileName get() = "blockchain_height" + + /** + * Fetch an observable instance of the persisted exchange rates. + */ + fun fetch(): Observable { + return blockchainHeightPreference.asObservable() + } + + fun fetchLatest(): Int { + return fetch().toBlocking().first() + } + + /** + * Store the current blockchain height. + */ + fun store(blockchainHeight: Int) { + blockchainHeightPreference.set(blockchainHeight) + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/ClientVersionRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/ClientVersionRepository.java deleted file mode 100644 index 7263fb6a..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/ClientVersionRepository.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.muun.apollo.data.preferences; - -import io.muun.apollo.data.preferences.rx.Preference; -import io.muun.common.Optional; - -import android.content.Context; -import rx.Observable; - -import javax.inject.Inject; - -public class ClientVersionRepository extends BaseRepository { - - private static final String KEY_MIN_CLIENT_VERSION = "min_client_version"; - - private final Preference minClientVersionPreference; - - /** - * Creates a repository for storing minClientVersion. - */ - @Inject - public ClientVersionRepository(Context context, RepositoryRegistry repositoryRegistry) { - super(context, repositoryRegistry); - minClientVersionPreference = rxSharedPreferences.getInteger(KEY_MIN_CLIENT_VERSION, null); - } - - @Override - protected String getFileName() { - return "clientVersion"; - } - - /** - * Save minClientVersion in preferences. - */ - public void storeMinClientVersion(int minClientVersion) { - this.minClientVersionPreference.set(minClientVersion); - } - - /** - * Load minClientVersion from preferences, if present. - */ - public Optional getMinClientVersion() { - return watchMinClientVersion().toBlocking().first(); - } - - /** - * Return an Observable of MinClientVersion preference. - */ - public Observable> watchMinClientVersion() { - return minClientVersionPreference.asObservable() - .map(Optional::ofNullable); - } -} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/ClientVersionRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/ClientVersionRepository.kt new file mode 100644 index 00000000..0baf19d6 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/ClientVersionRepository.kt @@ -0,0 +1,44 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import io.muun.apollo.data.preferences.rx.Preference +import io.muun.common.Optional +import rx.Observable +import javax.inject.Inject + +class ClientVersionRepository @Inject constructor( + context: Context, + repositoryRegistry: RepositoryRegistry, +) : BaseRepository(context, repositoryRegistry) { + + companion object { + private const val KEY_MIN_CLIENT_VERSION = "min_client_version" + } + + private val minClientVersionPreference: Preference + get() = rxSharedPreferences.getInteger(KEY_MIN_CLIENT_VERSION, null) + + override val fileName get() = "clientVersion" + + /** + * Save minClientVersion in preferences. + */ + fun storeMinClientVersion(minClientVersion: Int) { + minClientVersionPreference.set(minClientVersion) + } + + /** + * Load minClientVersion from preferences, if present. + */ + // TODO this optional can probably be removed. + val minClientVersion: Optional + get() = watchMinClientVersion().toBlocking().first() + + /** + * Return an Observable of MinClientVersion preference. + */ + fun watchMinClientVersion(): Observable> { + return minClientVersionPreference.asObservable() + .map { value: Int? -> Optional.ofNullable(value) } + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/ExchangeRateWindowRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/ExchangeRateWindowRepository.java deleted file mode 100644 index 713b7ecd..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/ExchangeRateWindowRepository.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.muun.apollo.data.preferences; - -import io.muun.apollo.data.preferences.rx.Preference; -import io.muun.apollo.data.serialization.SerializationUtils; -import io.muun.apollo.domain.model.ExchangeRateWindow; -import io.muun.common.Optional; - -import android.content.Context; -import rx.Observable; - -import javax.inject.Inject; - -public class ExchangeRateWindowRepository extends BaseRepository { - - private static final String KEY_WINDOW = "window"; - private static final String KEY_FIXED_WINDOW = "fixed_window"; - - private final Preference windowPreference; - private final Preference fixedWindowPreference; - - /** - * Constructor. - */ - @Inject - public ExchangeRateWindowRepository(Context context, RepositoryRegistry repositoryRegistry) { - super(context, repositoryRegistry); - windowPreference = rxSharedPreferences.getString(KEY_WINDOW); - fixedWindowPreference = rxSharedPreferences.getString(KEY_FIXED_WINDOW); - } - - @Override - protected String getFileName() { - return "exchange_rate_window"; - } - - /** - * Return true if `fetch()` is safe to call. - */ - public boolean isSet() { - return windowPreference.isSet(); - } - - /** - * Fetch an observable instance of the persisted exchange rates. - */ - public Observable fetch() { - return windowPreference.asObservable() - .map(str -> SerializationUtils.deserializeJson(ExchangeRateWindow.class, str)); - } - - /** - * Get the current exchange rates. - */ - public ExchangeRateWindow fetchOne() { - return fetch().toBlocking().first(); - } - - /** - * Store the current exchange rates. - */ - public void storeLatest(ExchangeRateWindow exchangeRateWindow) { - windowPreference.set( - SerializationUtils.serializeJson(ExchangeRateWindow.class, exchangeRateWindow) - ); - } - - /** - * Store an exchange rate window, that will, temporarily, be fixed for some specific flow. - */ - public void storeAndFix(ExchangeRateWindow exchangeRateWindow) { - fixedWindowPreference.set( - SerializationUtils.serializeJson(ExchangeRateWindow.class, exchangeRateWindow) - ); - } - - /** - * Get a fixed exchange rate window, if any. - */ - public Optional getFixedWindow() { - if (fixedWindowPreference.isSet()) { - final String json = fixedWindowPreference.get(); - return Optional.of(SerializationUtils.deserializeJson(ExchangeRateWindow.class, json)); - } else { - return Optional.empty(); - } - } -} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/ExchangeRateWindowRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/ExchangeRateWindowRepository.kt new file mode 100644 index 00000000..25cfe59d --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/ExchangeRateWindowRepository.kt @@ -0,0 +1,82 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import io.muun.apollo.data.preferences.rx.Preference +import io.muun.apollo.data.serialization.SerializationUtils +import io.muun.apollo.domain.model.ExchangeRateWindow +import io.muun.common.Optional +import rx.Observable +import javax.inject.Inject + +// Open for mockito to mock/spy +open class ExchangeRateWindowRepository @Inject constructor( + context: Context, + repositoryRegistry: RepositoryRegistry, +) : BaseRepository(context, repositoryRegistry) { + + companion object { + private const val KEY_WINDOW = "window" + private const val KEY_FIXED_WINDOW = "fixed_window" + } + + private val windowPreference: Preference + get() = rxSharedPreferences.getString(KEY_WINDOW) + private val fixedWindowPreference: Preference + get() = rxSharedPreferences.getString(KEY_FIXED_WINDOW) + + override val fileName get() = "exchange_rate_window" + + /** + * Return true if `fetch()` is safe to call. + */ + val isSet: Boolean + get() = windowPreference.isSet + + /** + * Fetch an observable instance of the persisted exchange rates. + */ + fun fetch(): Observable { + return windowPreference.asObservable() + .map { str: String? -> + SerializationUtils.deserializeJson( + ExchangeRateWindow::class.java, str + ) + } + } + + /** + * Get the current exchange rates. + */ + fun fetchOne(): ExchangeRateWindow { + return fetch().toBlocking().first() + } + + /** + * Store the current exchange rates. + */ + fun storeLatest(exchangeRateWindow: ExchangeRateWindow) { + windowPreference.set( + SerializationUtils.serializeJson(ExchangeRateWindow::class.java, exchangeRateWindow) + ) + } + + /** + * Store an exchange rate window, that will, temporarily, be fixed for some specific flow. + */ + fun storeAndFix(exchangeRateWindow: ExchangeRateWindow) { + fixedWindowPreference.set( + SerializationUtils.serializeJson(ExchangeRateWindow::class.java, exchangeRateWindow) + ) + } + + /** + * Get a fixed exchange rate window, if any. + */ + val fixedWindow: Optional + get() = if (fixedWindowPreference.isSet) { + val json = fixedWindowPreference.get() + Optional.of(SerializationUtils.deserializeJson(ExchangeRateWindow::class.java, json)) + } else { + Optional.empty() + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/FeaturesRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/FeaturesRepository.kt index 70d08e05..83b043bd 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/FeaturesRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/FeaturesRepository.kt @@ -5,28 +5,29 @@ import io.muun.apollo.data.preferences.adapter.JsonPreferenceAdapter import io.muun.apollo.data.preferences.rx.Preference import io.muun.apollo.data.preferences.stored.StoredBackendFeatures import io.muun.apollo.domain.model.MuunFeature +import libwallet.BackendActivatedFeatureStatusProvider import rx.Observable +import timber.log.Timber import javax.inject.Inject // Open for mockito to mock/spy open class FeaturesRepository @Inject constructor( context: Context, repositoryRegistry: RepositoryRegistry, -) : BaseRepository(context, repositoryRegistry) { +) : BaseRepository(context, repositoryRegistry), BackendActivatedFeatureStatusProvider { companion object { private const val BACKEND_FEATURES = "backend_features" } - private val backendFeaturesPref: Preference = rxSharedPreferences - .getObject( - BACKEND_FEATURES, - StoredBackendFeatures(), - JsonPreferenceAdapter(StoredBackendFeatures::class.java) - ) - - override fun getFileName() = - "features" + private val backendFeaturesPref: Preference + get() = rxSharedPreferences + .getObject( + BACKEND_FEATURES, + StoredBackendFeatures(), + JsonPreferenceAdapter(StoredBackendFeatures::class.java) + ) + override val fileName get() = "features" /** * Fetch an observable instance of the currently supported backend features. @@ -45,4 +46,17 @@ open class FeaturesRepository @Inject constructor( backendFeaturesPref.set(storedBackendFeatures) } -} \ No newline at end of file + /** + * Check for a specific feature flag, passed as a string. + * Not to be used by the app. This is a bridge to provide feature flag information to libwallet + * until we implement a more generic libwallet-side storage mechanism. + */ + override fun isBackendFlagEnabled(flag: String?): Boolean { + if (flag == null) { + Timber.e("Tried to read null feature flag from libwallet.") + return false + } + + return backendFeaturesPref.get()?.features?.contains(flag) == true + } +} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/FeeWindowRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/FeeWindowRepository.java deleted file mode 100644 index ddc1ec06..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/FeeWindowRepository.java +++ /dev/null @@ -1,124 +0,0 @@ -package io.muun.apollo.data.preferences; - -import io.muun.apollo.data.preferences.adapter.JsonPreferenceAdapter; -import io.muun.apollo.data.preferences.rx.Preference; -import io.muun.apollo.domain.model.FeeWindow; - -import android.content.Context; -import org.threeten.bp.ZonedDateTime; -import rx.Observable; - -import java.util.SortedMap; -import javax.inject.Inject; - -public class FeeWindowRepository extends BaseRepository { - - /** - * Like FeeWindow, but JSON-serializable. - */ - private static class StoredFeeWindow { - // WAIT - // WARNING - // CAREFUL - // READ THIS, I MEAN IT: - - // We forgot to exclude this class from Proguard rules. This means that the order of - // declaration of this attributes is important -- until we remove this class from proguard - // and migrate the preference to a non-minified JSON this class is APPEND-ONLY. - - public Long houstonId; - public ZonedDateTime fetchDate; - public SortedMap targetedFees; - public int fastConfTarget; - public int mediumConfTarget; - public int slowConfTarget; - - public StoredFeeWindow() { - } - - StoredFeeWindow(FeeWindow feeWindow) { - this.houstonId = feeWindow.houstonId; - this.fetchDate = feeWindow.fetchDate; - this.targetedFees = feeWindow.targetedFees; - this.fastConfTarget = feeWindow.fastConfTarget; - this.mediumConfTarget = feeWindow.mediumConfTarget; - this.slowConfTarget = feeWindow.slowConfTarget; - } - - FeeWindow toFeeWindow() { - return new FeeWindow( - houstonId, - fetchDate, - targetedFees, - fastConfTarget, - mediumConfTarget, - slowConfTarget - ); - } - } - - private static final String KEY_FEE_WINDOW = "fee_window"; - - private final Preference feeWindowPreference; - - /** - * Constructor. - */ - @Inject - public FeeWindowRepository(Context context, RepositoryRegistry repositoryRegistry) { - super(context, repositoryRegistry); - - feeWindowPreference = rxSharedPreferences.getObject( - KEY_FEE_WINDOW, - new JsonPreferenceAdapter<>(StoredFeeWindow.class) - ); - } - - @Override - protected String getFileName() { - return "fee_window"; - } - - /** - * Return true if `fetch()` is safe to call. - */ - public boolean isSet() { - return feeWindowPreference.isSet(); - } - - /** - * Fetch an observable instance of the latest expected fee. - */ - public Observable fetch() { - return feeWindowPreference.asObservable().map(storedFeeWindow -> { - if (storedFeeWindow != null) { - return storedFeeWindow.toFeeWindow(); - } else { - return null; - } - }); - } - - public FeeWindow fetchOne() { - return fetch().toBlocking().first(); - } - - /** - * Store the current expected fee. - */ - public void store(FeeWindow feeWindow) { - feeWindowPreference.set(new StoredFeeWindow(feeWindow)); - } - - /** - * Migration to init dynamic fee targets. - */ - public void initDynamicFeeTargets() { - final FeeWindow feeWindow = fetchOne(); - - if (feeWindow != null) { - final FeeWindow migratedFeeWindow = feeWindow.initDynamicFeeTargets(); - store(migratedFeeWindow); - } - } -} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/FeeWindowRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/FeeWindowRepository.kt new file mode 100644 index 00000000..3d0dd459 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/FeeWindowRepository.kt @@ -0,0 +1,140 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import io.muun.apollo.data.preferences.adapter.JsonPreferenceAdapter +import io.muun.apollo.data.preferences.rx.Preference +import io.muun.apollo.domain.model.FeeWindow +import org.threeten.bp.ZonedDateTime +import rx.Observable +import java.util.SortedMap +import javax.inject.Inject + +// Open for mockito to mock/spy +open class FeeWindowRepository @Inject constructor( + context: Context, + repositoryRegistry: RepositoryRegistry, +) : BaseRepository(context, repositoryRegistry) { + + companion object { + private const val KEY_FEE_WINDOW = "fee_window" + } + + /** + * Like FeeWindow, but JSON-serializable. + */ + private class StoredFeeWindow { + // WAIT + // WARNING + // CAREFUL + // READ THIS, I MEAN IT: + // We forgot to exclude this class from Proguard rules. This means that the order of + // declaration of this attributes is important -- until we remove this class from proguard + // and migrate the preference to a non-minified JSON this class is APPEND-ONLY. + @JvmField + var houstonId: Long? = null + + @JvmField + var fetchDate: ZonedDateTime? = null + + @JvmField + var targetedFees: SortedMap? = null + + @JvmField + var fastConfTarget = 0 + + @JvmField + var mediumConfTarget = 0 + + @JvmField + var slowConfTarget = 0 + + /** + * JSON constructor. + */ + @Suppress("unused") + constructor() + + constructor(feeWindow: FeeWindow) { + houstonId = feeWindow.houstonId + fetchDate = feeWindow.fetchDate + targetedFees = feeWindow.targetedFees + fastConfTarget = feeWindow.fastConfTarget + mediumConfTarget = feeWindow.mediumConfTarget + slowConfTarget = feeWindow.slowConfTarget + } + + fun toFeeWindow(): FeeWindow { + return FeeWindow( + houstonId, + fetchDate, + targetedFees, + fastConfTarget, + mediumConfTarget, + slowConfTarget + ) + } + } + + private val feeWindowPreference: Preference + get() = rxSharedPreferences.getObject( + KEY_FEE_WINDOW, + JsonPreferenceAdapter(StoredFeeWindow::class.java) + ) + + override val fileName get() = "fee_window" + + /** + * Return true if `fetch()` is safe to call. + */ + private val isSet: Boolean + get() = feeWindowPreference.isSet + + /** + * Fetch an observable instance of the latest expected fee. + * Only to be used after feeWindowPreference.isSet == true. + */ + fun fetchNonNull(): Observable { + return fetch().map { feeWindow -> feeWindow!! } + } + + /** + * Fetch an observable instance of the latest expected fee. + */ + fun fetch(): Observable { + return feeWindowPreference.asObservable().map { storedFeeWindow: StoredFeeWindow? -> + if (storedFeeWindow != null) { + return@map storedFeeWindow.toFeeWindow() + } else { + return@map null + } + } + } + + private fun fetchOne(): FeeWindow? { + return fetch().toBlocking().first() + } + + /** + * Store the current expected fee. + */ + fun store(feeWindow: FeeWindow) { + feeWindowPreference.set(StoredFeeWindow(feeWindow)) + } + + /** + * Migration to init dynamic fee targets. + */ + fun initDynamicFeeTargets() { + val feeWindow = fetchOne() + if (feeWindow != null) { + val migratedFeeWindow = feeWindow.initDynamicFeeTargets() + store(migratedFeeWindow) + } + } + + fun isFeeRecent() = + if (isSet) + fetchOne()!!.isRecent + else + false +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/FirebaseInstallationIdRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/FirebaseInstallationIdRepository.kt index 2fdce0e5..320f95df 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/FirebaseInstallationIdRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/FirebaseInstallationIdRepository.kt @@ -1,6 +1,7 @@ package io.muun.apollo.data.preferences import android.content.Context +import io.muun.apollo.data.preferences.rx.Preference import rx.Observable import timber.log.Timber import javax.inject.Inject @@ -17,14 +18,14 @@ class FirebaseInstallationIdRepository @Inject constructor( private const val BIG_QUERY_PSEUDO_ID = "big_query_pseudo_id" } - private val fcmTokenPreference = rxSharedPreferences.getString(FCM_TOKEN_KEY) + private val fcmTokenPreference: Preference + get() = rxSharedPreferences.getString(FCM_TOKEN_KEY) - private val bigQueryPseudoIdPreference = rxSharedPreferences.getString(BIG_QUERY_PSEUDO_ID) + private val bigQueryPseudoIdPreference: Preference + get() = rxSharedPreferences.getString(BIG_QUERY_PSEUDO_ID) - override fun getFileName(): String { - // legacy name to avoid migration - return "fcmToken" - } + // legacy name to avoid migration + override val fileName get() = "fcmToken" fun storeBigQueryPseudoId(id: String) { bigQueryPseudoIdPreference.set(id) diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/ForwardingPoliciesRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/ForwardingPoliciesRepository.kt index 2e232f1c..4a4c241c 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/ForwardingPoliciesRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/ForwardingPoliciesRepository.kt @@ -50,16 +50,14 @@ open class ForwardingPoliciesRepository @Inject constructor( } } - private val preference: Preference> = - rxSharedPreferences.getObject( + private val preference: Preference> + get() = rxSharedPreferences.getObject( KEY, emptyList(), JsonListPreferenceAdapter(StoredForwardingPolicy::class.java) ) - override fun getFileName(): String { - return "forwarding_policies" - } + override val fileName get() = "forwarding_policies" fun fetchOne(): List { diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/KeysRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/KeysRepository.java deleted file mode 100644 index bf66eef4..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/KeysRepository.java +++ /dev/null @@ -1,486 +0,0 @@ -package io.muun.apollo.data.preferences; - -import io.muun.apollo.data.os.secure_storage.SecureStorageProvider; -import io.muun.apollo.data.os.secure_storage.SecureStorageProvider.SecureStorageNoSuchElementError; -import io.muun.apollo.data.preferences.adapter.PublicKeyPreferenceAdapter; -import io.muun.apollo.data.preferences.rx.Preference; -import io.muun.apollo.domain.errors.BugDetected; -import io.muun.apollo.domain.errors.MissingMigrationError; -import io.muun.common.crypto.ChallengePublicKey; -import io.muun.common.crypto.ChallengeType; -import io.muun.common.crypto.hd.PrivateKey; -import io.muun.common.crypto.hd.PublicKey; -import io.muun.common.crypto.hd.PublicKeyPair; -import io.muun.common.crypto.hd.PublicKeyTriple; -import io.muun.common.crypto.hd.Schema; -import io.muun.common.rx.ObservableFn; -import io.muun.common.utils.Encodings; -import io.muun.common.utils.Preconditions; - -import android.content.Context; -import org.bitcoinj.core.NetworkParameters; -import org.threeten.bp.ZonedDateTime; -import rx.Observable; -import timber.log.Timber; - -import java.util.Objects; -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.validation.constraints.NotNull; - -public class KeysRepository extends BaseRepository { - - private static final int SECONDS_IN_A_DAY = 60 * 60 * 24; - - // Storage-level keys: - private static final String KEY_BASE_PRIVATE_KEY_PATH = "xpriv_path"; - private static final String KEY_BASE_58_PRIVATE_KEY = "key_base_58_private_key"; - private static final String KEY_BASE_PUBLIC_KEY = "base_public_key"; - - private static final String KEY_CHALLENGE_PUBLIC_KEY = "key_challenge_public_key_"; - - private static final String KEY_ENCRYPTED_PRIVATE_USER_KEY = "key_encrypted_private_apollo_key"; - private static final String KEY_ENCRYPTED_PRIVATE_MUUN_KEY = "key_encrypted_private_muun_key"; - private static final String KEY_MUUN_KEY_FINGERPRINT = "fingerprint_muun_key"; - private static final String KEY_USER_KEY_FINGERPRINT = "fingerprint_user_key"; - - private static final String KEY_BASE_MUUN_PUBLIC_KEY = "base_muun_public_key"; - private static final String KEY_BASE_SWAP_SERVER_PUBLIC_KEY = "base_swap_server_public_key"; - - private static final String KEY_MAX_USED_EXTERNAL_ADDRESS_INDEX = - "key_max_used_external_address_index"; - - private static final String KEY_MAX_WATCHING_EXTERNAL_ADDRESS_INDEX = - "key_max_watching_external_address_index"; - - // Reactive preferences: - private final Preference basePrivateKeyPathPreference; - - private final Preference basePublicKeyPreference; - private final Preference baseMuunPublicKeyPreference; - private final Preference baseSwapServerPublicKeyPreference; - - private final Preference muunKeyFingerprintPreference; - private final Preference userKeyFingerprintPreference; - - private final Preference maxUsedExternalAddressIndexPreference; - private final Preference maxWatchingExternalAddressIndexPreference; - - // Dependencies: - private final NetworkParameters networkParameters; - private final SecureStorageProvider secureStorageProvider; - private final UserRepository userRepository; - - /** - * Creates a KeysRepository. - */ - @Inject - public KeysRepository( - Context context, - NetworkParameters networkParameters, - SecureStorageProvider secureStorageProvider, - UserRepository userRepository, - RepositoryRegistry repositoryRegistry) { - - super(context, repositoryRegistry); - - this.networkParameters = networkParameters; - - this.secureStorageProvider = secureStorageProvider; - - this.userRepository = userRepository; - - basePrivateKeyPathPreference = rxSharedPreferences - .getString(KEY_BASE_PRIVATE_KEY_PATH); - - maxUsedExternalAddressIndexPreference = rxSharedPreferences - .getInteger(KEY_MAX_USED_EXTERNAL_ADDRESS_INDEX); - - maxWatchingExternalAddressIndexPreference = rxSharedPreferences - .getInteger(KEY_MAX_WATCHING_EXTERNAL_ADDRESS_INDEX); - - basePublicKeyPreference = rxSharedPreferences.getObject( - KEY_BASE_PUBLIC_KEY, - PublicKeyPreferenceAdapter.INSTANCE - ); - - baseMuunPublicKeyPreference = rxSharedPreferences.getObject( - KEY_BASE_MUUN_PUBLIC_KEY, - PublicKeyPreferenceAdapter.INSTANCE - ); - - baseSwapServerPublicKeyPreference = rxSharedPreferences.getObject( - KEY_BASE_SWAP_SERVER_PUBLIC_KEY, - PublicKeyPreferenceAdapter.INSTANCE - ); - - muunKeyFingerprintPreference = rxSharedPreferences - .getString(KEY_MUUN_KEY_FINGERPRINT); - - userKeyFingerprintPreference = rxSharedPreferences - .getString(KEY_USER_KEY_FINGERPRINT); - } - - /** - * Returns a new non-stored root PrivateKey. - */ - @NotNull - public PrivateKey createRootPrivateKey() { - return PrivateKey.getNewRootPrivateKey(networkParameters); - } - - /** - * Returns the current base private key. - */ - @NotNull - public Observable getBasePrivateKey() { - return secureStorageProvider.getAsync(KEY_BASE_58_PRIVATE_KEY) - .map(base58Bytes -> { - final String path = basePrivateKeyPathPreference.get(); - final String base58 = new String(base58Bytes); - - return PrivateKey.deserializeFromBase58(path, base58); - }); - } - - /** - * Returns the base public key. - */ - public PublicKey getBasePublicKey() { - final PublicKey publicKey = basePublicKeyPreference.get(); - Preconditions.checkState(publicKey != null, "PublicKey must NOT be null!"); - - return publicKey; - } - - /** - * Returns the base Muun cosigning public key. - */ - public PublicKey getBaseMuunPublicKey() { - final PublicKey publicKey = baseMuunPublicKeyPreference.get(); - Preconditions.checkState(publicKey != null, "Muun cosigning PublicKey must NOT be null!"); - - return publicKey; - } - - /** - * Returns the swap server public key for this user. - */ - public PublicKey getBaseSwapServerPublicKey() { - return baseSwapServerPublicKeyPreference.get(); - } - - /** - * Returns the base user-Muun public key pair. - * @deprecated use getBasePublicKeyTriple - */ - @Deprecated - public PublicKeyPair getBasePublicKeyPair() { - return new PublicKeyPair( - getBasePublicKey(), - getBaseMuunPublicKey() - ); - } - - /** - * Returns the base user-Muun-swapServer public key triple. - */ - public PublicKeyTriple getBasePublicKeyTriple() { - return new PublicKeyTriple( - getBasePublicKey(), - getBaseMuunPublicKey(), - getBaseSwapServerPublicKey() - ); - } - - /** - * Save the base Muun cosigning public key. - */ - public void storeBaseMuunPublicKey(@NotNull PublicKey publicKey) { - baseMuunPublicKeyPreference.set(publicKey); - } - - /** - * Save the base swap server key for this user. - */ - public void storeSwapServerPublicKey(@NotNull PublicKey publicKey) { - baseSwapServerPublicKeyPreference.set(publicKey); - } - - /** - * Saves the base private key from which all are derived. - */ - public Observable storeBasePrivateKey(@NotNull PrivateKey privateKey) { - // The private key can be rooted at different derivation paths depending on - // when it was generated. Older apps rooted them at m. The now expected path is - // Schema#getBasePath, so we derive to it to ensure consistency for older keys. - final PrivateKey basePrivateKey = privateKey.deriveFromAbsolutePath(Schema.getBasePath()); - - final byte[] base58 = basePrivateKey.serializeBase58().getBytes(); - - return secureStorageProvider.putAsync(KEY_BASE_58_PRIVATE_KEY, base58) - .flatMap(ignored -> storeEncryptedBasePrivateKey(basePrivateKey)) - .doOnNext(ignored -> { - basePrivateKeyPathPreference.set(basePrivateKey.getAbsoluteDerivationPath()); - - basePublicKeyPreference.set(basePrivateKey.getPublicKey()); - maxUsedExternalAddressIndexPreference.set(-1); // set a default - }); - } - - private Observable storeEncryptedBasePrivateKey(@NotNull PrivateKey basePrivateKey) { - - if (!hasChallengePublicKey(ChallengeType.RECOVERY_CODE)) { - // Without challenge keys, we can't store the exportable encrypted private key: - return Observable.just(null); - } - - return getChallengePublicKey(ChallengeType.RECOVERY_CODE) - .map(challengePublicKey -> { - final long birthday = getWalletBirthdaySinceGenesis(); - return challengePublicKey.encryptPrivateKey(basePrivateKey, birthday); - }) - .flatMap(encryptedKey -> { - - Preconditions.checkNotNull(encryptedKey); - - Timber.d("Storing encrypted Apollo private key in secure storage."); - - storeUserKeyFingerprint(Encodings.bytesToHex(basePrivateKey.getFingerprint())); - - return secureStorageProvider.putAsync( - KEY_ENCRYPTED_PRIVATE_USER_KEY, - encryptedKey.getBytes() - ); - }); - } - - private long getWalletBirthdaySinceGenesis() { - - final ZonedDateTime createdAt = userRepository.fetchOneOptional() - .flatMap(it -> it.createdAt) - .orElse(null); - - if (createdAt == null) { - return 0xFFFF; // a placeholder, only for the user key. Not ideal, not necessary. - } - - final long creationTimestamp = createdAt.toEpochSecond(); - final long genesisTimestamp = networkParameters.getGenesisBlock().getTimeSeconds(); - return (creationTimestamp - genesisTimestamp) / SECONDS_IN_A_DAY; - } - - public Observable getEncryptedBasePrivateKey() { - return secureStorageProvider.getAsync(KEY_ENCRYPTED_PRIVATE_USER_KEY) - .map(String::new); - } - - /** - * Obtain from secure storage the ChallengePublicKey for a ChallengeType. - */ - public Observable getChallengePublicKey(ChallengeType type) { - - return secureStorageProvider - .getAsync(KEY_CHALLENGE_PUBLIC_KEY + type.toString()) - .map(serialization -> { - - // Latest ChallengePublicKey.deserialize() assumes pub key serialization has - // salt (and pub key version). If the length of the pub key serialization - // doesn't include the salt, it means the salt migration hasn't run. We need - // that to run to properly migrated ChallengePublicKey to have salt and version. - if (serialization.length == ChallengePublicKey.PUBLIC_KEY_LENGTH) { - throw new MissingMigrationError("ChallengePublicKey salt"); - } - - final ChallengePublicKey challengePublicKey = ChallengePublicKey.deserialize( - serialization - ); - - // NOTE: - // Old Apollo versions did not store the challenge key salt. Modern Apollo - // versions concatenate the salt to the public key bytes (see the store method - // below). We use the length of the stored key to determine whether the salt - // is present (the subArray will be empty if not): - if (Objects.requireNonNull(challengePublicKey.getSalt()).length == 0) { - throw new MissingMigrationError("ChallengePublicKey salt"); - } - - return challengePublicKey; - }); - } - - /** - * Return true if ChallengeKeys have already been migrated. - */ - public boolean hasMigratedChallengeKeys() { - return getChallengePublicKey(ChallengeType.PASSWORD) - .map(key -> true) - .compose(ObservableFn.onTypedErrorResumeNext( - SecureStorageNoSuchElementError.class, - error -> Observable.just(true) // nothing to migrate if the user is UU - )) - .compose(ObservableFn.onTypedErrorResumeNext( - MissingMigrationError.class, - error -> Observable.just(false) - )) - .toBlocking() - .first(); - } - - /** - * Return true if the public key for a ChallengeType is present in storage. - */ - public boolean hasChallengePublicKey(ChallengeType type) { - return secureStorageProvider.has(KEY_CHALLENGE_PUBLIC_KEY + type.toString()); - } - - /** - * Saves a string containing an encrypted private muun key. - * - * @param encryptedPrivateMuunKey the encrypted key plus metadata. - */ - public void storeEncryptedMuunPrivateKey(String encryptedPrivateMuunKey) { - - Timber.d("Stored encrypted muun key on secure storage."); - - secureStorageProvider.put( - KEY_ENCRYPTED_PRIVATE_MUUN_KEY, - encryptedPrivateMuunKey.getBytes() - ); - } - - public Observable getEncryptedMuunPrivateKey() { - return secureStorageProvider.getAsync(KEY_ENCRYPTED_PRIVATE_MUUN_KEY) - .map(String::new); - } - - /** - * Save a fingerprint corresponding to Muun's private key. - */ - public void storeMuunKeyFingerprint(String muunKeyFingerprint) { - muunKeyFingerprintPreference.set(muunKeyFingerprint); - } - - public Observable getMuunKeyFingerprint() { - return muunKeyFingerprintPreference.asObservable(); - } - - /** - * Save a fingerprint corresponding to the user private key. - */ - public void storeUserKeyFingerprint(String userKeyFingerprint) { - userKeyFingerprintPreference.set(userKeyFingerprint); - } - - public Observable getUserKeyFingerprint() { - return userKeyFingerprintPreference.asObservable(); - } - - /** - * Stores a public challenge key on secure storage. - * - * @param publicKey challenge public key. - * @param type the challenge type. - */ - public void storePublicChallengeKey(ChallengePublicKey publicKey, ChallengeType type) { - - Timber.d("Storing challenge public key %s on secure storage.", type.toString()); - - if (publicKey.getSalt() == null) { - throw new MissingMigrationError("ChallengePublicKey salt"); - } - - secureStorageProvider.put( - KEY_CHALLENGE_PUBLIC_KEY + type, - publicKey.serialize() - ); - - // Update the encrypted Apollo private key when some challenge key changes. - if (secureStorageProvider.has(KEY_BASE_58_PRIVATE_KEY)) { - getBasePrivateKey() - .flatMap(this::storeEncryptedBasePrivateKey) - .toBlocking() - .first(); - } - } - - /** - * Add the salt to an existing challenge key for old sessions. - * - *

This is compat method and should not be used for anything else.

- */ - public void seasonPublicChallengeKey(final byte[] salt, final ChallengeType type) { - Timber.d("Adding salt to %s", type.toString()); - - final byte[] publicKey = secureStorageProvider - .get(KEY_CHALLENGE_PUBLIC_KEY + type.toString()); - - if (publicKey.length != ChallengePublicKey.PUBLIC_KEY_LENGTH) { - Timber.e(new BugDetected("Tried to double-migrate salt for key " + type.toString())); - return; - } - - storePublicChallengeKey(ChallengePublicKey.buildLegacy(publicKey, salt), type); - } - - /** - * Add version to an existing challenge key. Assumes that it has salt (e.g was created with one - * or was correctly migrated, see method above). - * - *

This is compat method and should not be used for anything else.

- */ - public void addChallengePublicKeyVersionMigration(final ChallengeType type) { - - final byte[] legacyPublicKeySerialization = secureStorageProvider - .get(KEY_CHALLENGE_PUBLIC_KEY + type.toString()); - - // If serialization doesn't have salt, it means the salt migration has not run yet. Skip - // this for now and let salt migration take care of it, when it runs. - if (legacyPublicKeySerialization.length != ChallengePublicKey.PUBLIC_KEY_LENGTH + 8) { - return; - } - - final ChallengePublicKey challengePublicKey = ChallengePublicKey.deserializeLegacy( - legacyPublicKeySerialization - ); - - storePublicChallengeKey(challengePublicKey, type); - } - - @Override - protected String getFileName() { - return "keys"; - } - - /** - * Returns the max used external address index or null if none has been used. - */ - @Nullable - public Integer getMaxUsedExternalAddressIndex() { - if (maxUsedExternalAddressIndexPreference.isSet()) { - return maxUsedExternalAddressIndexPreference.get(); - } else { - return null; - } - } - - public Integer getMaxWatchingExternalAddressIndex() { - return maxWatchingExternalAddressIndexPreference.get(); - } - - /** - * Save a new max used external address index. This means the maximum index which we have - * used to generate an address. - */ - public void setMaxUsedExternalAddressIndex(@Nullable Integer maxUsedExternalAddressIndex) { - this.maxUsedExternalAddressIndexPreference.set(maxUsedExternalAddressIndex); - } - - /** - * Save a new max watching external address index. This means the maximum index which we will - * using for scanning address for incoming transactions. - */ - public void setMaxWatchingExternalAddressIndex(Integer maxWatchingExternalAddressIndex) { - this.maxWatchingExternalAddressIndexPreference.set(maxWatchingExternalAddressIndex); - } -} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/KeysRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/KeysRepository.kt new file mode 100644 index 00000000..fc454a47 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/KeysRepository.kt @@ -0,0 +1,397 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import io.muun.apollo.data.os.secure_storage.SecureStorageProvider +import io.muun.apollo.data.os.secure_storage.SecureStorageProvider.SecureStorageNoSuchElementError +import io.muun.apollo.data.preferences.adapter.PublicKeyPreferenceAdapter +import io.muun.apollo.data.preferences.rx.Preference +import io.muun.apollo.domain.errors.BugDetected +import io.muun.apollo.domain.errors.MissingMigrationError +import io.muun.apollo.domain.utils.toVoid +import io.muun.common.crypto.ChallengePublicKey +import io.muun.common.crypto.ChallengeType +import io.muun.common.crypto.hd.PrivateKey +import io.muun.common.crypto.hd.PublicKey +import io.muun.common.crypto.hd.PublicKeyPair +import io.muun.common.crypto.hd.PublicKeyTriple +import io.muun.common.crypto.hd.Schema +import io.muun.common.rx.ObservableFn +import io.muun.common.utils.Encodings +import io.muun.common.utils.Preconditions +import org.bitcoinj.core.NetworkParameters +import rx.Observable +import timber.log.Timber +import javax.inject.Inject + +// Open for mockito to mock/spy +open class KeysRepository @Inject constructor( + context: Context, + repositoryRegistry: RepositoryRegistry, + // Dependencies: + private val networkParameters: NetworkParameters, + private val secureStorageProvider: SecureStorageProvider, + private val userRepository: UserRepository, +) : BaseRepository(context, repositoryRegistry) { + + companion object { + private const val SECONDS_IN_A_DAY = 60 * 60 * 24 + + // Storage-level keys: + private const val KEY_BASE_PRIVATE_KEY_PATH = "xpriv_path" + private const val KEY_BASE_58_PRIVATE_KEY = "key_base_58_private_key" + private const val KEY_BASE_PUBLIC_KEY = "base_public_key" + private const val KEY_CHALLENGE_PUBLIC_KEY = "key_challenge_public_key_" + private const val KEY_ENCRYPTED_PRIVATE_USER_KEY = "key_encrypted_private_apollo_key" + private const val KEY_ENCRYPTED_PRIVATE_MUUN_KEY = "key_encrypted_private_muun_key" + private const val KEY_MUUN_KEY_FINGERPRINT = "fingerprint_muun_key" + private const val KEY_USER_KEY_FINGERPRINT = "fingerprint_user_key" + private const val KEY_BASE_MUUN_PUBLIC_KEY = "base_muun_public_key" + private const val KEY_BASE_SWAP_SERVER_PUBLIC_KEY = "base_swap_server_public_key" + private const val KEY_MAX_USED_EXTERNAL_ADDRESS_INDEX = + "key_max_used_external_address_index" + private const val KEY_MAX_WATCHING_EXTERNAL_ADDRESS_INDEX = + "key_max_watching_external_address_index" + } + + + // Reactive preferences: + private val basePrivateKeyPathPreference: Preference + get() = rxSharedPreferences.getString(KEY_BASE_PRIVATE_KEY_PATH) + private val basePublicKeyPreference: Preference + get() = rxSharedPreferences.getObject(KEY_BASE_PUBLIC_KEY, PublicKeyPreferenceAdapter.INSTANCE) + private val baseMuunPublicKeyPreference: Preference + get() = rxSharedPreferences.getObject(KEY_BASE_MUUN_PUBLIC_KEY, PublicKeyPreferenceAdapter.INSTANCE) + private val baseSwapServerPublicKeyPreference: Preference + get() = rxSharedPreferences.getObject( + KEY_BASE_SWAP_SERVER_PUBLIC_KEY, + PublicKeyPreferenceAdapter.INSTANCE + ) + private val muunKeyFingerprintPreference: Preference + get() = rxSharedPreferences.getString(KEY_MUUN_KEY_FINGERPRINT) + private val userKeyFingerprintPreference: Preference + get() = rxSharedPreferences.getString(KEY_USER_KEY_FINGERPRINT) + private val maxUsedExternalAddressIndexPreference: Preference + get() = rxSharedPreferences.getInteger(KEY_MAX_USED_EXTERNAL_ADDRESS_INDEX) + private val maxWatchingExternalAddressIndexPreference: Preference + get() = rxSharedPreferences.getInteger(KEY_MAX_WATCHING_EXTERNAL_ADDRESS_INDEX) + + override val fileName get() = "keys" + + /** + * Returns a new non-stored root PrivateKey. + */ + fun createRootPrivateKey(): PrivateKey { + return PrivateKey.getNewRootPrivateKey(networkParameters) + } + + /** + * Returns the current base private key. + */ + val basePrivateKey: Observable + get() = secureStorageProvider.getAsync(KEY_BASE_58_PRIVATE_KEY) + .map { base58Bytes: ByteArray? -> + val path = basePrivateKeyPathPreference.get() + val base58 = String(base58Bytes!!) + PrivateKey.deserializeFromBase58(path, base58) + } + + /** + * Returns the base public key. + */ + val basePublicKey: PublicKey + get() { + val publicKey = basePublicKeyPreference.get() + Preconditions.checkState(publicKey != null, "PublicKey must NOT be null!") + return publicKey!! + } + + /** + * Returns the base Muun cosigning public key. + */ + val baseMuunPublicKey: PublicKey + get() { + val publicKey = baseMuunPublicKeyPreference.get() + Preconditions.checkState( + publicKey != null, + "Muun cosigning PublicKey must NOT be null!" + ) + return publicKey!! + } + + /** + * Returns the swap server public key for this user. + */ + private val baseSwapServerPublicKey: PublicKey? + get() = baseSwapServerPublicKeyPreference.get() + + /** + * Returns the base user-Muun public key pair. + */ + @get:Deprecated("use getBasePublicKeyTriple", ReplaceWith("basePublicKeyTriple")) + val basePublicKeyPair: PublicKeyPair + get() = PublicKeyPair( + basePublicKey, + baseMuunPublicKey + ) + + /** + * Returns the base user-Muun-swapServer public key triple. + */ + val basePublicKeyTriple: PublicKeyTriple + get() = PublicKeyTriple( + basePublicKey, + baseMuunPublicKey, + baseSwapServerPublicKey + ) + + /** + * Save the base Muun cosigning public key. + */ + fun storeBaseMuunPublicKey(publicKey: PublicKey) { + baseMuunPublicKeyPreference.set(publicKey) + } + + /** + * Save the base swap server key for this user. + */ + fun storeSwapServerPublicKey(publicKey: PublicKey) { + baseSwapServerPublicKeyPreference.set(publicKey) + } + + /** + * Saves the base private key from which all are derived. + */ + fun storeBasePrivateKey(privateKey: PrivateKey): Observable { + // The private key can be rooted at different derivation paths depending on + // when it was generated. Older apps rooted them at m. The now expected path is + // Schema#getBasePath, so we derive to it to ensure consistency for older keys. + val basePrivateKey = privateKey.deriveFromAbsolutePath(Schema.getBasePath()) + val base58 = basePrivateKey.serializeBase58().toByteArray() + return secureStorageProvider.putAsync(KEY_BASE_58_PRIVATE_KEY, base58) + .flatMap { storeEncryptedBasePrivateKey(basePrivateKey) } + .doOnNext { + basePrivateKeyPathPreference.set(basePrivateKey.absoluteDerivationPath) + basePublicKeyPreference.set(basePrivateKey.publicKey) + maxUsedExternalAddressIndexPreference.set(-1) // set a default + } + .toVoid() // Needed because JAVA!!! (rm after secureStorageProvider is kotlinized) + } + + private fun storeEncryptedBasePrivateKey(basePrivateKey: PrivateKey): Observable { + return if (!hasChallengePublicKey(ChallengeType.RECOVERY_CODE)) { + // Without challenge keys, we can't store the exportable encrypted private key: + Observable.just(null) + } else getChallengePublicKey(ChallengeType.RECOVERY_CODE) + .map { challengePublicKey: ChallengePublicKey -> + val birthday = walletBirthdaySinceGenesis + challengePublicKey.encryptPrivateKey(basePrivateKey, birthday) + } + .flatMap { encryptedKey: String -> + Preconditions.checkNotNull(encryptedKey) + Timber.d("Storing encrypted Apollo private key in secure storage.") + storeUserKeyFingerprint(Encodings.bytesToHex(basePrivateKey.fingerprint)) + secureStorageProvider.putAsync( + KEY_ENCRYPTED_PRIVATE_USER_KEY, + encryptedKey.toByteArray() + ) + } + } + + // a placeholder, only for the user key. Not ideal, not necessary. + private val walletBirthdaySinceGenesis: Long + get() { + val createdAt = userRepository.fetchOneOptional() + .flatMap { it.createdAt } + .orElse(null) + ?: return 0xFFFF // a placeholder, only for the user key. Not ideal, not necessary. + val creationTimestamp = createdAt.toEpochSecond() + val genesisTimestamp = networkParameters.genesisBlock.timeSeconds + return (creationTimestamp - genesisTimestamp) / SECONDS_IN_A_DAY + } + + val encryptedBasePrivateKey: Observable + get() = secureStorageProvider.getAsync(KEY_ENCRYPTED_PRIVATE_USER_KEY) + .map { bytes: ByteArray -> String(bytes) } + + /** + * Obtain from secure storage the ChallengePublicKey for a ChallengeType. + */ + fun getChallengePublicKey(type: ChallengeType): Observable { + return secureStorageProvider + .getAsync(KEY_CHALLENGE_PUBLIC_KEY + type.toString()) + .map { serialization: ByteArray -> + + // Latest ChallengePublicKey.deserialize() assumes pub key serialization has + // salt (and pub key version). If the length of the pub key serialization + // doesn't include the salt, it means the salt migration hasn't run. We need + // that to run to properly migrated ChallengePublicKey to have salt and version. + if (serialization.size == ChallengePublicKey.PUBLIC_KEY_LENGTH) { + throw MissingMigrationError("ChallengePublicKey salt") + } + val challengePublicKey = ChallengePublicKey.deserialize(serialization) + + // NOTE: + // Old Apollo versions did not store the challenge key salt. Modern Apollo + // versions concatenate the salt to the public key bytes (see the store method + // below). We use the length of the stored key to determine whether the salt + // is present (the subArray will be empty if not): + if (requireNotNull(challengePublicKey.salt).isEmpty()) { + throw MissingMigrationError("ChallengePublicKey salt") + } + challengePublicKey + } + } + + /** + * Return true if ChallengeKeys have already been migrated. + */ + fun hasMigratedChallengeKeys(): Boolean { + return getChallengePublicKey(ChallengeType.PASSWORD) + .map { true } + .compose(ObservableFn.onTypedErrorResumeNext( + SecureStorageNoSuchElementError::class.java + ) { Observable.just(true) } // nothing to migrate if the user is UU + ) + .compose(ObservableFn.onTypedErrorResumeNext( + MissingMigrationError::class.java + ) { Observable.just(false) }) + .toBlocking() + .first() + } + + /** + * Return true if the public key for a ChallengeType is present in storage. + */ + fun hasChallengePublicKey(type: ChallengeType): Boolean { + return secureStorageProvider.has(KEY_CHALLENGE_PUBLIC_KEY + type.toString()) + } + + /** + * Saves a string containing an encrypted private muun key. + * + * @param encryptedPrivateMuunKey the encrypted key plus metadata. + */ + fun storeEncryptedMuunPrivateKey(encryptedPrivateMuunKey: String) { + Timber.d("Stored encrypted muun key on secure storage.") + secureStorageProvider.put( + KEY_ENCRYPTED_PRIVATE_MUUN_KEY, + encryptedPrivateMuunKey.toByteArray() + ) + } + + val encryptedMuunPrivateKey: Observable + get() = secureStorageProvider.getAsync(KEY_ENCRYPTED_PRIVATE_MUUN_KEY) + .map { bytes: ByteArray -> String(bytes) } + + /** + * Save a fingerprint corresponding to Muun's private key. + */ + fun storeMuunKeyFingerprint(muunKeyFingerprint: String) { + muunKeyFingerprintPreference.set(muunKeyFingerprint) + } + + val muunKeyFingerprint: Observable + get() = muunKeyFingerprintPreference.asObservable() + + /** + * Save a fingerprint corresponding to the user private key. + */ + fun storeUserKeyFingerprint(userKeyFingerprint: String) { + userKeyFingerprintPreference.set(userKeyFingerprint) + } + + val userKeyFingerprint: Observable + get() = userKeyFingerprintPreference.asObservable() + + /** + * Stores a public challenge key on secure storage. + * + * @param publicKey challenge public key. + * @param type the challenge type. + */ + fun storePublicChallengeKey(publicKey: ChallengePublicKey, type: ChallengeType) { + Timber.d("Storing challenge public key %s on secure storage.", type.toString()) + if (publicKey.salt == null) { + throw MissingMigrationError("ChallengePublicKey salt") + } + secureStorageProvider.put( + KEY_CHALLENGE_PUBLIC_KEY + type, + publicKey.serialize() + ) + + // Update the encrypted Apollo private key when some challenge key changes. + if (secureStorageProvider.has(KEY_BASE_58_PRIVATE_KEY)) { + basePrivateKey + .flatMap { basePrivateKey: PrivateKey -> + storeEncryptedBasePrivateKey(basePrivateKey) + } + .toBlocking() + .first() + } + } + + /** + * Add the salt to an existing challenge key for old sessions. + * + * + * This is compat method and should not be used for anything else. + */ + fun seasonPublicChallengeKey(salt: ByteArray, type: ChallengeType) { + Timber.d("Adding salt to %s", type.toString()) + val publicKey = secureStorageProvider[KEY_CHALLENGE_PUBLIC_KEY + type.toString()] + if (publicKey.size != ChallengePublicKey.PUBLIC_KEY_LENGTH) { + Timber.e(BugDetected("Tried to double-migrate salt for key $type")) + return + } + storePublicChallengeKey(ChallengePublicKey.buildLegacy(publicKey, salt), type) + } + + /** + * Add version to an existing challenge key. Assumes that it has salt (e.g was created with one + * or was correctly migrated, see method above). + * + * + * This is compat method and should not be used for anything else. + */ + fun addChallengePublicKeyVersionMigration(type: ChallengeType) { + val legacyPublicKeySerialization = + secureStorageProvider[KEY_CHALLENGE_PUBLIC_KEY + type.toString()] + + // If serialization doesn't have salt, it means the salt migration has not run yet. Skip + // this for now and let salt migration take care of it, when it runs. + if (legacyPublicKeySerialization.size != ChallengePublicKey.PUBLIC_KEY_LENGTH + 8) { + return + } + val challengePublicKey = ChallengePublicKey.deserializeLegacy( + legacyPublicKeySerialization + ) + storePublicChallengeKey(challengePublicKey, type) + } + + /** + * Returns the max used external address index or null if none has been used. + */ + var maxUsedExternalAddressIndex: Int? + get() = if (maxUsedExternalAddressIndexPreference.isSet) { + maxUsedExternalAddressIndexPreference.get() + } else { + null + } + /** + * Save a new max used external address index. This means the maximum index which we have + * used to generate an address. + */ + set(maxUsedExternalAddressIndex) { + maxUsedExternalAddressIndexPreference.set(maxUsedExternalAddressIndex) + } + + /** + * Save a new max watching external address index. This means the maximum index which we will + * using for scanning address for incoming transactions. + */ + var maxWatchingExternalAddressIndex: Int + get() = maxWatchingExternalAddressIndexPreference.get()!! + set(maxWatchingExternalAddressIndex) { + maxWatchingExternalAddressIndexPreference.set(maxWatchingExternalAddressIndex) + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/MinFeeRateRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/MinFeeRateRepository.kt index 68d72ccd..2172bd20 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/MinFeeRateRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/MinFeeRateRepository.kt @@ -15,15 +15,14 @@ open class MinFeeRateRepository @Inject constructor( private const val KEY = "MIN_FEE_RATE" } - private val preference: Preference = rxSharedPreferences.getObject( - KEY, - 1.0, // Default (lowest limit) just to avoid NPE problems before RTD is pulled - DoublePreferenceAdapter.INSTANCE - ) - - override fun getFileName(): String { - return "min_fee_rate" - } + private val preference: Preference + get() = rxSharedPreferences.getObject( + KEY, + 1.0, // Default (lowest limit) just to avoid NPE problems before RTD is pulled + DoublePreferenceAdapter.INSTANCE + ) + + override val fileName get() = "min_fee_rate" /** * Fetch an observable instance of the latest min fee rate in weight units. diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/NightModeRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/NightModeRepository.kt index d6cc4e04..2a1d6202 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/NightModeRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/NightModeRepository.kt @@ -14,14 +14,14 @@ class NightModeRepository @Inject constructor( private const val KEY = "current_night_mode" } - private val nightModePreference: Preference = rxSharedPreferences.getEnum( - KEY, - NightMode.FOLLOW_SYSTEM, - NightMode::class.java - ) - - override fun getFileName(): String = - "current_night_mode" + private val nightModePreference: Preference + get() = rxSharedPreferences.getEnum( + KEY, + NightMode.FOLLOW_SYSTEM, + NightMode::class.java + ) + + override val fileName get() = "current_night_mode" fun storeNightMode(mode: NightMode) { nightModePreference.set(mode) diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationPermissionDeniedRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationPermissionDeniedRepository.kt index ba7f0b5f..af4fa6d1 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationPermissionDeniedRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationPermissionDeniedRepository.kt @@ -13,11 +13,10 @@ class NotificationPermissionDeniedRepository @Inject constructor( private const val NOTIFICATION_PERMISSION_DENIED = "notification_permission_denied" } - private val permissionDeniedPref: Preference = rxSharedPreferences - .getBoolean(NOTIFICATION_PERMISSION_DENIED) + private val permissionDeniedPref: Preference + get() = rxSharedPreferences.getBoolean(NOTIFICATION_PERMISSION_DENIED) - override fun getFileName() = - "notification_permission_denied" + override val fileName get() = "notification_permission_denied" fun setHasPreviouslyDeniedNotificationPermission() { permissionDeniedPref.set(true) diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationPermissionSkippedRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationPermissionSkippedRepository.kt index d238783d..01a740dd 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationPermissionSkippedRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationPermissionSkippedRepository.kt @@ -13,11 +13,10 @@ class NotificationPermissionSkippedRepository @Inject constructor( private const val NOTIFICATION_PERMISSION_SKIPPED = "notification_permission_skipped" } - private val permissionSkippedPref: Preference = rxSharedPreferences - .getBoolean(NOTIFICATION_PERMISSION_SKIPPED) + private val permissionSkippedPref: Preference + get() = rxSharedPreferences.getBoolean(NOTIFICATION_PERMISSION_SKIPPED) - override fun getFileName() = - "notification_permission_skipped" + override val fileName get() = "notification_permission_skipped" fun store(permissionSkipped: Boolean) { permissionSkippedPref.set(permissionSkipped) diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationRepository.java deleted file mode 100644 index 2e272117..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationRepository.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.muun.apollo.data.preferences; - - -import io.muun.apollo.data.preferences.rx.Preference; - -import android.content.Context; - -import javax.inject.Inject; - -public class NotificationRepository extends BaseRepository { - - private static final String KEY_LAST_PROCESSED_ID = "last_processed_id"; - private static final String KEY_PROCESSING_FAILURES = "processing_failures"; - - private final Preference lastProcessedIdPreference; - private final Preference processingFailuresPreference; - - @Inject - public NotificationRepository(Context context, RepositoryRegistry repositoryRegistry) { - super(context, repositoryRegistry); - lastProcessedIdPreference = rxSharedPreferences.getLong(KEY_LAST_PROCESSED_ID, 0L); - processingFailuresPreference = rxSharedPreferences.getLong(KEY_PROCESSING_FAILURES, 0L); - } - - @Override - protected String getFileName() { - return "notifications"; - } - - public long getLastProcessedId() { - return lastProcessedIdPreference.get(); - } - - public void setLastProcessedId(long lastConfirmedId) { - if (getLastProcessedId() < lastConfirmedId) { - processingFailuresPreference.set(0L); - } - lastProcessedIdPreference.set(lastConfirmedId); - } - - public void increaseProcessingFailures() { - processingFailuresPreference.set(getProcessingFailures() + 1); - } - - public long getProcessingFailures() { - return processingFailuresPreference.get(); - } -} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationRepository.kt new file mode 100644 index 00000000..44040119 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/NotificationRepository.kt @@ -0,0 +1,40 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import io.muun.apollo.data.preferences.rx.Preference +import javax.inject.Inject + +// Open for mockito to mock/spy +open class NotificationRepository @Inject constructor( + context: Context, + repositoryRegistry: RepositoryRegistry, +) : BaseRepository(context, repositoryRegistry) { + + companion object { + private const val KEY_LAST_PROCESSED_ID = "last_processed_id" + private const val KEY_PROCESSING_FAILURES = "processing_failures" + } + + private val lastProcessedIdPreference: Preference + get() = rxSharedPreferences.getLong(KEY_LAST_PROCESSED_ID, 0L) + private val processingFailuresPreference: Preference + get() = rxSharedPreferences.getLong(KEY_PROCESSING_FAILURES, 0L) + + override val fileName get() = "notifications" + + var lastProcessedId: Long + get() = lastProcessedIdPreference.get()!! + set(lastConfirmedId) { + if (lastProcessedId < lastConfirmedId) { + processingFailuresPreference.set(0L) + } + lastProcessedIdPreference.set(lastConfirmedId) + } + + fun increaseProcessingFailures() { + processingFailuresPreference.set(processingFailures + 1) + } + + val processingFailures: Long + get() = processingFailuresPreference.get()!! +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/PlayIntegrityNonceRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/PlayIntegrityNonceRepository.kt index 17076c22..a8293a4a 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/PlayIntegrityNonceRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/PlayIntegrityNonceRepository.kt @@ -13,11 +13,10 @@ class PlayIntegrityNonceRepository @Inject constructor( private const val PLAY_INTEGRITY_NONCE = "play_integrity_nonce" } - private val noncePref: Preference = rxSharedPreferences - .getString(PLAY_INTEGRITY_NONCE) + private val noncePref: Preference + get() = rxSharedPreferences.getString(PLAY_INTEGRITY_NONCE) - override fun getFileName() = - "play_integrity_nonce" + override val fileName get() = "play_integrity_nonce" fun store(nonce: String?) { noncePref.set(nonce) diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/RepositoryRegistry.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/RepositoryRegistry.kt index d5b79fc2..45f95fd2 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/RepositoryRegistry.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/RepositoryRegistry.kt @@ -27,29 +27,29 @@ class RepositoryRegistry { private val lock = this private val registry: Set> = setOf( - ApiMigrationsVersionRepository::class.java, - AuthRepository::class.java, - BlockchainHeightRepository::class.java, - ClientVersionRepository::class.java, - ExchangeRateWindowRepository::class.java, - FeeWindowRepository::class.java, - FirebaseInstallationIdRepository::class.java, - ForwardingPoliciesRepository::class.java, - MinFeeRateRepository::class.java, - KeysRepository::class.java, - NightModeRepository::class.java, - NotificationRepository::class.java, - SchemaVersionRepository::class.java, - TransactionSizeRepository::class.java, - UserPreferencesRepository::class.java, - UserRepository::class.java, - FeaturesRepository::class.java, - AppVersionRepository::class.java, - PlayIntegrityNonceRepository::class.java, - NotificationPermissionStateRepository::class.java, - NotificationPermissionDeniedRepository::class.java, - NotificationPermissionSkippedRepository::class.java, - BackgroundTimesRepository::class.java + ApiMigrationsVersionRepository::class.java, + AuthRepository::class.java, + BlockchainHeightRepository::class.java, + ClientVersionRepository::class.java, + ExchangeRateWindowRepository::class.java, + FeeWindowRepository::class.java, + FirebaseInstallationIdRepository::class.java, + ForwardingPoliciesRepository::class.java, + MinFeeRateRepository::class.java, + KeysRepository::class.java, + NightModeRepository::class.java, + NotificationRepository::class.java, + SchemaVersionRepository::class.java, + TransactionSizeRepository::class.java, + UserPreferencesRepository::class.java, + UserRepository::class.java, + FeaturesRepository::class.java, + AppVersionRepository::class.java, + PlayIntegrityNonceRepository::class.java, + NotificationPermissionStateRepository::class.java, + NotificationPermissionDeniedRepository::class.java, + NotificationPermissionSkippedRepository::class.java, + BackgroundTimesRepository::class.java ) // Notable exceptions: @@ -63,11 +63,11 @@ class RepositoryRegistry { // its more clear and clean to keep it and avoid wiping it (there's no privacy or security // issues). private val logoutSurvivorRepositories: Set> = setOf( - FirebaseInstallationIdRepository::class.java, - NightModeRepository::class.java, - SchemaVersionRepository::class.java, - NotificationPermissionDeniedRepository::class.java, - NotificationPermissionSkippedRepository::class.java + FirebaseInstallationIdRepository::class.java, + NightModeRepository::class.java, + SchemaVersionRepository::class.java, + NotificationPermissionDeniedRepository::class.java, + NotificationPermissionSkippedRepository::class.java ) // Note: the use of a map is critical here for 2 reasons, both of them related to memory @@ -80,7 +80,7 @@ class RepositoryRegistry { // a reference to that "old" repository instance (e.g objects where that repository was injected // as a dependency). private val loadedRepos: MutableMap, BaseRepository> = - mutableMapOf() + mutableMapOf() fun load(repo: BaseRepository) { synchronized(lock) { @@ -90,7 +90,7 @@ class RepositoryRegistry { loadedRepos[repo.javaClass] = repo Timber.d( - "RepositoryRegistry#load(${repo.javaClass.simpleName}). Size: ${loadedRepos.size}" + "RepositoryRegistry#load(${repo.javaClass.simpleName}). Size: ${loadedRepos.size}" ) } } @@ -100,12 +100,12 @@ class RepositoryRegistry { * logoutSurvivorRepositories. */ fun repositoriesToClearOnLogout(): Collection = - synchronized(lock) { - loadedRepos.filterKeys { - !logoutSurvivorRepositories.contains(it) - }.values - } + synchronized(lock) { + loadedRepos.filterKeys { + !logoutSurvivorRepositories.contains(it) + }.values + } private fun isRegistered(repository: BaseRepository) = - registry.contains(repository.javaClass) + registry.contains(repository.javaClass) } \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/SchemaVersionRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/SchemaVersionRepository.java deleted file mode 100644 index b5cfa677..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/SchemaVersionRepository.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.muun.apollo.data.preferences; - -import io.muun.apollo.data.preferences.rx.Preference; - -import android.content.Context; - -import javax.inject.Inject; - -public class SchemaVersionRepository extends BaseRepository { - - private static final String KEY_VERSION = "version"; - - private final Preference versionPreference; - - /** - * Creates a repository. - */ - @Inject - public SchemaVersionRepository(Context context, RepositoryRegistry repositoryRegistry) { - super(context, repositoryRegistry); - versionPreference = rxSharedPreferences.getInteger(KEY_VERSION); - } - - @Override - protected String getFileName() { - return "schema_version"; - } - - /** - * Returns the stored version, or 0 if none is stored. - */ - public int getVersion() { - return versionPreference.get(); - } - - public boolean hasVersion() { - return versionPreference.isSet(); - } - - public void setVersion(int version) { - versionPreference.set(version); - } - -} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/SchemaVersionRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/SchemaVersionRepository.kt new file mode 100644 index 00000000..75f39c54 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/SchemaVersionRepository.kt @@ -0,0 +1,33 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import io.muun.apollo.data.preferences.rx.Preference +import javax.inject.Inject + +class SchemaVersionRepository @Inject constructor( + context: Context, + repositoryRegistry: RepositoryRegistry, +) : BaseRepository(context, repositoryRegistry) { + + companion object { + private const val KEY_VERSION = "version" + } + + private val versionPreference: Preference + get() = rxSharedPreferences.getInteger(KEY_VERSION) + + override val fileName get() = "schema_version" + + /** + * Returns the stored version, or 0 if none is stored. + */ + var version: Int + get() = versionPreference.get()!! + set(version) { + versionPreference.set(version) + } + + fun hasVersion(): Boolean { + return versionPreference.isSet + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/TransactionSizeRepository.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/TransactionSizeRepository.java deleted file mode 100644 index de0fabe0..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/TransactionSizeRepository.java +++ /dev/null @@ -1,113 +0,0 @@ -package io.muun.apollo.data.preferences; - - -import io.muun.apollo.data.preferences.rx.Preference; -import io.muun.apollo.data.serialization.SerializationUtils; -import io.muun.apollo.domain.model.NextTransactionSize; -import io.muun.common.utils.Preconditions; - -import android.content.Context; -import rx.Observable; - -import javax.annotation.Nullable; -import javax.inject.Inject; - -public class TransactionSizeRepository extends BaseRepository { - - private static final String KEY_TRANSACTION_SIZE = "transaction_size"; - - private final Preference transactionSizePreference; - - @Inject - public TransactionSizeRepository(Context context, RepositoryRegistry repositoryRegistry) { - super(context, repositoryRegistry); - transactionSizePreference = rxSharedPreferences.getString(KEY_TRANSACTION_SIZE, null); - } - - @Override - protected String getFileName() { - return "transaction_size"; - } - - /** - * Get the stored NextTransactionSize. - */ - @Nullable - public NextTransactionSize getNextTransactionSize() { - return watchNextTransactionSize().toBlocking().first(); - } - - /** - * Watch the stored NextTransactionSize. - */ - public Observable watchNextTransactionSize() { - return transactionSizePreference.asObservable() - .map(json -> { - if (json != null) { - return SerializationUtils.deserializeJson(NextTransactionSize.class, json); - } else { - return null; - } - }); - } - - /** - * Replace the stored NextTransactionSize. - */ - public void setTransactionSize(NextTransactionSize transactionSize) { - transactionSizePreference.setNow( - SerializationUtils.serializeJson(NextTransactionSize.class, transactionSize) - ); - } - - /** - * Migration to init expected debt for pre-existing NTSs. - */ - public void initExpectedDebt() { - final boolean hasNts = sharedPreferences.contains(KEY_TRANSACTION_SIZE); - - if (!hasNts) { - return; - } - - final NextTransactionSize nts = getNextTransactionSize(); - - Preconditions.checkNotNull(nts); - - setTransactionSize(nts.initExpectedDebt()); - } - - /** - * Migration to init outpoints for pre-existing NTSs. - */ - public void initNtsOutpoints() { - final boolean hasNts = sharedPreferences.contains(KEY_TRANSACTION_SIZE); - - if (!hasNts) { - return; - } - - final NextTransactionSize nts = getNextTransactionSize(); - - Preconditions.checkNotNull(nts); - - setTransactionSize(nts.initOutpoints()); - } - - /** - * Migration to init utxo status for pre-existing NTSs. - */ - public void initNtsUtxoStatus() { - final boolean hasNts = sharedPreferences.contains(KEY_TRANSACTION_SIZE); - - if (!hasNts) { - return; - } - - final NextTransactionSize nts = getNextTransactionSize(); - - Preconditions.checkNotNull(nts); - - setTransactionSize(nts.initUtxoStatus()); - } -} diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/TransactionSizeRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/TransactionSizeRepository.kt new file mode 100644 index 00000000..451a8011 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/TransactionSizeRepository.kt @@ -0,0 +1,102 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import io.muun.apollo.data.preferences.rx.Preference +import io.muun.apollo.data.serialization.SerializationUtils +import io.muun.apollo.domain.model.NextTransactionSize +import io.muun.common.utils.Preconditions +import rx.Observable +import javax.inject.Inject + +class TransactionSizeRepository @Inject constructor( + context: Context, + repositoryRegistry: RepositoryRegistry, +) : BaseRepository(context, repositoryRegistry) { + + companion object { + private const val KEY_TRANSACTION_SIZE = "transaction_size" + } + + private val transactionSizePreference: Preference + get() = rxSharedPreferences.getString(KEY_TRANSACTION_SIZE, null) + + override val fileName get() = "transaction_size" + + /** + * Get the stored NextTransactionSize. + */ + val nextTransactionSize: NextTransactionSize? + get() = watchNextTransactionSize().toBlocking().first() + + /** + * Watch the stored NextTransactionSize. + * Only to be used after transactionSizePreference.isSet == true. + */ + fun watchNonNullNts(): Observable { + return watchNextTransactionSize().map { nts -> nts!! } + } + + /** + * Watch the stored NextTransactionSize. + */ + fun watchNextTransactionSize(): Observable { + return transactionSizePreference.asObservable() + .map { json: String? -> + if (json != null) { + return@map SerializationUtils.deserializeJson( + NextTransactionSize::class.java, json + ) + } else { + return@map null + } + } + } + + /** + * Replace the stored NextTransactionSize. + */ + fun setTransactionSize(transactionSize: NextTransactionSize) { + transactionSizePreference.setNow( + SerializationUtils.serializeJson(NextTransactionSize::class.java, transactionSize) + ) + } + + /** + * Migration to init expected debt for pre-existing NTSs. + */ + fun initExpectedDebt() { + val hasNts = sharedPreferences.contains(KEY_TRANSACTION_SIZE) + if (!hasNts) { + return + } + val nts = nextTransactionSize + Preconditions.checkNotNull(nts) + setTransactionSize(nts!!.initExpectedDebt()) + } + + /** + * Migration to init outpoints for pre-existing NTSs. + */ + fun initNtsOutpoints() { + val hasNts = sharedPreferences.contains(KEY_TRANSACTION_SIZE) + if (!hasNts) { + return + } + val nts = nextTransactionSize + Preconditions.checkNotNull(nts) + setTransactionSize(nts!!.initOutpoints()) + } + + /** + * Migration to init utxo status for pre-existing NTSs. + */ + fun initNtsUtxoStatus() { + val hasNts = sharedPreferences.contains(KEY_TRANSACTION_SIZE) + if (!hasNts) { + return + } + val nts = nextTransactionSize + Preconditions.checkNotNull(nts) + setTransactionSize(nts!!.initUtxoStatus()) + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/UserPreferencesRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/UserPreferencesRepository.kt index 035b7a50..2046087d 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/UserPreferencesRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/UserPreferencesRepository.kt @@ -20,16 +20,14 @@ open class UserPreferencesRepository @Inject constructor( private const val KEY = "USER_PREFERENCES" } - private val preference: Preference = - rxSharedPreferences.getObject( + private val preference: Preference + get() = rxSharedPreferences.getObject( KEY, StoredUserPreferences(), JsonPreferenceAdapter(StoredUserPreferences::class.java) ) - override fun getFileName(): String { - return KEY - } + override val fileName get() = KEY open fun watch(): Observable { return preference.asObservable() 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 deleted file mode 100644 index 14807b8c..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/UserRepository.java +++ /dev/null @@ -1,721 +0,0 @@ -package io.muun.apollo.data.preferences; - -import io.muun.apollo.data.preferences.adapter.JsonPreferenceAdapter; -import io.muun.apollo.data.preferences.rx.Preference; -import io.muun.apollo.data.preferences.stored.StoredEkVerificationCodes; -import io.muun.apollo.data.serialization.SerializationUtils; -import io.muun.apollo.domain.model.BitcoinUnit; -import io.muun.apollo.domain.model.EmergencyKitExport; -import io.muun.apollo.domain.model.PermissionState; -import io.muun.apollo.domain.model.user.EmergencyKit; -import io.muun.apollo.domain.model.user.User; -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; - -import android.content.Context; -import android.content.SharedPreferences; -import android.net.Uri; -import androidx.annotation.NonNull; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import libwallet.Libwallet; -import rx.Observable; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.TreeSet; -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.money.CurrencyUnit; - -@Singleton -public class UserRepository extends BaseRepository { - - private static final String KEY_USER = "user"; - - private static final String KEY_LAST_COPIED_ADDRESS = "key_last_copied_address"; - - private static final String PENDING_PROFILE_PICTURE_URI_KEY = "pending_profile_picture_uri_key"; - - private static final String PASSWORD_CHANGE_AUTHORIZED_UUID = "password_change_authorized_uuid"; - - private static final String CONTACTS_PERMISSION_STATE_KEY = "contacts_permission_state_key"; - - private static final String INITIAL_SYNC_COMPLETED_KEY = "initial_sync_completed_key"; - - private static final String RC_SETUP_IN_PROCESS = "rc_setup_in_process"; - - private static final String DISPLAY_SATS = "use_sats_as_currency"; - - private static final String PENDING_EMAIL_LINK = "pending_email_link"; - - private static final String BALANCE_HIDDEN_KEY = "balance_hidden_key"; - - private static final String TAPROOT_CELEBRATION_PENDING = "taproot_celebration_pending"; - - private static final String SEEN_WELCOME_TO_MUUN_DIALOG_KEY = "seen_welcome_to_muun_dialog_key"; - - // Preferences: - - private final Preference lastCopiedAddress; - - private final Preference pendingProfilePictureUriPreference; - - private final Preference passwordChangeAuthorizedUuidPreference; - - private final Preference conctactsPermissionStatePreference; - - private final Preference initialSyncCompletedPreference; - - private final Preference recoveryCodeSetupInProcessPreference; - - private final Preference displaySatsPreference; - - private final Preference userPreference; - - private final Preference pendingEmailLinkPreference; - - private final Preference balanceHiddenPreference; - - // Horrible I know, but only temporary until taproot activation date. Afterwards this goes away. - private final Preference taprootCelebrationPending; - - private final Preference seenWelcomeToMuunDialogPreference; - - /** - * Creates a user preference repository. - */ - @Inject - public UserRepository(Context context, RepositoryRegistry repositoryRegistry) { - super(context, repositoryRegistry); - - userPreference = rxSharedPreferences - .getObject(KEY_USER, new UserPreferenceDebugAdapter()); - - lastCopiedAddress = rxSharedPreferences.getString(KEY_LAST_COPIED_ADDRESS); - - pendingProfilePictureUriPreference = rxSharedPreferences.getString( - PENDING_PROFILE_PICTURE_URI_KEY - ); - - passwordChangeAuthorizedUuidPreference = rxSharedPreferences.getString( - PASSWORD_CHANGE_AUTHORIZED_UUID - ); - - conctactsPermissionStatePreference = rxSharedPreferences.getEnum( - CONTACTS_PERMISSION_STATE_KEY, - PermissionState.DENIED, - PermissionState.class - ); - - initialSyncCompletedPreference = rxSharedPreferences.getBoolean( - INITIAL_SYNC_COMPLETED_KEY, - false - ); - - recoveryCodeSetupInProcessPreference = rxSharedPreferences.getBoolean(RC_SETUP_IN_PROCESS); - - displaySatsPreference = rxSharedPreferences.getEnum( - DISPLAY_SATS, - BitcoinUnit.BTC, - BitcoinUnit.class - ); - - // Non-nullable default to assure non-nullability of values - pendingEmailLinkPreference = rxSharedPreferences.getString(PENDING_EMAIL_LINK, "default"); - - balanceHiddenPreference = rxSharedPreferences.getBoolean( - BALANCE_HIDDEN_KEY, - false - ); - - taprootCelebrationPending = rxSharedPreferences.getBoolean( - TAPROOT_CELEBRATION_PENDING, - false - ); - - seenWelcomeToMuunDialogPreference = rxSharedPreferences.getBoolean( - SEEN_WELCOME_TO_MUUN_DIALOG_KEY, - false - ); - } - - @Override - protected String getFileName() { - return "user"; - } - - /** - * Stores the user. - */ - public synchronized void store(User user) { - userPreference.set(StoredUserJson.fromUser(user)); - } - - /** - * Fetches the user, throws NoSuchElementException if not present. - */ - public Observable fetch() { - return fetchOptional().map(Optional::get); - } - - /** - * Fetches the user, throws NPE if not present. - */ - public Observable> fetchOptional() { - return userPreference.asObservable() - .map(storedUser -> { - if (storedUser != null) { - return Optional.of(storedUser.toUser()); - } else { - return Optional.empty(); - } - }); - } - - /** - * Get the user for the current session. Returns an empty Optional if there's no user currently - * LOGGED_IN. - */ - public Optional fetchOneOptional() { - return fetchOptional().toBlocking().first(); - } - - /** - * Execute the migration that ends the multi-preference hell. - */ - public void migrateCthulhuToJsonPreference() { - final long hid = sharedPreferences.getLong("hid", -1L); - - if (hid == -1L) { - return; // If no logged in user, then avoid setting any preferences. - } - - final StoredUserJson value = new StoredUserJson( - hid, - sharedPreferences.getString("email", null), - sharedPreferences.getString("created_at", null), - sharedPreferences.getString("phone_number", null), - sharedPreferences.getBoolean("phone_number_verified", false), - sharedPreferences.getString("first_name", null), - sharedPreferences.getString("last_name", null), - sharedPreferences.getString("profile_picture_url", null), - sharedPreferences.getBoolean("email_verified_key", false), - sharedPreferences.getBoolean("has_recovery_code", false), - true, // all users had passwords before this feature - sharedPreferences.getBoolean("has_p2p_enabled", false), - false, // non-existent at migration time. This is a good default - sharedPreferences.getString("primary_currency_key", "USD"), - null, // non-existent at migration time - null, // non-existent at migration time - null, // non-existent at migration time - new StoredEkVerificationCodes(), // non-existent at migration time - new LinkedList<>() - ); - - userPreference.set(value); - } - - /** - * Get the user for the current session. Throws an Exception (NoSuchElementException) if there's - * no user currently LOGGED_IN. - */ - public User fetchOne() { - return fetch().toBlocking().first(); - } - - /** - * Stores a user Profile. - */ - public synchronized void storeProfile(@Nullable UserProfile profile) { - final StoredUserJson value = Preconditions.checkNotNull(userPreference.get()); - - value.setProfileFrom(profile); - userPreference.set(value); - } - - /** - * Stores a user PhoneNumber. - */ - public synchronized void storePhoneNumber(@Nullable UserPhoneNumber phoneNumber) { - final StoredUserJson value = Preconditions.checkNotNull(userPreference.get()); - - value.setPhoneNumberFrom(phoneNumber); - userPreference.set(value); - } - - /** - * Returns the Uri of a profile picture that needs to be uploaded. - */ - @Nullable - public Uri getPendingProfilePictureUri() { - final String uriString = pendingProfilePictureUriPreference.get(); - - if (uriString == null) { - return null; - } - - return Uri.parse(uriString); - } - - /** - * Enqueues a profile picture to be uploaded in the future. - */ - public void setPendingProfilePictureUri(@Nullable Uri uri) { - - if (uri == null) { - pendingProfilePictureUriPreference.delete(); - return; - } - - pendingProfilePictureUriPreference.set(uri.toString()); - } - - /** - * Note: no longer necessary a bitcoin address, can be a Ln invoice. - */ - @Nullable - public String getLastCopiedContentFromReceive() { - return lastCopiedAddress.get(); - } - - /** - * Note: no longer necessary a bitcoin address, can be a Ln invoice. - */ - public void setLastCopiedContentFromReceive(String content) { - lastCopiedAddress.set(content); - } - - /** - * Store the fact that the user has verified their email. - */ - public void storeEmailVerified() { - final StoredUserJson value = Preconditions.checkNotNull(userPreference.get()); - - value.isEmailVerified = true; - userPreference.set(value); - } - - /** - * Wait for the authorized email notification. - */ - public Observable awaitForAuthorizedPasswordChange() { - return passwordChangeAuthorizedUuidPreference.asObservable() - .filter(uuid -> uuid != null && !uuid.isEmpty()); - } - - /** - * Authorize pending password change process. - */ - public void authorizePasswordChange(String uuid) { - passwordChangeAuthorizedUuidPreference.set(uuid); - } - - /** - * Save contacts permission state. - */ - public void storeContactsPermissionState(PermissionState state) { - conctactsPermissionStatePreference.set(state); - } - - public PermissionState getContactsPermissionState() { - return conctactsPermissionStatePreference.get(); - } - - /** - * Get an Observable to observe changes to the contacts permission preference. - */ - public Observable watchContactsPermissionState() { - return conctactsPermissionStatePreference.asObservable(); - } - - /** - * Save flag to signal that user has completed initial sync, and thus is probably LOGGED_IN now. - */ - public void storeInitialSyncCompleted() { - initialSyncCompletedPreference.set(true); - } - - public boolean isInitialSyncCompleted() { - //noinspection ConstantConditions - return initialSyncCompletedPreference.get(); - } - - /** - * Save flag to signal that RecoveryCode setup process, though started, has not been completed. - */ - public void setRecoveryCodeSetupInProcess(boolean isInProcess) { - recoveryCodeSetupInProcessPreference.set(isInProcess); - } - - /** - * Get the bitcoin unit preference. - */ - public BitcoinUnit getBitcoinUnit() { - return displaySatsPreference.get(); - } - - /** - * Get an Observable to observe changes to the bitcoin unit preference. - */ - public Observable watchBitcoinUnit() { - return displaySatsPreference.asObservable(); - } - - /** - * Save a new bitcoin unit preference value. - */ - public void setBitcoinUnit(BitcoinUnit value) { - displaySatsPreference.set(value); - } - - /** - * Save email action link that is being await for confirmation (via email link click). This - * is used to distinguish between the different email links actions that we have. - */ - public void setPendingEmailLink(String emailLink) { - pendingEmailLinkPreference.set(emailLink); - } - - public String getPendingEmailLink() { - return pendingEmailLinkPreference.get(); - } - - /** - * Save user's choice to see her wallet balance hidden in our home screen. - */ - public void setBalanceHidden(boolean hidden) { - balanceHiddenPreference.set(hidden); - } - - /** - * Get an Observable to observe changes to the balance hidden preference. - */ - public Observable watchBalanceHidden() { - return balanceHiddenPreference.asObservable(); - } - - /** - * Save whether the taproot celebration is in order. - */ - public void setPendingTaprootCelebration(boolean isCelebrationPending) { - taprootCelebrationPending.set(isCelebrationPending); - } - - /** - * Get an Observable to observe changes to the Taproot Celebration pending preference. - */ - public Observable watchPendingTaprootCelebration() { - return taprootCelebrationPending.asObservable(); - } - - /** - * Save a flag signalling that the "Welcome to Muun" dialog has been shown/seen. - */ - public void setWelcomeToMuunDialogSeen() { - seenWelcomeToMuunDialogPreference.set(true); - } - - /** - * Get whether the user has seen the "Welcome to Muun" dialog or not. - */ - public Boolean getWelcomeToMuunDialogSeen() { - return seenWelcomeToMuunDialogPreference.get(); - } - - /** - * Migration to init emergency kit version. - */ - public void initEmergencyKitVersion() { - final StoredUserJson storedUser = userPreference.get(); - if (storedUser != null) { - storedUser.initEmergencyKitVersion(); - } - userPreference.set(storedUser); - } - - /** - * Save latest emergency kit verification code show to user. This can either be a verification - * code that is later successfully used, or not. - */ - public void storeEmergencyKitVerificationCode(String verificationCode) { - final StoredUserJson value = Preconditions.checkNotNull(userPreference.get()); - - value.emergencyKitCodes.addNewest(verificationCode); - userPreference.set(value); - } - - /** - * Record that this user has successfully completed the password backup (e.g they have set up - * their password Challenge Key). - * Note: Assumes there's currently a LOGGED_IN user store in this repository. - */ - public void setHasPassword() { - final StoredUserJson value = Preconditions.checkNotNull(userPreference.get()); - - value.hasPassword = true; - userPreference.set(value); - } - - /** - * Record that this user has successfully completed the Recovery Code backup (e.g they have set - * up their Recovery Code Challenge Key). - * Note: Assumes there's currently a LOGGED_IN user store in this repository. - */ - public void setHasRecoveryCode() { - final StoredUserJson value = Preconditions.checkNotNull(userPreference.get()); - - value.hasRecoveryCode = true; - userPreference.set(value); - } - - @JsonInclude(JsonInclude.Include.NON_NULL) - @JsonIgnoreProperties(ignoreUnknown = true) - public static class StoredUserJson { - // WAIT - // WARNING - // CAREFUL - // READ THIS, I MEAN IT: - - // We forgot to exclude this class from Proguard rules. This means that the order of - // declaration of this attributes is important -- until we remove this class from proguard - // 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 - public StoredEkVerificationCodes emergencyKitCodes = new StoredEkVerificationCodes(); - - @NonNull - public List ekVersions = new LinkedList<>(); - - static StoredUserJson fromUser(User user) { - return new StoredUserJson( - user.hid, - user.email.orElse(null), - user.createdAt.map(SerializationUtils::serializeDate).orElse(null), - user.phoneNumber.map(PhoneNumber::toE164String).orElse(null), - user.phoneNumber.map(UserPhoneNumber::isVerified).orElse(false), - user.profile.map(UserProfile::getFirstName).orElse(null), - user.profile.map(UserProfile::getLastName).orElse(null), - user.profile.map(UserProfile::getPictureUrl).orElse(null), - user.isEmailVerified, - user.hasRecoveryCode, - user.hasPassword, - user.hasP2PEnabled, - user.hasExportedKeys, - SerializationUtils.serializeCurrencyUnit(user.unsafeGetPrimaryCurrency()), - user.emergencyKit - .map(ek -> ek.getLastExportedAt()) - .map(SerializationUtils::serializeDate) - .orElse(null), - user.emergencyKit.map(ek -> ek.getVersion()).orElse(null), - user.emergencyKit.map(ek -> ek.getExportMethod()).orElse(null), - user.emergencyKitVerificationCodes, - new ArrayList<>(user.emergencyKitVersions) - ); - } - - /** - * Json constructor. - */ - 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 - ) { - - this.hid = hid; - this.email = email; - this.createdAt = createdAt; - this.phoneNumber = phoneNumber; - this.isPhoneNumberVerified = isPhoneNumberVerified; - this.firstName = firstName; - this.lastName = lastName; - this.profilePictureUrl = profilePictureUrl; - this.isEmailVerified = isEmailVerified; - this.hasRecoveryCode = hasRecoveryCode; - this.hasPassword = hasPassword; - this.hasP2PEnabled = hasP2PEnabled; - this.hasExportedKeys = hasExportedKeys; - this.currency = currency; - this.emergencyKitLastExportedAt = emergencyKitLastExportedAt; - this.emergencyKitVersion = emergencyKitVersion; - this.emergencyKitExportMethod = emergencyKitExportMethod != null - ? emergencyKitExportMethod.name() - : null; - this.emergencyKitCodes = ekVerificationCodes; - this.ekVersions = ekVersions; - } - - User toUser() { - return new User( - hid, - Optional.ofNullable(email), - isEmailVerified, - - phoneNumber != null - ? Optional.of(new UserPhoneNumber(phoneNumber, isPhoneNumberVerified)) - : Optional.empty(), - - firstName != null - ? Optional.of(new UserProfile(firstName, lastName, profilePictureUrl)) - : Optional.empty(), - - loadCurrencyFromStorage(), - - hasRecoveryCode, - hasPassword, - hasP2PEnabled, - hasExportedKeys, - - emergencyKitLastExportedAt != null - ? Optional.of(buildEK()) - : Optional.empty(), - - emergencyKitCodes, - new TreeSet<>(ekVersions), - - Optional.ofNullable(createdAt).map(SerializationUtils::deserializeDate) - ); - } - - 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; - ekVersions.add((int) Libwallet.EKVersionDescriptors); - // We can't know which method was used for prior exports so... - emergencyKitExportMethod = null; - } - } - - EmergencyKit buildEK() { - Preconditions.checkNotNull(emergencyKitLastExportedAt); - return new EmergencyKit( - Objects.requireNonNull( - SerializationUtils.deserializeDate(emergencyKitLastExportedAt) - ), - emergencyKitVersion, - emergencyKitExportMethod != null - ? EmergencyKitExport.Method.valueOf(emergencyKitExportMethod) - : null - ); - } - - void setProfileFrom(@Nullable UserProfile newValue) { - if (newValue != null) { - firstName = newValue.getFirstName(); - lastName = newValue.getLastName(); - profilePictureUrl = newValue.getPictureUrl(); - } else { - firstName = null; - lastName = null; - profilePictureUrl = null; - } - } - - void setPhoneNumberFrom(@Nullable UserPhoneNumber newValue) { - if (newValue != null) { - phoneNumber = newValue.toE164String(); - isPhoneNumberVerified = newValue.isVerified(); - } else { - phoneNumber = null; - isPhoneNumberVerified = false; - } - } - } - - private static class UserPreferenceDebugAdapter extends JsonPreferenceAdapter { - - public UserPreferenceDebugAdapter() { - super(StoredUserJson.class); - } - - @Override - public StoredUserJson get(@NonNull String key, @NonNull SharedPreferences preferences) { - return super.get(key, preferences); - } - - @Override - 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/preferences/UserRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/UserRepository.kt new file mode 100644 index 00000000..11baf9e0 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/UserRepository.kt @@ -0,0 +1,633 @@ +package io.muun.apollo.data.preferences + +import android.content.Context +import android.content.SharedPreferences +import android.net.Uri +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonInclude +import io.muun.apollo.data.preferences.UserRepository.StoredUserJson +import io.muun.apollo.data.preferences.adapter.JsonPreferenceAdapter +import io.muun.apollo.data.preferences.rx.Preference +import io.muun.apollo.data.preferences.stored.StoredEkVerificationCodes +import io.muun.apollo.data.serialization.SerializationUtils +import io.muun.apollo.domain.model.BitcoinUnit +import io.muun.apollo.domain.model.EmergencyKitExport +import io.muun.apollo.domain.model.PermissionState +import io.muun.apollo.domain.model.user.EmergencyKit +import io.muun.apollo.domain.model.user.User +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.utils.Preconditions +import libwallet.Libwallet +import rx.Observable +import java.util.LinkedList +import java.util.TreeSet +import javax.inject.Inject +import javax.inject.Singleton +import javax.money.CurrencyUnit + +@Singleton +class UserRepository @Inject constructor( + context: Context, + repositoryRegistry: RepositoryRegistry, +) : BaseRepository(context, repositoryRegistry) { + + companion object { + private const val KEY_USER = "user" + private const val KEY_LAST_COPIED_ADDRESS = "key_last_copied_address" + private const val PENDING_PROFILE_PICTURE_URI_KEY = "pending_profile_picture_uri_key" + private const val PASSWORD_CHANGE_AUTHORIZED_UUID = "password_change_authorized_uuid" + private const val CONTACTS_PERMISSION_STATE_KEY = "contacts_permission_state_key" + private const val INITIAL_SYNC_COMPLETED_KEY = "initial_sync_completed_key" + private const val RC_SETUP_IN_PROCESS = "rc_setup_in_process" + private const val DISPLAY_SATS = "use_sats_as_currency" + private const val PENDING_EMAIL_LINK = "pending_email_link" + private const val BALANCE_HIDDEN_KEY = "balance_hidden_key" + private const val TAPROOT_CELEBRATION_PENDING = "taproot_celebration_pending" + private const val SEEN_WELCOME_TO_MUUN_DIALOG_KEY = "seen_welcome_to_muun_dialog_key" + } + + // Preferences: + private val userPreference: Preference + get() = rxSharedPreferences.getObject(KEY_USER, UserPreferenceDebugAdapter()) + private val lastCopiedAddress: Preference + get() = rxSharedPreferences.getString(KEY_LAST_COPIED_ADDRESS) + private val pendingProfilePictureUriPreference: Preference + get() = rxSharedPreferences.getString(PENDING_PROFILE_PICTURE_URI_KEY) + private val passwordChangeAuthorizedUuidPreference: Preference + get() = rxSharedPreferences.getString(PASSWORD_CHANGE_AUTHORIZED_UUID) + private val conctactsPermissionStatePreference: Preference + get() = rxSharedPreferences.getEnum( + CONTACTS_PERMISSION_STATE_KEY, + PermissionState.DENIED, + PermissionState::class.java + ) + private val initialSyncCompletedPreference: Preference + get() = rxSharedPreferences.getBoolean(INITIAL_SYNC_COMPLETED_KEY, false) + private val recoveryCodeSetupInProcessPreference: Preference + get() = rxSharedPreferences.getBoolean(RC_SETUP_IN_PROCESS) + private val displaySatsPreference: Preference + get() = rxSharedPreferences.getEnum( + DISPLAY_SATS, + BitcoinUnit.BTC, + BitcoinUnit::class.java + ) + private val pendingEmailLinkPreference: Preference + get() = rxSharedPreferences.getString(PENDING_EMAIL_LINK, "default") + private val balanceHiddenPreference: Preference + get() = rxSharedPreferences.getBoolean(BALANCE_HIDDEN_KEY, false) + + // Horrible I know, but only temporary until taproot activation date. Afterwards this goes away. + private val taprootCelebrationPending: Preference + get() = rxSharedPreferences.getBoolean(TAPROOT_CELEBRATION_PENDING, false) + private val seenWelcomeToMuunDialogPreference: Preference + get() = rxSharedPreferences.getBoolean(SEEN_WELCOME_TO_MUUN_DIALOG_KEY, false) + + override val fileName get() = "user" + + /** + * Stores the user. + */ + @Synchronized + fun store(user: User) { + userPreference.set(StoredUserJson.fromUser(user)) + } + + /** + * Fetches the user, throws NoSuchElementException if not present. + */ + fun fetch(): Observable { + return fetchOptional().map { obj: Optional -> obj.get() } + } + + /** + * Fetches the user, throws NPE if not present. + */ + fun fetchOptional(): Observable> { + return userPreference.asObservable() + .map { storedUser: StoredUserJson? -> + if (storedUser != null) { + return@map Optional.of(storedUser.toUser()) + } else { + return@map Optional.empty() + } + } + } + + /** + * Get the user for the current session. Returns an empty Optional if there's no user currently + * LOGGED_IN. + */ + fun fetchOneOptional(): Optional { + return fetchOptional().toBlocking().first() + } + + /** + * Execute the migration that ends the multi-preference hell. + */ + fun migrateCthulhuToJsonPreference() { + val hid = sharedPreferences.getLong("hid", -1L) + if (hid == -1L) { + return // If no logged in user, then avoid setting any preferences. + } + val value = StoredUserJson( + hid, + sharedPreferences.getString("email", null), + sharedPreferences.getString("created_at", null), + sharedPreferences.getString("phone_number", null), + sharedPreferences.getBoolean("phone_number_verified", false), + sharedPreferences.getString("first_name", null), + sharedPreferences.getString("last_name", null), + sharedPreferences.getString("profile_picture_url", null), + sharedPreferences.getBoolean("email_verified_key", false), + sharedPreferences.getBoolean("has_recovery_code", false), + true, // all users had passwords before this feature + sharedPreferences.getBoolean("has_p2p_enabled", false), + false, // non-existent at migration time. This is a good default + sharedPreferences.getString("primary_currency_key", "USD")!!, + null, // non-existent at migration time + null, // non-existent at migration time + null, // non-existent at migration time + StoredEkVerificationCodes(), // non-existent at migration time + LinkedList() + ) + userPreference.set(value) + } + + /** + * Get the user for the current session. Throws an Exception (NoSuchElementException) if there's + * no user currently LOGGED_IN. + */ + fun fetchOne(): User { + return fetch().toBlocking().first() + } + + /** + * Stores a user Profile. + */ + @Synchronized + fun storeProfile(profile: UserProfile?) { + val value = Preconditions.checkNotNull(userPreference.get()) + value.setProfileFrom(profile) + userPreference.set(value) + } + + /** + * Stores a user PhoneNumber. + */ + @Synchronized + fun storePhoneNumber(phoneNumber: UserPhoneNumber?) { + val value = Preconditions.checkNotNull(userPreference.get()) + value.setPhoneNumberFrom(phoneNumber) + userPreference.set(value) + } + + var pendingProfilePictureUri: Uri? + /** + * Returns the Uri of a profile picture that needs to be uploaded. + */ + get() { + val uriString = pendingProfilePictureUriPreference.get() + return uriString?.let { Uri.parse(uriString) } + } + /** + * Enqueues a profile picture to be uploaded in the future. + */ + set(uri) { + if (uri == null) { + pendingProfilePictureUriPreference.delete() + return + } + pendingProfilePictureUriPreference.set(uri.toString()) + } + + /** + * Note: no longer necessary a bitcoin address, can be a Ln invoice. + */ + var lastCopiedContentFromReceive: String? + get() = lastCopiedAddress.get() + set(content) { + lastCopiedAddress.set(content) + } + + /** + * Store the fact that the user has verified their email. + */ + fun storeEmailVerified() { + val value = Preconditions.checkNotNull(userPreference.get()) + value.isEmailVerified = true + userPreference.set(value) + } + + /** + * Wait for the authorized email notification. + */ + fun awaitForAuthorizedPasswordChange(): Observable { + return passwordChangeAuthorizedUuidPreference.asObservable() + .filter { uuid: String? -> uuid != null && uuid.isNotEmpty() } + } + + /** + * Authorize pending password change process. + */ + fun authorizePasswordChange(uuid: String) { + passwordChangeAuthorizedUuidPreference.set(uuid) + } + + /** + * Save contacts permission state. + */ + fun storeContactsPermissionState(state: PermissionState) { + conctactsPermissionStatePreference.set(state) + } + + val contactsPermissionState: PermissionState + get() = conctactsPermissionStatePreference.get()!! + + /** + * Get an Observable to observe changes to the contacts permission preference. + */ + fun watchContactsPermissionState(): Observable { + return conctactsPermissionStatePreference.asObservable() + } + + /** + * Save flag to signal that user has completed initial sync, and thus is probably LOGGED_IN now. + */ + fun storeInitialSyncCompleted() { + initialSyncCompletedPreference.set(true) + } + + val isInitialSyncCompleted: Boolean + get() = initialSyncCompletedPreference.get()!! + + /** + * Save flag to signal that RecoveryCode setup process, though started, has not been completed. + */ + fun setRecoveryCodeSetupInProcess(isInProcess: Boolean) { + recoveryCodeSetupInProcessPreference.set(isInProcess) + } + /** + * Get the bitcoin unit preference. + */ + /** + * Save a new bitcoin unit preference value. + */ + var bitcoinUnit: BitcoinUnit + get() = displaySatsPreference.get()!! + set(value) { + displaySatsPreference.set(value) + } + + /** + * Get an Observable to observe changes to the bitcoin unit preference. + */ + fun watchBitcoinUnit(): Observable { + return displaySatsPreference.asObservable() + } + + /** + * Save email action link that is being await for confirmation (via email link click). This + * is used to distinguish between the different email links actions that we have. + */ + var pendingEmailLink: String + get() = pendingEmailLinkPreference.get()!! + set(emailLink) { + pendingEmailLinkPreference.set(emailLink) + } + + /** + * Save user's choice to see her wallet balance hidden in our home screen. + */ + fun setBalanceHidden(hidden: Boolean) { + balanceHiddenPreference.set(hidden) + } + + /** + * Get an Observable to observe changes to the balance hidden preference. + */ + fun watchBalanceHidden(): Observable { + return balanceHiddenPreference.asObservable() + } + + /** + * Save whether the taproot celebration is in order. + */ + fun setPendingTaprootCelebration(isCelebrationPending: Boolean) { + taprootCelebrationPending.set(isCelebrationPending) + } + + /** + * Get an Observable to observe changes to the Taproot Celebration pending preference. + */ + fun watchPendingTaprootCelebration(): Observable { + return taprootCelebrationPending.asObservable() + } + + /** + * Save a flag signalling that the "Welcome to Muun" dialog has been shown/seen. + */ + fun setWelcomeToMuunDialogSeen() { + seenWelcomeToMuunDialogPreference.set(true) + } + + /** + * Get whether the user has seen the "Welcome to Muun" dialog or not. + */ + val welcomeToMuunDialogSeen: Boolean + get() = seenWelcomeToMuunDialogPreference.get()!! + + /** + * Migration to init emergency kit version. + */ + fun initEmergencyKitVersion() { + val storedUser = userPreference.get() + storedUser?.initEmergencyKitVersion() + userPreference.set(storedUser) + } + + /** + * Save latest emergency kit verification code show to user. This can either be a verification + * code that is later successfully used, or not. + */ + fun storeEmergencyKitVerificationCode(verificationCode: String) { + val value = Preconditions.checkNotNull(userPreference.get()) + value.emergencyKitCodes.addNewest(verificationCode) + userPreference.set(value) + } + + /** + * Record that this user has successfully completed the password backup (e.g they have set up + * their password Challenge Key). + * Note: Assumes there's currently a LOGGED_IN user store in this repository. + */ + fun setHasPassword() { + val value = Preconditions.checkNotNull(userPreference.get()) + value.hasPassword = true + userPreference.set(value) + } + + /** + * Record that this user has successfully completed the Recovery Code backup (e.g they have set + * up their Recovery Code Challenge Key). + * Note: Assumes there's currently a LOGGED_IN user store in this repository. + */ + fun setHasRecoveryCode() { + val value = Preconditions.checkNotNull(userPreference.get()) + value.hasRecoveryCode = true + userPreference.set(value) + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties(ignoreUnknown = true) + class StoredUserJson { + // WAIT + // WARNING + // CAREFUL + // READ THIS, I MEAN IT: + // We forgot to exclude this class from Proguard rules. This means that the order of + // declaration of this attributes is important -- until we remove this class from proguard + // and migrate the preference to a non-minified JSON this class is APPEND-ONLY. + @JvmField + var hid: Long = 0 + + @JvmField + var email: String? = null + + @JvmField + var createdAt: String? = null + + @JvmField + var phoneNumber: String? = null + + @JvmField + var isPhoneNumberVerified = false + + @JvmField + var firstName: String? = null + + @JvmField + var lastName: String? = null + + @JvmField + var profilePictureUrl: String? = null + + @JvmField + var isEmailVerified = false + + @JvmField + var hasRecoveryCode = false + + @JvmField + var hasPassword = false + + @JvmField + var hasP2PEnabled = false + + @JvmField + var hasExportedKeys = false + + @JvmField + var currency: String? = null + + @JvmField + var emergencyKitLastExportedAt: String? = null + + @JvmField + var emergencyKitVersion: Int? = null + + @JvmField + var emergencyKitExportMethod: String? = null + + // Not backed by Houston, cached locally + @JvmField + var emergencyKitCodes = StoredEkVerificationCodes() + + @JvmField + var ekVersions: MutableList = LinkedList() + + /** + * Json constructor. + */ + @Suppress("unused") + constructor() + + /** + * Manual constructor. + */ + constructor( + hid: Long, + email: String?, + createdAt: String?, + phoneNumber: String?, + isPhoneNumberVerified: Boolean, + firstName: String?, + lastName: String?, + profilePictureUrl: String?, + isEmailVerified: Boolean, + hasRecoveryCode: Boolean, + hasPassword: Boolean, + hasP2PEnabled: Boolean, + hasExportedKeys: Boolean, + currency: String, + emergencyKitLastExportedAt: String?, + emergencyKitVersion: Int?, + emergencyKitExportMethod: EmergencyKitExport.Method?, + ekVerificationCodes: StoredEkVerificationCodes, + ekVersions: MutableList, + ) { + this.hid = hid + this.email = email + this.createdAt = createdAt + this.phoneNumber = phoneNumber + this.isPhoneNumberVerified = isPhoneNumberVerified + this.firstName = firstName + this.lastName = lastName + this.profilePictureUrl = profilePictureUrl + this.isEmailVerified = isEmailVerified + this.hasRecoveryCode = hasRecoveryCode + this.hasPassword = hasPassword + this.hasP2PEnabled = hasP2PEnabled + this.hasExportedKeys = hasExportedKeys + this.currency = currency + this.emergencyKitLastExportedAt = emergencyKitLastExportedAt + this.emergencyKitVersion = emergencyKitVersion + this.emergencyKitExportMethod = emergencyKitExportMethod?.name + emergencyKitCodes = ekVerificationCodes + this.ekVersions = ekVersions + } + + fun toUser(): User { + return User( + hid, + Optional.ofNullable(email), + isEmailVerified, + if (phoneNumber != null) { + Optional.of(UserPhoneNumber(phoneNumber, isPhoneNumberVerified)) + } else { + Optional.empty() + }, + if (firstName != null) { + Optional.of(UserProfile(firstName, lastName, profilePictureUrl)) + } else { + Optional.empty() + }, + loadCurrencyFromStorage(), + hasRecoveryCode, + hasPassword, + hasP2PEnabled, + hasExportedKeys, + if (emergencyKitLastExportedAt != null) Optional.of(buildEK()) else Optional.empty(), + emergencyKitCodes, + TreeSet(ekVersions), + Optional.ofNullable(createdAt).map(SerializationUtils::deserializeDate) + ) + } + + private fun loadCurrencyFromStorage(): CurrencyUnit { + return try { + val currencyCode = if (currency != null) currency!! else "USD" + SerializationUtils.deserializeCurrencyUnit(currencyCode) + } catch (e: Exception) { + // This can happen for example if user primary currency is no longer supported + // after an app or OS update. + Currency.getUnit(Currency.DEFAULT.code).get() + } + } + + fun initEmergencyKitVersion() { + if (emergencyKitLastExportedAt != null) { + emergencyKitVersion = Libwallet.EKVersionDescriptors.toInt() + ekVersions.add(Libwallet.EKVersionDescriptors.toInt()) + // We can't know which method was used for prior exports so... + emergencyKitExportMethod = null + } + } + + private fun buildEK(): EmergencyKit { + Preconditions.checkNotNull(emergencyKitLastExportedAt) + return EmergencyKit( + requireNotNull(SerializationUtils.deserializeDate(emergencyKitLastExportedAt)), + emergencyKitVersion!!, + if (emergencyKitExportMethod != null) { + EmergencyKitExport.Method.valueOf(emergencyKitExportMethod!!) + } else { + null + } + ) + } + + fun setProfileFrom(newValue: UserProfile?) { + if (newValue != null) { + firstName = newValue.firstName + lastName = newValue.lastName + profilePictureUrl = newValue.pictureUrl + } else { + firstName = null + lastName = null + profilePictureUrl = null + } + } + + fun setPhoneNumberFrom(newValue: UserPhoneNumber?) { + if (newValue != null) { + phoneNumber = newValue.toE164String() + isPhoneNumberVerified = newValue.isVerified + } else { + phoneNumber = null + isPhoneNumberVerified = false + } + } + + companion object { + fun fromUser(user: User): StoredUserJson { + return StoredUserJson( + user.hid, + user.email.orElse(null), + user.createdAt.map(SerializationUtils::serializeDate).orElse(null), + user.phoneNumber.map(UserPhoneNumber::toE164String).orElse(null), + user.phoneNumber.map { obj: UserPhoneNumber -> obj.isVerified }.orElse(false), + user.profile.map { obj: UserProfile -> obj.firstName }.orElse(null), + user.profile.map { obj: UserProfile -> obj.lastName }.orElse(null), + user.profile.map { obj: UserProfile -> obj.pictureUrl }.orElse(null), + user.isEmailVerified, + user.hasRecoveryCode, + user.hasPassword, + user.hasP2PEnabled, + user.hasExportedKeys, + SerializationUtils.serializeCurrencyUnit(user.unsafeGetPrimaryCurrency()), + user.emergencyKit + .map { ek -> ek.lastExportedAt } + .map(SerializationUtils::serializeDate) + .orElse(null), + user.emergencyKit.map { ek -> ek.version }.orElse(null), + user.emergencyKit.map { ek -> ek.exportMethod }.orElse(null), + user.emergencyKitVerificationCodes, + ArrayList(user.emergencyKitVersions) + ) + } + } + } + + /** + * Useful for adding debugging prints. + */ + private class UserPreferenceDebugAdapter : JsonPreferenceAdapter( + StoredUserJson::class.java + ) { + override fun get(key: String, preferences: SharedPreferences): StoredUserJson { + return super.get(key, preferences)!! + } + + @Suppress("RedundantOverride") + override fun set( + key: String, + value: StoredUserJson, + editor: SharedPreferences.Editor, + ) { + super.set(key, value, editor) + } + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/migration/PreferencesMigrationManager.java b/android/apollo/src/main/java/io/muun/apollo/data/preferences/migration/PreferencesMigrationManager.java index c56b91b8..740b4e0d 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/migration/PreferencesMigrationManager.java +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/migration/PreferencesMigrationManager.java @@ -11,7 +11,8 @@ import io.muun.apollo.data.preferences.stored.StoredEkVerificationCodes; import io.muun.apollo.data.serialization.SerializationUtils; import io.muun.apollo.domain.SignupDraftManager; -import io.muun.apollo.domain.action.LogoutActions; +import io.muun.apollo.domain.action.session.ClearRepositoriesAction; +import io.muun.apollo.domain.action.session.LegacyLogoutUserForMigrationAction; import io.muun.apollo.domain.model.FeeWindow; import io.muun.common.crypto.ChallengeType; import io.muun.common.model.SessionStatus; @@ -34,7 +35,8 @@ public class PreferencesMigrationManager { private final Context context; private final SignupDraftManager signupDraftManager; - private final LogoutActions logoutActions; + private final LegacyLogoutUserForMigrationAction legacyLogout; + private final ClearRepositoriesAction clearRepositories; private final SchemaVersionRepository schemaVersionRepository; private final AuthRepository authRepository; @@ -117,7 +119,8 @@ public class PreferencesMigrationManager { public PreferencesMigrationManager( Context context, SignupDraftManager signupDraftManager, - LogoutActions logoutActions, + LegacyLogoutUserForMigrationAction legacyLogout, + ClearRepositoriesAction clearRepositories, SchemaVersionRepository schemaVersionRepository, AuthRepository authRepository, UserRepository userRepository, @@ -130,7 +133,8 @@ public PreferencesMigrationManager( this.context = context; this.signupDraftManager = signupDraftManager; - this.logoutActions = logoutActions; + this.legacyLogout = legacyLogout; + this.clearRepositories = clearRepositories; this.schemaVersionRepository = schemaVersionRepository; this.authRepository = authRepository; @@ -162,11 +166,11 @@ public void migrate() { } private void clearAllRepositories() { - logoutActions.clearAllRepositories(); + clearRepositories.clearAll(); } private void logout() { - logoutActions.destroyRecoverableWallet(); + legacyLogout.run(); } /** diff --git a/android/apollo/src/main/java/io/muun/apollo/data/preferences/permission/NotificationPermissionStateRepository.kt b/android/apollo/src/main/java/io/muun/apollo/data/preferences/permission/NotificationPermissionStateRepository.kt index e52cbb58..6d91d552 100644 --- a/android/apollo/src/main/java/io/muun/apollo/data/preferences/permission/NotificationPermissionStateRepository.kt +++ b/android/apollo/src/main/java/io/muun/apollo/data/preferences/permission/NotificationPermissionStateRepository.kt @@ -22,8 +22,7 @@ class NotificationPermissionStateRepository @Inject constructor( PermissionState::class.java ) - override fun getFileName() = - "notification_permission_state" + override val fileName get() = "notification_permission_state" fun store(permissionState: PermissionState) { permissionStatePref.set(permissionState) 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 5b8d6517..e7f95b16 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 @@ -5,70 +5,66 @@ import io.muun.apollo.data.external.NotificationService; import io.muun.apollo.data.fs.LibwalletDataDirectory; import io.muun.apollo.data.os.secure_storage.SecureStorageProvider; -import io.muun.apollo.data.preferences.BaseRepository; -import io.muun.apollo.data.preferences.FirebaseInstallationIdRepository; -import io.muun.apollo.data.preferences.RepositoryRegistry; import io.muun.apollo.domain.ApplicationLockManager; import io.muun.apollo.domain.SignupDraftManager; import io.muun.apollo.domain.action.base.AsyncActionStore; +import io.muun.apollo.domain.action.session.ClearRepositoriesAction; import io.muun.apollo.domain.errors.UnrecoverableUserLogoutError; import io.muun.apollo.domain.model.SignupDraft; import io.muun.apollo.domain.selector.LogoutOptionsSelector; import io.muun.apollo.domain.selector.LogoutOptionsSelector.LogoutOptions; import io.muun.common.utils.Preconditions; -import android.content.Context; import androidx.annotation.VisibleForTesting; import timber.log.Timber; -import java.util.Arrays; -import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; @Singleton public class LogoutActions { - // Presentation - private final Context context; - // Domain private final ContactActions contactActions; // TODO should be dismembered (as this action bag) + private final AsyncActionStore asyncActionStore; + private final DaoManager daoManager; + private final ApplicationLockManager lockManager; + private final LogoutOptionsSelector logoutOptionsSel; + private final SignupDraftManager signupDraftManager; + private final ClearRepositoriesAction clearRepositories; + // Data private final TaskScheduler taskScheduler; + private final SecureStorageProvider secureStorageProvider; + private final NotificationService notificationService; - private final RepositoryRegistry repositoryRegistry; - private final FirebaseInstallationIdRepository firebaseInstallationIdRepository; - private final LibwalletDataDirectory libwalletDataDirectory; - private final List thirdPartyPreferencesToClear; + private final LibwalletDataDirectory libwalletDataDirectory; /** * Constructor. */ @Inject - public LogoutActions(Context context, - ContactActions contactActions, - AsyncActionStore asyncActionStore, - DaoManager daoManager, - ApplicationLockManager lockManager, - LogoutOptionsSelector logoutOptionsSel, - SignupDraftManager signupDraftManager, - TaskScheduler taskScheduler, - SecureStorageProvider secureStorageProvider, - NotificationService notificationService, - RepositoryRegistry repositoryRegistry, - FirebaseInstallationIdRepository firebaseInstallationIdRepository, - LibwalletDataDirectory libwalletDataDirectory) { - - this.context = context; + public LogoutActions( + ContactActions contactActions, + AsyncActionStore asyncActionStore, + DaoManager daoManager, + ApplicationLockManager lockManager, + LogoutOptionsSelector logoutOptionsSel, + SignupDraftManager signupDraftManager, + ClearRepositoriesAction clearRepositories, + TaskScheduler taskScheduler, + SecureStorageProvider secureStorageProvider, + NotificationService notificationService, + LibwalletDataDirectory libwalletDataDirectory + ) { this.contactActions = contactActions; this.asyncActionStore = asyncActionStore; @@ -76,15 +72,12 @@ public LogoutActions(Context context, this.lockManager = lockManager; this.logoutOptionsSel = logoutOptionsSel; this.signupDraftManager = signupDraftManager; + this.clearRepositories = clearRepositories; this.taskScheduler = taskScheduler; this.secureStorageProvider = secureStorageProvider; this.notificationService = notificationService; - this.repositoryRegistry = repositoryRegistry; - this.firebaseInstallationIdRepository = firebaseInstallationIdRepository; this.libwalletDataDirectory = libwalletDataDirectory; - - this.thirdPartyPreferencesToClear = createThirdPartyPreferencesList(); } /** @@ -163,7 +156,7 @@ private void destroyWallet() { asyncActionStore.resetAllExceptLogout(); secureStorageProvider.wipe(); - clearRepositoriesForLogout(); + clearRepositories.clearForLogout(); contactActions.stopWatchingContacts(); daoManager.delete(); @@ -172,62 +165,4 @@ private void destroyWallet() { notificationService.cancelAllNotifications(); libwalletDataDirectory.reset(); } - - /** - * Destroy all data in non-encrypted repositories. - * Warning: this method is supposed to be used solely in PreferencesMigrationManager, since we - * shouldn't be needing any "full wipe preference migration" anymore, this method is left just - * for the sake of completeness. - */ - public void clearAllRepositories() { - clearRepositoriesForLogout(); - clearRepository(firebaseInstallationIdRepository); - } - - /** - * Destroy all data in non-encrypted repositories that should be cleared upon logout. We avoid - * clearing some repositories on logout (e.g FcmTokenRepository). - */ - private void clearRepositoriesForLogout() { - for (BaseRepository repository : repositoryRegistry.repositoriesToClearOnLogout()) { - clearRepository(repository); - } - - for (String fileName : thirdPartyPreferencesToClear) { - context.getSharedPreferences(fileName, Context.MODE_PRIVATE).edit().clear().apply(); - } - } - - private void clearRepository(BaseRepository repository) { - try { - repository.clear(); - } catch (Exception e) { - Timber.e(e); - } - } - - private List createThirdPartyPreferencesList() { - // NOTE: there is no reliable way of listing all SharedPreferences. The XML files are - // *usually* located in a known directory, but some vendors change this. We cannot control - // where the directory lies in a particular device, but we can know and control the actual - // preferences created by Apollo's dependencies. - - // The following list was created by logging into Apollo, and listing the XML files created - // in the data/shared_prefs folder of the application. - - // ON-RELEASE verify that the list matches the actual files added by our dependencies. This - // won't crash if files do not exist, but we could miss some. - return Arrays.asList( - "TwitterAdvertisingInfoPreferences", - "WebViewChromiumPrefs", - "com.crashlytics.prefs", - "com.crashlytics.sdk.android:answers:settings", - "com.crashlytics.sdk.android.crashlytics-core" - + ":com.crashlytics.android.core.CrashlyticsCore", - - "com.google.android.gms.appid", - "com.google.android.gms.measurement.prefs", - "io.fabric.sdk.android:fabric:io.fabric.sdk.android.Onboarding" - ); - } } diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/NotificationActions.java b/android/apollo/src/main/java/io/muun/apollo/domain/action/NotificationActions.java deleted file mode 100644 index 166af778..00000000 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/NotificationActions.java +++ /dev/null @@ -1,275 +0,0 @@ -package io.muun.apollo.domain.action; - - -import io.muun.apollo.data.external.AppStandbyBucketProvider; -import io.muun.apollo.data.net.HoustonClient; -import io.muun.apollo.data.preferences.NotificationRepository; -import io.muun.apollo.domain.NotificationProcessor; -import io.muun.apollo.domain.action.base.AsyncAction0; -import io.muun.apollo.domain.action.base.AsyncActionStore; -import io.muun.apollo.domain.errors.BugDetected; -import io.muun.apollo.domain.errors.notifications.NotificationProcessingError; -import io.muun.apollo.domain.model.NotificationReport; -import io.muun.common.api.beam.notification.NotificationJson; -import io.muun.common.api.messages.FulfillIncomingSwapMessage; - -import android.os.Build; -import androidx.annotation.Nullable; -import rx.BackpressureOverflow; -import rx.Completable; -import rx.Observable; -import rx.Observable.Transformer; -import rx.Scheduler; -import rx.Single; -import rx.Subscription; -import rx.functions.Func1; -import rx.subjects.PublishSubject; -import timber.log.Timber; - -import java.util.List; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -import static io.muun.apollo.domain.action.NotificationActions_ExtensionsKt.asString; -import static io.muun.apollo.domain.action.NotificationActions_ExtensionsKt.mapIds; - -@Singleton // important -public class NotificationActions implements NotificationPoller { - - // If too many NotificationReports accumulate, we'll save them into a buffer with the following - // capacity. Dropping reports is not ideal, since Apollo will need to query Houston for missing - // notifications, but it's not serious either. This can happen after regaining connectivity, - // or after Muun services recover from a temporary failure. - private static final int MAX_PENDING_REPORTS_BEFORE_DROP_OLDEST = 32; - - private final NotificationRepository notificationRepository; - private final HoustonClient houstonClient; - - private final NotificationProcessor notificationProcessor; - - // NOTE: these 2 properties make this class stateful (and thus @Singleton). After moving the - // subject to a centralized SubjectStore (could be refactored out of AsyncActionStore) this - // action bag would be stateless. - private final PublishSubject reportQueue; - private Subscription reportQueueSub; - private final Scheduler scheduler; - - public final AsyncAction0 pullNotificationsAction; - - private final AppStandbyBucketProvider appStandbyBucketProvider; - - /** - * Constructor. - */ - @Inject - public NotificationActions(NotificationRepository notificationRepository, - HoustonClient houstonClient, - AsyncActionStore asyncActionStore, - NotificationProcessor notificationProcessor, - AppStandbyBucketProvider appStandbyBucketProvider, - @Named("notificationScheduler") Scheduler notificationScheduler) { - - this.notificationRepository = notificationRepository; - this.houstonClient = houstonClient; - this.notificationProcessor = notificationProcessor; - this.appStandbyBucketProvider = appStandbyBucketProvider; - this.scheduler = notificationScheduler; - reportQueue = PublishSubject.create(); - - pullNotificationsAction = asyncActionStore - .get("notifications/pull", this::pullNotifications); - } - - /** - * Invoke when a new NotificationReportJson is received. - */ - public synchronized void onNotificationReport(NotificationReport report) { - if (reportQueueSub == null || reportQueueSub.isUnsubscribed()) { - reportQueueSub = startProcessingQueue(reportQueue); - } - - reportQueue.onNext(report); - } - - /** - * Pull the latest notifications from Houston. - */ - public Observable pullNotifications() { - - return Observable.defer(() -> { - - final long lastProcessedId = notificationRepository.getLastProcessedId(); - Timber.i("[Notifications] Pulling... LastProcessedId: " + lastProcessedId); - - return houstonClient.fetchNotificationReportAfter(lastProcessedId) - .map(report -> { - onNotificationReport(report); - return null; - }); - }); - } - - /** - * Process a notification report. Do not call directly. - */ - private void processReport(final NotificationReport report) { - - NotificationReport reportToProcess = report; - - // If we have a gap between the report and what we last processed, ignore the report - // and start processing from the start of the gap. We'll refetch a bit of data, but it - // makes for simple code. - if (reportToProcess.getPreviousId() > notificationRepository.getLastProcessedId()) { - reportToProcess = fetchNotificationReport(notificationRepository.getLastProcessedId()) - .toBlocking() - .value(); - } - - processNotificationList(reportToProcess.getPreview()).await(); - - // We rely on the fact that we mark notifications that failed to process as processed - // anyway. Without that the following loop might be infinite. - while (notificationRepository.getLastProcessedId() < reportToProcess.getMaximumId()) { - - reportToProcess = fetchNotificationReport(notificationRepository.getLastProcessedId()) - .toBlocking() - .value(); - - processNotificationList(reportToProcess.getPreview()).await(); - - } - } - - /** - * Process a list of notifications. Do not call directly. - */ - private Completable processNotificationList(final List notifications) { - - return Completable.defer(() -> { - final long lastIdBefore = notificationRepository.getLastProcessedId(); - - Timber.i("[Notifications] Processing List: " + asString(mapIds(notifications))); - - return Observable.from(notifications) - .compose(forEach(this::processNotification)) - .lastOrDefault(null) - .flatMap(ignored -> { - final long lastIdAfter = notificationRepository.getLastProcessedId(); - - if (lastIdAfter > lastIdBefore) { - - return houstonClient.confirmNotificationsDeliveryUntil( - lastIdAfter, - Build.MODEL, - String.valueOf(Build.VERSION.SDK_INT), - appStandbyBucketProvider.current().toString() - ); - - } else { - return Observable.just(null); - } - }) - .toCompletable() - ; - }); - } - - /** - * Process a single notification. Do not call directly. - */ - private Completable processNotification(NotificationJson notification) { - - return Completable.defer(() -> { - final String messageType = notification.messageType; - final String bucket = appStandbyBucketProvider.current().toString(); - final Long id = notification.id; - Timber.i("[Notifications] Processing (" + id + ") " + messageType + " - " + bucket); - - final long lastProcessedId = notificationRepository.getLastProcessedId(); - - if (id <= lastProcessedId) { - return Completable.complete(); // already processed! - } - - if (notification.previousId != lastProcessedId) { - throw NotificationProcessingError.fromMissingIds(notification, lastProcessedId); - } - - final long processingFailures = notificationRepository.getProcessingFailures(); - return notificationProcessor.process(notification, processingFailures) - .onErrorComplete(cause -> { - notificationRepository.increaseProcessingFailures(); - - logBreadcrumb(id, messageType, processingFailures, cause); - Timber.e(NotificationProcessingError.fromCause(notification, cause)); - - if (processingFailures > 3) { - // Abort after 3 failed retries to avoid bricking clients - return true; - } - - //noinspection RedundantIfStatement - if (messageType.equals(FulfillIncomingSwapMessage.SPEC.messageType)) { - // We don't allow skipping fulfills - return false; - } - - return true; // skip notification, log the error - }) - .doOnCompleted(() -> - notificationRepository.setLastProcessedId(id) - ); - }); - } - - private Single fetchNotificationReport(@Nullable Long afterId) { - return houstonClient.fetchNotificationReportAfter(afterId).toSingle(); - } - - private Subscription startProcessingQueue(Observable queue) { - // NOTE: using `observeOn` instead of `subscribeOn` here simplifies testing, by making - // subscriptions happen synchronously and making the jump to another thread later. - - // The fact that this method is called lazily after construction is also a compromise to - // allow testing. The way Mocks and Spies work, the `this` reference in the lambda below - // needs to be captured *after* patching this object, or calls won't be registered. - - return queue - .onBackpressureBuffer( - MAX_PENDING_REPORTS_BEFORE_DROP_OLDEST, - () -> Timber.e( - new BugDetected("NotificationReport queue too big: dropping oldest") - ), - BackpressureOverflow.ON_OVERFLOW_DROP_OLDEST - ) - .observeOn(scheduler) - .subscribe(report -> { - try { - processReport(report); - } catch (Exception e) { - Timber.e(e); - } - }); - } - - /** - * Transformer to run a task on each item of an Observable, logging errors if any. - */ - private Transformer forEach(Func1 createTask) { - return (items) -> items - .map(createTask) - .onBackpressureBuffer(200) - .concatMap(task -> task - .doOnError(Timber::e) - .toObservable() - ); - } - - private void logBreadcrumb(Long id, String msgType, long failures, Throwable cause) { - final String e = cause.getClass().getSimpleName() + ":" + cause.getLocalizedMessage(); - Timber.i( - "[Notifications] Error: (" + id + ") " + msgType + " - " + failures + " - " + e - ); - } -} diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/NotificationActions.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/NotificationActions.kt new file mode 100644 index 00000000..2ce79e2c --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/NotificationActions.kt @@ -0,0 +1,235 @@ +package io.muun.apollo.domain.action + +import android.os.Build +import io.muun.apollo.data.external.AppStandbyBucketProvider +import io.muun.apollo.data.net.HoustonClient +import io.muun.apollo.data.preferences.NotificationRepository +import io.muun.apollo.domain.NotificationProcessor +import io.muun.apollo.domain.action.base.AsyncAction0 +import io.muun.apollo.domain.action.base.AsyncActionStore +import io.muun.apollo.domain.errors.BugDetected +import io.muun.apollo.domain.errors.notifications.NotificationProcessingError.Companion.fromCause +import io.muun.apollo.domain.errors.notifications.NotificationProcessingError.Companion.fromMissingIds +import io.muun.apollo.domain.model.NotificationReport +import io.muun.common.api.beam.notification.NotificationJson +import io.muun.common.api.messages.FulfillIncomingSwapMessage +import rx.BackpressureOverflow +import rx.Completable +import rx.Observable +import rx.Scheduler +import rx.Single +import rx.Subscription +import rx.functions.Func1 +import rx.subjects.PublishSubject +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +@Singleton // important +class NotificationActions @Inject constructor( + private val notificationRepository: NotificationRepository, + private val houstonClient: HoustonClient, + asyncActionStore: AsyncActionStore, + private val notificationProcessor: NotificationProcessor, + private val appStandbyBucketProvider: AppStandbyBucketProvider, + @param:Named("notificationScheduler") private val scheduler: Scheduler, +) : NotificationPoller { + + companion object { + // If too many NotificationReports accumulate, we'll save them into a buffer with the following + // capacity. Dropping reports is not ideal, since Apollo will need to query Houston for missing + // notifications, but it's not serious either. This can happen after regaining connectivity, + // or after Muun services recover from a temporary failure. + private const val MAX_PENDING_REPORTS_BEFORE_DROP_OLDEST = 32 + } + + // NOTE: these 2 properties make this class stateful (and thus @Singleton). After moving the + // subject to a centralized SubjectStore (could be refactored out of AsyncActionStore) this + // action bag would be stateless. + private val reportQueue: PublishSubject = PublishSubject.create() + private var reportQueueSub: Subscription? = null + + @JvmField + val pullNotificationsAction: AsyncAction0 + + init { + pullNotificationsAction = asyncActionStore + .get("notifications/pull") { pullNotifications() } + } + + /** + * Invoke when a new NotificationReportJson is received. + */ + @Synchronized + fun onNotificationReport(report: NotificationReport) { + if (reportQueueSub == null || reportQueueSub!!.isUnsubscribed) { + reportQueueSub = startProcessingQueue(reportQueue) + } + reportQueue.onNext(report) + } + + /** + * Pull the latest notifications from Houston. + */ + override fun pullNotifications(): Observable { + return Observable.defer { + val lastProcessedId = notificationRepository.lastProcessedId + + Timber.i("[Notifications] Pulling... LastProcessedId: $lastProcessedId") + + houstonClient.fetchNotificationReportAfter(lastProcessedId) + .map { report: NotificationReport -> + onNotificationReport(report) + null + } + } + } + + /** + * Process a notification report. Do not call directly. + */ + private fun processReport(report: NotificationReport) { + var reportToProcess = report + + // If we have a gap between the report and what we last processed, ignore the report + // and start processing from the start of the gap. We'll refetch a bit of data, but it + // makes for simple code. + if (reportToProcess.previousId > notificationRepository.lastProcessedId) { + reportToProcess = fetchNotificationReport(notificationRepository.lastProcessedId) + .toBlocking() + .value() + } + processNotificationList(reportToProcess.preview).await() + + // We rely on the fact that we mark notifications that failed to process as processed + // anyway. Without that the following loop might be infinite. + while (notificationRepository.lastProcessedId < reportToProcess.maximumId) { + reportToProcess = fetchNotificationReport(notificationRepository.lastProcessedId) + .toBlocking() + .value() + processNotificationList(reportToProcess.preview).await() + } + } + + /** + * Process a list of notifications. Do not call directly. + */ + private fun processNotificationList(notifications: List): Completable { + return Completable.defer { + val lastIdBefore = notificationRepository.lastProcessedId + + Timber.i("[Notifications] Processing List: " + notifications.mapIds().asString()) + + Observable.from(notifications) + .compose(forEach { notification: NotificationJson -> + processNotification(notification) + }) + .lastOrDefault(null) + .flatMap { + val lastIdAfter = notificationRepository.lastProcessedId + if (lastIdAfter > lastIdBefore) { + return@flatMap houstonClient.confirmNotificationsDeliveryUntil( + lastIdAfter, + Build.MODEL, + Build.VERSION.SDK_INT.toString(), + appStandbyBucketProvider.current().toString() + ) + } else { + return@flatMap Observable.just(null) + } + } + .toCompletable() + } + } + + /** + * Process a single notification. Do not call directly. + */ + private fun processNotification(notification: NotificationJson): Completable { + return Completable.defer { + val messageType = notification.messageType + val bucket = appStandbyBucketProvider.current().toString() + val id = notification.id + + Timber.i("[Notifications] Processing ($id) $messageType - $bucket") + + val lastProcessedId = notificationRepository.lastProcessedId + if (id <= lastProcessedId) { + return@defer Completable.complete() // already processed! + } + + if (notification.previousId != lastProcessedId) { + throw fromMissingIds(notification, lastProcessedId) + } + + val processingFailures = notificationRepository.processingFailures + notificationProcessor.process(notification, processingFailures) + .onErrorComplete { cause: Throwable -> + notificationRepository.increaseProcessingFailures() + logBreadcrumb(id, messageType, processingFailures, cause) + Timber.e(fromCause(notification, cause)) + if (processingFailures > 3) { + // Abort after 3 failed retries to avoid bricking clients + return@onErrorComplete true + } + if (messageType == FulfillIncomingSwapMessage.SPEC.messageType) { + // We don't allow skipping fulfills + return@onErrorComplete false + } + true // skip notification, log the error + } + .doOnCompleted { notificationRepository.lastProcessedId = id } + } + } + + private fun fetchNotificationReport(afterId: Long): Single { + return houstonClient.fetchNotificationReportAfter(afterId).toSingle() + } + + private fun startProcessingQueue(queue: Observable): Subscription { + // NOTE: using `observeOn` instead of `subscribeOn` here simplifies testing, by making + // subscriptions happen synchronously and making the jump to another thread later. + + // The fact that this method is called lazily after construction is also a compromise to + // allow testing. The way Mocks and Spies work, the `this` reference in the lambda below + // needs to be captured *after* patching this object, or calls won't be registered. + return queue + .onBackpressureBuffer( + MAX_PENDING_REPORTS_BEFORE_DROP_OLDEST.toLong(), + { + Timber.e(BugDetected("NotificationReport queue too big: dropping oldest")) + }, + BackpressureOverflow.ON_OVERFLOW_DROP_OLDEST + ) + .observeOn(scheduler) + .subscribe { report: NotificationReport -> + try { + processReport(report) + } catch (e: Exception) { + Timber.e(e) + } + } + } + + /** + * Transformer to run a task on each item of an Observable, logging errors if any. + */ + private fun forEach(createTask: Func1): Observable.Transformer { + return Observable.Transformer { items: Observable -> + items + .map(createTask) + .onBackpressureBuffer(200) + .concatMap { task: Completable -> + task + .doOnError { t: Throwable -> Timber.e(t) } + .toObservable() + } + } + } + + private fun logBreadcrumb(id: Long, msgType: String, failures: Long, cause: Throwable) { + val e = cause.javaClass.simpleName + ":" + cause.localizedMessage + Timber.i("[Notifications] Error: ($id) $msgType - $failures - $e") + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/SigninActions.java b/android/apollo/src/main/java/io/muun/apollo/domain/action/SigninActions.java index bbcc57d5..d4b6a54b 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/SigninActions.java +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/SigninActions.java @@ -29,7 +29,7 @@ public Optional getSessionStatus() { * Delete session/auth related data currently stored, both in preferences as in secure storage. */ public void clearSession() { - authRepository.clear(); + authRepository.clearSession(); } /** diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/UserActions.java b/android/apollo/src/main/java/io/muun/apollo/domain/action/UserActions.java index f778a2ab..08084c13 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/UserActions.java +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/UserActions.java @@ -30,20 +30,23 @@ public class UserActions { public static final String NOTIFY_LOGOUT_ACTION = "user/logout"; private final UserRepository userRepository; + private final AuthRepository authRepository; private final HoustonClient houstonClient; - private final ContactActions contactActions; - private final UpdateProfilePictureAction updateProfilePictureAction; public final AsyncAction1 createPhoneAction; + public final AsyncAction1 resendVerificationCodeAction; + public final AsyncAction1 confirmPhoneAction; + public final AsyncAction1 createProfileAction; public final AsyncAction2 updateUsernameAction; + public final AsyncAction1 updatePrimaryCurrencyAction; public final AsyncAction2 submitFeedbackAction; @@ -54,19 +57,18 @@ public class UserActions { * Constructor. */ @Inject - public UserActions(AsyncActionStore asyncActionStore, - UserRepository userRepository, - AuthRepository authRepository, - HoustonClient houstonClient, - ContactActions contactActions, - UpdateProfilePictureAction updateProfilePictureAction) { + public UserActions( + AsyncActionStore asyncActionStore, + UserRepository userRepository, + AuthRepository authRepository, + HoustonClient houstonClient, + UpdateProfilePictureAction updateProfilePictureAction + ) { this.userRepository = userRepository; this.authRepository = authRepository; this.houstonClient = houstonClient; - this.contactActions = contactActions; - this.updateProfilePictureAction = updateProfilePictureAction; this.createPhoneAction = asyncActionStore diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/challenge_keys/recovery_code_setup/StartRecoveryCodeSetupAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/challenge_keys/recovery_code_setup/StartRecoveryCodeSetupAction.kt index 3c190222..ed01cc41 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/challenge_keys/recovery_code_setup/StartRecoveryCodeSetupAction.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/challenge_keys/recovery_code_setup/StartRecoveryCodeSetupAction.kt @@ -35,8 +35,8 @@ class StartRecoveryCodeSetupAction @Inject constructor( Preconditions.checkNotNull(setupChallengeResponse.muunKey) Preconditions.checkNotNull(setupChallengeResponse.muunKeyFingerprint) - keysRepository.storeEncryptedMuunPrivateKey(setupChallengeResponse.muunKey) - keysRepository.storeMuunKeyFingerprint(setupChallengeResponse.muunKeyFingerprint) + keysRepository.storeEncryptedMuunPrivateKey(setupChallengeResponse.muunKey!!) + keysRepository.storeMuunKeyFingerprint(setupChallengeResponse.muunKeyFingerprint!!) } .onErrorResumeNext { error -> Observable.error(StartRecoveryCodeSetupError(error)) diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/di/ActionComponent.java b/android/apollo/src/main/java/io/muun/apollo/domain/action/di/ActionComponent.java index 156258a1..f794a324 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/di/ActionComponent.java +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/di/ActionComponent.java @@ -29,6 +29,7 @@ import io.muun.apollo.domain.action.operation.SubmitPaymentAction; import io.muun.apollo.domain.action.permission.UpdateContactsPermissionStateAction; import io.muun.apollo.domain.action.realtime.FetchRealTimeDataAction; +import io.muun.apollo.domain.action.realtime.FetchRealTimeFeesAction; import io.muun.apollo.domain.action.session.CreateLoginSessionAction; import io.muun.apollo.domain.action.session.LogInAction; import io.muun.apollo.domain.action.session.SyncApplicationDataAction; @@ -82,6 +83,8 @@ public interface ActionComponent { FetchRealTimeDataAction fetchRealTimeDataAction(); + FetchRealTimeFeesAction fetchRealTimeFeesAction(); + ResolveOperationUriAction resolveOperationUriAction(); ResolveLnInvoiceAction resolveLnInvoiceAction(); diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/keys/DecryptAndStoreKeySetAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/keys/DecryptAndStoreKeySetAction.kt index 5ee0dcac..8a3dc17d 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/keys/DecryptAndStoreKeySetAction.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/keys/DecryptAndStoreKeySetAction.kt @@ -40,8 +40,8 @@ class DecryptAndStoreKeySetAction @Inject constructor( if (keySet.muunKey != null) { Preconditions.checkNotNull(keySet.muunKeyFingerprint) - keysRepository.storeEncryptedMuunPrivateKey(keySet.muunKey) - keysRepository.storeMuunKeyFingerprint(keySet.muunKeyFingerprint) + keysRepository.storeEncryptedMuunPrivateKey(keySet.muunKey!!) + keysRepository.storeMuunKeyFingerprint(keySet.muunKeyFingerprint!!) } val userPrivateKey = KeyCrypter().decrypt(keySet.encryptedPrivateKey, userInput) diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/migration/MigrateFingerprintsAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/migration/MigrateFingerprintsAction.kt index a49c4856..3b70bbf3 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/migration/MigrateFingerprintsAction.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/migration/MigrateFingerprintsAction.kt @@ -20,15 +20,15 @@ class MigrateFingerprintsAction @Inject constructor( ::Pair ) .doOnNext { (userKeyFingerprint, muunKeyFingerprint) -> - keysRepository.storeUserKeyFingerprint(userKeyFingerprint) - keysRepository.storeMuunKeyFingerprint(muunKeyFingerprint) + keysRepository.storeUserKeyFingerprint(userKeyFingerprint!!) + keysRepository.storeMuunKeyFingerprint(muunKeyFingerprint!!) } .toVoid() - private fun getMuunKeyFingerprint() = + private fun getMuunKeyFingerprint(): Observable = houstonClient.fetchMuunKeyFingerprint() - private fun getUserKeyFingerprint() = + private fun getUserKeyFingerprint(): Observable = keysRepository.basePrivateKey .map { Encodings.bytesToHex(it.fingerprint) } diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/permission/UpdateContactsPermissionStateAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/permission/UpdateContactsPermissionStateAction.kt index 380e527d..54a2c88f 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/permission/UpdateContactsPermissionStateAction.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/permission/UpdateContactsPermissionStateAction.kt @@ -13,7 +13,7 @@ class UpdateContactsPermissionStateAction @Inject constructor( ) { val action = UpdatePermissionStateAction( - userRepository::getContactsPermissionState, + userRepository::contactsPermissionState, this::onContactsPermissionGranted, this::onContactsPermissionDenied, ) diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/realtime/FetchRealTimeDataAction.java b/android/apollo/src/main/java/io/muun/apollo/domain/action/realtime/FetchRealTimeDataAction.java index c92eefb4..aa0cec03 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/realtime/FetchRealTimeDataAction.java +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/realtime/FetchRealTimeDataAction.java @@ -85,8 +85,7 @@ private Observable forceSyncRealTimeData() { } private boolean shouldSync() { - final boolean isFeeRecent = feeWindowRepository.isSet() - && feeWindowRepository.fetchOne().isRecent(); + final boolean isFeeRecent = feeWindowRepository.isFeeRecent(); final boolean isExchangeRateRecent = exchangeRateWindowRepository.isSet() && exchangeRateWindowRepository.fetchOne().isRecent(); diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/realtime/FetchRealTimeFeesAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/realtime/FetchRealTimeFeesAction.kt new file mode 100644 index 00000000..f3a109ad --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/realtime/FetchRealTimeFeesAction.kt @@ -0,0 +1,70 @@ +package io.muun.apollo.domain.action.realtime + +import io.muun.apollo.data.net.HoustonClient +import io.muun.apollo.data.preferences.FeeWindowRepository +import io.muun.apollo.data.preferences.MinFeeRateRepository +import io.muun.apollo.data.preferences.TransactionSizeRepository +import io.muun.apollo.domain.action.base.BaseAsyncAction0 +import io.muun.apollo.domain.libwallet.errors.FeeBumpFunctionsStoreError +import io.muun.apollo.domain.model.RealTimeFees +import io.muun.apollo.domain.utils.toLibwalletModel +import io.muun.common.Rules +import io.muun.common.rx.RxHelper +import newop.Newop +import rx.Observable +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Update fees data, such as fee window and fee bump functions. + */ + +@Singleton +class FetchRealTimeFeesAction @Inject constructor( + private val houstonClient: HoustonClient, + private val feeWindowRepository: FeeWindowRepository, + private val minFeeRateRepository: MinFeeRateRepository, + private val transactionSizeRepository: TransactionSizeRepository +) : BaseAsyncAction0() { + override fun action(): Observable { + return Observable.defer { + syncRealTimeFees() + } + } + + private fun syncRealTimeFees(): Observable { + Timber.d("[Sync] Updating realTime fees data") + + transactionSizeRepository.nextTransactionSize?.sizeProgression?.let { + return houstonClient.fetchRealTimeFees(it) + .doOnNext { realTimeFees: RealTimeFees -> + Timber.d("[Sync] Saving updated fees") + storeFeeData(realTimeFees) + storeFeeBumpFunctions(realTimeFees.feeBumpFunctions) + } + .map(RxHelper::toVoid) + } + + Timber.e("syncRealTimeFees was called without a local valid NTS") + return Observable.just(null) + } + + private fun storeFeeData(realTimeFees: RealTimeFees) { + feeWindowRepository.store(realTimeFees.feeWindow) + val minMempoolFeeRateInSatsPerWeightUnit = + Rules.toSatsPerWeight(realTimeFees.minMempoolFeeRateInSatPerVbyte) + minFeeRateRepository.store(minMempoolFeeRateInSatsPerWeightUnit) + } + + private fun storeFeeBumpFunctions(feeBumpFunctions: List) { + val feeBumpFunctionsStringList = feeBumpFunctions.toLibwalletModel() + + try { + Newop.persistFeeBumpFunctions(feeBumpFunctionsStringList) + } catch (e: Exception) { + Timber.e(e, "Error storing fee bump functions") + throw FeeBumpFunctionsStoreError(feeBumpFunctions.toString(), e) + } + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/session/ClearRepositoriesAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/ClearRepositoriesAction.kt new file mode 100644 index 00000000..57561375 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/ClearRepositoriesAction.kt @@ -0,0 +1,78 @@ +package io.muun.apollo.domain.action.session + +import android.content.Context +import io.muun.apollo.data.preferences.BaseRepository +import io.muun.apollo.data.preferences.FirebaseInstallationIdRepository +import io.muun.apollo.data.preferences.RepositoryRegistry +import timber.log.Timber +import javax.inject.Inject + +class ClearRepositoriesAction @Inject constructor( + + // Presentation + private val context: Context, + + // Data + private val repositoryRegistry: RepositoryRegistry, + private val firebaseInstallationIdRepository: FirebaseInstallationIdRepository, +) { + + private val thirdPartyPreferencesToClear: List = createThirdPartyPreferencesList() + + /** + * Destroy all data in non-encrypted repositories. + * Warning: this method is supposed to be used solely in PreferencesMigrationManager, since we + * shouldn't be needing any "full wipe preference migration" anymore, this method is left just + * for the sake of completeness. + */ + fun clearAll() { + clearForLogout() + clearRepository(firebaseInstallationIdRepository) + } + + /** + * Destroy all data in non-encrypted repositories that should be cleared upon logout. We avoid + * clearing some repositories on logout (e.g FcmTokenRepository). + */ + fun clearForLogout() { + for (repository in repositoryRegistry.repositoriesToClearOnLogout()) { + clearRepository(repository) + } + for (fileName in thirdPartyPreferencesToClear) { + context.getSharedPreferences(fileName, Context.MODE_PRIVATE).edit().clear().apply() + } + } + + private fun clearRepository(repository: BaseRepository) { + try { + repository.clear() + } catch (e: Exception) { + Timber.e(e) + } + } + + private fun createThirdPartyPreferencesList(): List { + // NOTE: there is no reliable way of listing all SharedPreferences. The XML files are + // *usually* located in a known directory, but some vendors change this. We cannot control + // where the directory lies in a particular device, but we can know and control the actual + // preferences created by Apollo's dependencies. + + // The following list was created by logging into Apollo, and listing the XML files created + // in the data/shared_prefs folder of the application. + + // ON-RELEASE verify that the list matches the actual files added by our dependencies. This + // won't crash if files do not exist, but we could miss some. + return listOf( + "TwitterAdvertisingInfoPreferences", + "WebViewChromiumPrefs", + "com.crashlytics.prefs", + "com.crashlytics.sdk.android:answers:settings", + "com.crashlytics.sdk.android.crashlytics-core" + + ":com.crashlytics.android.core.CrashlyticsCore", + "com.google.android.gms.appid", + "com.google.android.gms.measurement.prefs", + "io.fabric.sdk.android:fabric:io.fabric.sdk.android.Onboarding" + ) + } + +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/session/LegacyLogoutUserForMigrationAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/LegacyLogoutUserForMigrationAction.kt new file mode 100644 index 00000000..0bb90952 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/LegacyLogoutUserForMigrationAction.kt @@ -0,0 +1,39 @@ +package io.muun.apollo.domain.action.session + +import io.muun.apollo.data.async.tasks.TaskScheduler +import io.muun.apollo.data.db.DaoManager +import io.muun.apollo.data.external.NotificationService +import io.muun.apollo.data.os.secure_storage.SecureStorageProvider +import io.muun.apollo.domain.ApplicationLockManager +import io.muun.apollo.domain.action.ContactActions +import io.muun.apollo.domain.action.base.AsyncActionStore +import javax.inject.Inject + +class LegacyLogoutUserForMigrationAction @Inject constructor( + private val taskScheduler: TaskScheduler, + private val asyncActionStore: AsyncActionStore, + private val secureStorageProvider: SecureStorageProvider, + private val contactActions: ContactActions, + private val daoManager: DaoManager, + private val lockManager: ApplicationLockManager, + private val notificationService: NotificationService, + private val clearRepositories: ClearRepositoriesAction, +) { + + fun run() { + taskScheduler.unscheduleAllTasks() + asyncActionStore.resetAll() + secureStorageProvider.wipe() + + // Note: using this to avoid copying a lot of code. Also repository clear() is a safe op + // (if we clear a repo that didn't exist in the past it doesn't hurt). + clearRepositories.clearAll() + + contactActions.stopWatchingContacts() + daoManager.delete() + + lockManager.cancelAutoSetLocked() + + notificationService.cancelAllNotifications() + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/session/LogoutAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/LogoutAction.kt new file mode 100644 index 00000000..25665749 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/LogoutAction.kt @@ -0,0 +1,32 @@ +package io.muun.apollo.domain.action.session + +import io.muun.apollo.data.preferences.AuthRepository +import io.muun.apollo.domain.action.LogoutActions +import io.muun.apollo.domain.action.UserActions +import io.muun.apollo.domain.errors.MuunError +import io.muun.common.Optional +import timber.log.Timber +import javax.inject.Inject + +class LogoutAction @Inject constructor( + private val logoutActions: LogoutActions, + private val userActions: UserActions, + private val authRepository: AuthRepository, +) { + + fun run() { + val jwt: String = getJwt() + logoutActions.destroyRecoverableWallet() + userActions.notifyLogoutAction.run(jwt) + } + + private fun getJwt(): String { + val serverJwt: Optional = authRepository.serverJwt + if (!serverJwt.isPresent) { + // Shouldn't happen but we wanna know 'cause probably a bug + Timber.e(MuunError("Auth token expected to be present")) + return "" + } + return serverJwt.get() + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/session/SyncApplicationDataAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/SyncApplicationDataAction.kt index 1bcd8fa3..6e00132d 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/session/SyncApplicationDataAction.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/session/SyncApplicationDataAction.kt @@ -76,18 +76,17 @@ class SyncApplicationDataAction @Inject constructor( val step1 = syncPublicKeySet.action() // These can run in any order: - val step2 = Observable.zip( + val step2 = Observable.mergeDelayError( fetchUserInfo(), fetchNextTransactionSize.action(), fetchRealTimeData.action(), - syncContacts(hasContactsPermission), + runOnlyIf(!isFirstSession) { syncContacts(hasContactsPermission) }, Observable.fromCallable(apiMigrationsManager::reset), - RxHelper::toVoid ) // These must run after the ones before: val step3 = Observable.zip( - operationActions.fetchReplaceOperations(), + runOnlyIf(!isFirstSession) { operationActions.fetchReplaceOperations() }, registerInvoices.action(), googlePlayIntegrityCheck.run(), RxHelper::toVoid @@ -129,4 +128,12 @@ class SyncApplicationDataAction @Inject constructor( contactActions.fetchReplaceContacts() } } + + private fun runOnlyIf(condition: Boolean, block: () -> Observable): Observable { + return if (condition) { + block() + } else { + Observable.just(null) + } + } } diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/action/user/DeleteWalletAction.kt b/android/apollo/src/main/java/io/muun/apollo/domain/action/user/DeleteWalletAction.kt index b9df992c..2f4f9064 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/action/user/DeleteWalletAction.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/action/user/DeleteWalletAction.kt @@ -1,8 +1,10 @@ package io.muun.apollo.domain.action.user import io.muun.apollo.data.net.HoustonClient +import io.muun.apollo.domain.action.LogoutActions import io.muun.apollo.domain.action.base.BaseAsyncAction0 import io.muun.apollo.domain.action.challenge_keys.SignChallengeAction +import io.muun.apollo.domain.selector.UserSelector import io.muun.common.Optional import io.muun.common.crypto.ChallengeType import io.muun.common.model.challenge.Challenge @@ -12,11 +14,13 @@ import javax.inject.Singleton @Singleton class DeleteWalletAction @Inject constructor( + private val userSel: UserSelector, + private val logoutActions: LogoutActions, private val signChallenge: SignChallengeAction, private val houstonClient: HoustonClient, -) : BaseAsyncAction0() { +) : BaseAsyncAction0>() { - override fun action(): Observable { + override fun action(): Observable> { return Observable.defer { houstonClient.requestChallenge(ChallengeType.USER_KEY) .map { maybeChallenge: Optional -> @@ -27,6 +31,11 @@ class DeleteWalletAction @Inject constructor( houstonClient.deleteWallet(challengeSignature) .andThen(Observable.just(null)) } + .map { + val maybeSupportId = userSel.getOptional().flatMap { it.supportId } + logoutActions.dangerouslyDestroyWallet() + maybeSupportId + } } } } \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/analytics/AnalyticsEvent.kt b/android/apollo/src/main/java/io/muun/apollo/domain/analytics/AnalyticsEvent.kt index 162e20dc..e369eb10 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/analytics/AnalyticsEvent.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/analytics/AnalyticsEvent.kt @@ -1,8 +1,10 @@ package io.muun.apollo.domain.analytics +import android.app.Activity import io.muun.apollo.domain.model.BitcoinUnit import io.muun.apollo.domain.model.NightMode import io.muun.apollo.domain.model.PaymentRequest +import io.muun.apollo.domain.model.report.CrashReport import io.muun.common.model.OperationDirection import java.util.* @@ -30,7 +32,10 @@ sealed class AnalyticsEvent(metadataKeyValues: List> = listOf( class S_AUTHORIZE_EMAIL : AnalyticsEvent() class S_PIN_CHOOSE : AnalyticsEvent() class S_PIN_REPEAT : AnalyticsEvent() - class S_PIN_LOCKED : AnalyticsEvent() + class S_PIN_LOCKED(activity: Activity) : AnalyticsEvent( + listOf("screen" to activity.javaClass.simpleName) + ) + class S_SIGN_IN_WITH_RC : AnalyticsEvent() class S_SIGN_IN_WITH_RC_AUTHORIZE_EMAIL : AnalyticsEvent() @@ -461,7 +466,12 @@ sealed class AnalyticsEvent(metadataKeyValues: List> = listOf( ) ) - class E_ERROR_REPORT_DIALOG : AnalyticsEvent() + class E_ERROR_REPORT_DIALOG(vararg extras: Any) : AnalyticsEvent( + listOf( + *extras.mapIndexed { index: Int, extra: Any -> Pair("extra$index", extra) } + .toTypedArray() + ) + ) enum class LNURL_WITHDRAW_STATE_TYPE { CONTACTING, @@ -501,8 +511,14 @@ sealed class AnalyticsEvent(metadataKeyValues: List> = listOf( PDF_EXPORTED } - class E_CRASHLYTICS_ERROR(value: String) : AnalyticsEvent( - listOf("title" to value) + class E_CRASHLYTICS_ERROR(report: CrashReport) : AnalyticsEvent( + listOf( + "title" to report.getTrackingTitle(), + "tag" to report.tag, + "message" to report.message, + "error" to report.printError().replace("\n", " "), + "metadata" to report.printMetadata() + ) ) class E_BREADCRUMB(value: String) : AnalyticsEvent( diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/LibwalletBridge.java b/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/LibwalletBridge.java index 90bcd6b3..22d89f7c 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/LibwalletBridge.java +++ b/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/LibwalletBridge.java @@ -24,6 +24,7 @@ import io.muun.common.utils.Encodings; import io.muun.common.utils.Preconditions; +import libwallet.BackendActivatedFeatureStatusProvider; import libwallet.Config; import libwallet.EKInput; import libwallet.EKOutput; @@ -49,9 +50,14 @@ public class LibwalletBridge { /** * Initialize libwallet. */ - public static void init(String dataDir) { + public static void init( + String dataDir, + BackendActivatedFeatureStatusProvider featureStatusProvider + ) { final Config config = new Config(); config.setDataDir(dataDir); + config.setFeatureStatusProvider(featureStatusProvider); + config.setAppLogSink(new LibwalletLogAdapter()); Libwallet.init(config); } diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/LibwalletLogAdapter.kt b/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/LibwalletLogAdapter.kt new file mode 100644 index 00000000..3891263d --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/LibwalletLogAdapter.kt @@ -0,0 +1,99 @@ +package io.muun.apollo.domain.libwallet + +import android.util.Log +import io.muun.apollo.domain.utils.isEmpty +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.decodeFromJsonElement +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import libwallet.AppLogSink +import timber.log.Timber + +class LibwalletLogAdapter : AppLogSink { + + private val json = Json { + ignoreUnknownKeys = true + } + + override fun write(logBytes: ByteArray?): Long { + if (logBytes == null) { + return 0 + } + + val logLine = logBytes.toString(Charsets.UTF_8) + val jsonLog = json.parseToJsonElement(logLine).jsonObject + + val level = extractLevel(jsonLog) + val message = extractMessage(jsonLog) + val sourceLocation = extractSourceLocation(jsonLog) + + Timber.tag(sourceLocation).log(level, message) + + return logBytes.size.toLong() + } + + /** + * Return a message extracted from the json log object. + * + * The message will include the msg field unaltered (or "[MISSING MESSAGE]" if missing) + * followed by any extra values that can be found at the top level of the objects as + * formatted by extractExtraValues(). + */ + private fun extractMessage(jsonLog: JsonObject): String { + val message: String = jsonLog["msg"]?.jsonPrimitive?.contentOrNull ?: "[MISSING MESSAGE]" + val extraValues = extractExtraValues(jsonLog) + return if (extraValues.isEmpty()) + message + else + "$message $extraValues" + } + + /** + * Return a string encoding the source file and function that originates the log. + */ + private fun extractSourceLocation(jsonLog: JsonObject): String { + return jsonLog["source"]?.let { source -> + json.decodeFromJsonElement(source).let { + val fileName = it.file + val functionName = it.function + "$fileName:$functionName" + } + } ?: "" + } + + /** + * Return all of the non-default top level attributes from the json log as a string. + * + * This skips over the time, msg, level and source attributes. + */ + private fun extractExtraValues(jsonLog: JsonObject): String { + val knownKeys = setOf("time", "msg", "level", "source") + return jsonLog.entries + .filter { !knownKeys.contains(it.key) } + .joinToString { "${it.key}=${it.value}" } + } + + /** + * Return an android log level mapped from the json representation of the log. + */ + private fun extractLevel(jsonLog: JsonObject): Int { + val level = jsonLog["level"]?.jsonPrimitive?.contentOrNull + return when (level) { + "DEBUG" -> Log.DEBUG + "INFO" -> Log.INFO + "WARN" -> Log.WARN + // If we're not sure, it's an error. + else -> Log.ERROR + } + } +} + +@Serializable +private data class LibwalletLogSourceLocation( + val file: String = "", + val line: Long = 0, + val function: String = "", +) diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/errors/FeeBumpFunctionsStoreError.kt b/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/errors/FeeBumpFunctionsStoreError.kt new file mode 100644 index 00000000..3ebb6f81 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/domain/libwallet/errors/FeeBumpFunctionsStoreError.kt @@ -0,0 +1,14 @@ +package io.muun.apollo.domain.libwallet.errors + +import io.muun.apollo.domain.errors.MuunError + +private var msg = "Libwallet failed to store fee bump functions" + +class FeeBumpFunctionsStoreError( + val functions: String, cause: Throwable +) : MuunError(msg, cause) { + + init { + metadata["functions"] = functions + } +} \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/model/MuunFeature.kt b/android/apollo/src/main/java/io/muun/apollo/domain/model/MuunFeature.kt index ac881223..34298c70 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/model/MuunFeature.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/model/MuunFeature.kt @@ -9,7 +9,8 @@ enum class MuunFeature { TAPROOT_PREACTIVATION, APOLLO_BIOMETRICS, HIGH_FEES_HOME_BANNER, - HIGH_FEES_RECEIVE_FLOW; + HIGH_FEES_RECEIVE_FLOW, + EFFECTIVE_FEES_CALCULATION; companion object { @@ -20,6 +21,7 @@ enum class MuunFeature { MuunFeatureJson.APOLLO_BIOMETRICS -> APOLLO_BIOMETRICS MuunFeatureJson.HIGH_FEES_HOME_BANNER -> HIGH_FEES_HOME_BANNER MuunFeatureJson.HIGH_FEES_RECEIVE_FLOW -> HIGH_FEES_RECEIVE_FLOW + MuunFeatureJson.EFFECTIVE_FEES_CALCULATION -> EFFECTIVE_FEES_CALCULATION else -> throw MissingCaseError(json) } @@ -30,6 +32,7 @@ enum class MuunFeature { Libwallet.BackendFeatureApolloBiometrics -> APOLLO_BIOMETRICS Libwallet.BackendFeatureHighFeesHomeBanner -> HIGH_FEES_HOME_BANNER Libwallet.BackendFeatureHighFeesReceiveFlow -> HIGH_FEES_RECEIVE_FLOW + Libwallet.BackendFeatureEffectiveFeesCalculation -> EFFECTIVE_FEES_CALCULATION else -> throw MissingCaseError(name, "MuunFeature conversion from libwallet") } @@ -42,6 +45,7 @@ enum class MuunFeature { APOLLO_BIOMETRICS -> MuunFeatureJson.APOLLO_BIOMETRICS HIGH_FEES_HOME_BANNER -> MuunFeatureJson.HIGH_FEES_HOME_BANNER HIGH_FEES_RECEIVE_FLOW -> MuunFeatureJson.HIGH_FEES_RECEIVE_FLOW + EFFECTIVE_FEES_CALCULATION -> MuunFeatureJson.EFFECTIVE_FEES_CALCULATION } fun toLibwalletModel(): String = @@ -51,5 +55,6 @@ enum class MuunFeature { APOLLO_BIOMETRICS -> Libwallet.BackendFeatureApolloBiometrics HIGH_FEES_HOME_BANNER -> Libwallet.BackendFeatureHighFeesHomeBanner HIGH_FEES_RECEIVE_FLOW -> Libwallet.BackendFeatureHighFeesReceiveFlow + EFFECTIVE_FEES_CALCULATION -> Libwallet.BackendFeatureEffectiveFeesCalculation } } \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/model/PaymentContext.kt b/android/apollo/src/main/java/io/muun/apollo/domain/model/PaymentContext.kt index ed0f24ff..51b692d5 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/model/PaymentContext.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/model/PaymentContext.kt @@ -2,7 +2,7 @@ package io.muun.apollo.domain.model import io.muun.apollo.domain.model.user.User import io.muun.common.Rules -import newop.PaymentContext +import newop.InitialPaymentContext /** * The contextual information required to analyze and process a PaymentRequest. @@ -21,8 +21,8 @@ class PaymentContext( /** * Adapt apollo's (java) model to libwallet's (go). */ - fun toLibwallet(submarineSwap: SubmarineSwap?): PaymentContext { - val libwalletPayCtx = PaymentContext() + fun toLibwallet(submarineSwap: SubmarineSwap?): InitialPaymentContext { + val libwalletPayCtx = InitialPaymentContext() libwalletPayCtx.feeWindow = feeWindow.toLibwallet() libwalletPayCtx.exchangeRateWindow = exchangeRateWindow.toLibwallet() libwalletPayCtx.nextTransactionSize = nextTransactionSize.toLibwallet() diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/model/RealTimeFees.kt b/android/apollo/src/main/java/io/muun/apollo/domain/model/RealTimeFees.kt new file mode 100644 index 00000000..70ac6aa1 --- /dev/null +++ b/android/apollo/src/main/java/io/muun/apollo/domain/model/RealTimeFees.kt @@ -0,0 +1,12 @@ +package io.muun.apollo.domain.model + +import org.threeten.bp.ZonedDateTime + +data class RealTimeFees( + // Each fee bump functions is codified as a base64 string. + val feeBumpFunctions: List, + val feeWindow: FeeWindow, + val minMempoolFeeRateInSatPerVbyte: Double, + val minFeeRateIncrementToReplaceByFeeInSatPerVbyte: Double, + val computedAt: ZonedDateTime +) \ No newline at end of file diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReport.kt b/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReport.kt index e250f3a8..21c30aba 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReport.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/model/report/CrashReport.kt @@ -8,13 +8,13 @@ data class CrashReport( val message: String, val error: Throwable, val originalError: Throwable?, - val metadata: MutableMap + val metadata: MutableMap, ) { fun print() = "Tag:$tag\nMessage:$message\nError:${printError()}\nMetadata:{\n\n${printMetadata()}}" - private fun printMetadata(): String { + fun printMetadata(): String { val builder = StringBuilder() for (key in metadata.keys) { builder.append("$key=${metadata[key]}\n\n") @@ -22,5 +22,9 @@ data class CrashReport( return builder.toString() } - private fun printError() = Log.getStackTraceString(error) + fun printError(): String = + Log.getStackTraceString(error) + + fun getTrackingTitle(): String = + error.javaClass.simpleName + ":" + error.localizedMessage?.replace("\n", " ") } \ No newline at end of file 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 d111415c..15b7d5ed 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 @@ -32,7 +32,7 @@ class LogoutOptionsSelector @Inject constructor( } } - fun watch(): Observable = + private fun watch(): Observable = Observable .combineLatest( userSel.watch(), diff --git a/android/apollo/src/main/java/io/muun/apollo/domain/selector/PaymentContextSelector.kt b/android/apollo/src/main/java/io/muun/apollo/domain/selector/PaymentContextSelector.kt index 0acc1f93..576f42a5 100644 --- a/android/apollo/src/main/java/io/muun/apollo/domain/selector/PaymentContextSelector.kt +++ b/android/apollo/src/main/java/io/muun/apollo/domain/selector/PaymentContextSelector.kt @@ -21,8 +21,8 @@ class PaymentContextSelector @Inject constructor( Observable.combineLatest( userSel.watch(), exchangeRateWindowRepository.fetch(), - feeWindowRepository.fetch(), - transactionSizeRepository.watchNextTransactionSize(), + feeWindowRepository.fetchNonNull(), + transactionSizeRepository.watchNonNullNts(), minFeeRateRepository.fetch(), ::PaymentContext ) 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 fff4230f..2d6b860f 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 @@ -9,6 +9,7 @@ import io.muun.apollo.data.net.base.NetworkException import io.muun.apollo.domain.errors.MuunError import io.muun.apollo.domain.errors.SecureStorageError import io.muun.apollo.domain.model.report.CrashReport +import io.muun.common.Optional import io.muun.common.model.Currency import io.muun.common.rx.ObservableFn import io.muun.common.rx.RxHelper @@ -19,7 +20,7 @@ import libwallet.StringList import rx.Observable import java.io.Serializable import java.net.SocketTimeoutException -import java.util.* +import java.util.Locale import java.util.concurrent.TimeoutException import javax.crypto.BadPaddingException import javax.money.Monetary @@ -92,7 +93,10 @@ inline fun Throwable.isInstanceOrIsCausedByError() = this is T || isCausedByError() inline fun Throwable.isCausedByError() = - ExceptionUtils.getTypedCause(this, T::class.java).isPresent + getTypedClause().isPresent + +inline fun Throwable.getTypedClause(): Optional = + ExceptionUtils.getTypedCause(this, T::class.java) /** * Return the list of currencies reported by the device (based on the list of available locales diff --git a/android/apollo/src/test/java/io/muun/apollo/domain/action/AddressActionsTest.java b/android/apollo/src/test/java/io/muun/apollo/domain/action/AddressActionsTest.java deleted file mode 100644 index 8987bd81..00000000 --- a/android/apollo/src/test/java/io/muun/apollo/domain/action/AddressActionsTest.java +++ /dev/null @@ -1,160 +0,0 @@ -package io.muun.apollo.domain.action; - -import io.muun.apollo.BaseTest; -import io.muun.apollo.data.net.HoustonClient; -import io.muun.apollo.data.preferences.KeysRepository; -import io.muun.apollo.domain.action.address.CreateAddressAction; -import io.muun.apollo.domain.action.address.SyncExternalAddressIndexesAction; -import io.muun.apollo.template.TemplateHelpers; -import io.muun.common.api.ExternalAddressesRecord; -import io.muun.common.crypto.hd.PublicKeyTriple; - -import org.bitcoinj.core.Context; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.params.RegTestParams; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.mockito.Mock; -import rx.Observable; - -import static io.muun.apollo.TestUtils.fetchItemFromObservable; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - - -public class AddressActionsTest extends BaseTest { - - @Mock - private KeysRepository keysRepository; - - @Mock - private HoustonClient houstonClient; - - @Mock - - private SyncExternalAddressIndexesAction syncExternalAddressIndexes; - private CreateAddressAction createAddress; - - private NetworkParameters networkParameters = RegTestParams.get(); - - @Before - public void setUp() { - Context.propagate(new Context(networkParameters)); - - syncExternalAddressIndexes = new SyncExternalAddressIndexesAction( - houstonClient, - keysRepository - ); - - createAddress = new CreateAddressAction( - keysRepository, - networkParameters, - syncExternalAddressIndexes - ); - } - - private String toTransactionSchemeV3Address(PublicKeyTriple basePublicKeyTriple, int index) { - return null; -/* - TODO mv this and ignored tests to libwallet - final PublicKeyTriple pubKeyTriple = basePublicKeyTriple.deriveNextValidChild(index); - return TransactionScheme.V3.createAddress(pubKeyTriple, networkParameters).getAddress(); -*/ - } - - @Test - @Ignore - public void getExternalAddress() { - - final PublicKeyTriple basePublicKeyTriple = - TemplateHelpers.externalPublicKeyTriple().generateValue(); - - doReturn(basePublicKeyTriple.toPair()).when(keysRepository).getBasePublicKeyPair(); - - doReturn(5).when(keysRepository).getMaxUsedExternalAddressIndex(); - doReturn(10).when(keysRepository).getMaxWatchingExternalAddressIndex(); - - assertThat(createAddress.actionNow().getLegacy().getAddress()) - .isEqualTo(toTransactionSchemeV3Address(basePublicKeyTriple, 6)); - - verify(keysRepository).setMaxUsedExternalAddressIndex(6); - - verify(syncExternalAddressIndexes).run(); - } - - @Test - @Ignore - public void getExternalAddress_when_maxUsedIndex_is_null() { - - final PublicKeyTriple basePublicKeyTriple = - TemplateHelpers.externalPublicKeyTriple().generateValue(); - - doReturn(basePublicKeyTriple.toPair()).when(keysRepository).getBasePublicKeyPair(); - - doReturn(null).when(keysRepository).getMaxUsedExternalAddressIndex(); - doReturn(10).when(keysRepository).getMaxWatchingExternalAddressIndex(); - - assertThat(createAddress.actionNow().getLegacy().getAddress()) - .isEqualTo(toTransactionSchemeV3Address(basePublicKeyTriple, 0)); - - verify(keysRepository).setMaxUsedExternalAddressIndex(0); - } - - @Test - @Ignore - public void getExternalAddress_when_maxUsedIndex_is_equal_to_maxWatchingIndex() { - - final PublicKeyTriple basePublicKeyTriple = - TemplateHelpers.externalPublicKeyTriple().generateValue(); - - doReturn(basePublicKeyTriple.toPair()).when(keysRepository).getBasePublicKeyPair(); - - doReturn(3).when(keysRepository).getMaxUsedExternalAddressIndex(); - doReturn(3).when(keysRepository).getMaxWatchingExternalAddressIndex(); - - assertThat(createAddress.actionNow().getLegacy().getAddress()).isIn( - toTransactionSchemeV3Address(basePublicKeyTriple, 0), - toTransactionSchemeV3Address(basePublicKeyTriple, 1), - toTransactionSchemeV3Address(basePublicKeyTriple, 2), - toTransactionSchemeV3Address(basePublicKeyTriple, 3) - ); - - verify(keysRepository, never()).setMaxUsedExternalAddressIndex(anyInt()); - } - - @Test - public void syncExternalAddressesIndexes_when_maxUsedIndex_is_null() { - - final ExternalAddressesRecord record = new ExternalAddressesRecord(0, 10); - - doReturn(null).when(keysRepository).getMaxUsedExternalAddressIndex(); - doReturn(null).when(keysRepository).getMaxWatchingExternalAddressIndex(); - doReturn(Observable.just(record)).when(houstonClient).fetchExternalAddressesRecord(); - - fetchItemFromObservable(syncExternalAddressIndexes.action()); - - verify(keysRepository).setMaxUsedExternalAddressIndex(0); - verify(keysRepository).setMaxWatchingExternalAddressIndex(10); - } - - @Test - public void syncExternalAddressesIndexes_when_local_maxUsedIndex_is_greater_than_remote() { - - final ExternalAddressesRecord record = new ExternalAddressesRecord(0, 10); - - doReturn(2).when(keysRepository).getMaxUsedExternalAddressIndex(); - doReturn(5).when(keysRepository).getMaxWatchingExternalAddressIndex(); - - doReturn(Observable.just(record)) - .when(houstonClient).updateExternalAddressesRecord(anyInt()); - - fetchItemFromObservable(syncExternalAddressIndexes.action()); - - verify(keysRepository).setMaxUsedExternalAddressIndex(2); - verify(keysRepository).setMaxWatchingExternalAddressIndex(10); - } -} \ No newline at end of file diff --git a/android/apollo/src/test/java/io/muun/apollo/domain/action/AddressActionsTest.kt b/android/apollo/src/test/java/io/muun/apollo/domain/action/AddressActionsTest.kt new file mode 100644 index 00000000..145f7d46 --- /dev/null +++ b/android/apollo/src/test/java/io/muun/apollo/domain/action/AddressActionsTest.kt @@ -0,0 +1,143 @@ +package io.muun.apollo.domain.action + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import io.muun.apollo.BaseTest +import io.muun.apollo.TestUtils +import io.muun.apollo.data.net.HoustonClient +import io.muun.apollo.data.preferences.KeysRepository +import io.muun.apollo.domain.action.address.CreateAddressAction +import io.muun.apollo.domain.action.address.SyncExternalAddressIndexesAction +import io.muun.apollo.template.TemplateHelpers +import io.muun.common.api.ExternalAddressesRecord +import io.muun.common.crypto.hd.PublicKeyTriple +import org.assertj.core.api.Assertions +import org.bitcoinj.core.Context +import org.bitcoinj.core.NetworkParameters +import org.bitcoinj.params.RegTestParams +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.mockito.ArgumentMatchers +import org.mockito.Mockito +import rx.Observable + +class AddressActionsTest : BaseTest() { + + private val keysRepository = mockk(relaxed = true) + + private val houstonClient = mockk(relaxed = true) + + private lateinit var syncExternalAddressIndexes: SyncExternalAddressIndexesAction + private lateinit var createAddress: CreateAddressAction + + private val networkParameters: NetworkParameters = RegTestParams.get() + + @Before + fun setUp() { + Context.propagate(Context(networkParameters)) + syncExternalAddressIndexes = SyncExternalAddressIndexesAction( + houstonClient, + keysRepository + ) + createAddress = CreateAddressAction( + keysRepository, + networkParameters, + syncExternalAddressIndexes + ) + } + + private fun toTransactionSchemeV3Address( + basePublicKeyTriple: PublicKeyTriple, + index: Int, + ): String? { + return null +// TODO mv this and ignored tests to libwallet +// final PublicKeyTriple pubKeyTriple = basePublicKeyTriple.deriveNextValidChild(index); +// return TransactionScheme.V3.createAddress(pubKeyTriple, networkParameters).getAddress(); + } + + @Test + @Ignore("ignore tests that run libwallet code for now") + fun testGetExternalAddress() { + val basePublicKeyTriple = + TemplateHelpers.externalPublicKeyTriple().generateValue() + + Mockito.doReturn(basePublicKeyTriple.toPair()).`when`(keysRepository).basePublicKeyPair + Mockito.doReturn(5).`when`(keysRepository).maxUsedExternalAddressIndex + Mockito.doReturn(10).`when`(keysRepository).maxWatchingExternalAddressIndex + + Assertions.assertThat(createAddress.actionNow().legacy.address) + .isEqualTo(toTransactionSchemeV3Address(basePublicKeyTriple, 6)) + + Mockito.verify(keysRepository).maxUsedExternalAddressIndex = 6 + Mockito.verify(syncExternalAddressIndexes).run() + } + + @Test + @Ignore("ignore tests that run libwallet code for now") + fun testGetExternalAddress_when_maxUsedIndex_is_null() { + val basePublicKeyTriple = + TemplateHelpers.externalPublicKeyTriple().generateValue() + + Mockito.doReturn(basePublicKeyTriple.toPair()).`when`(keysRepository).basePublicKeyPair + Mockito.doReturn(null).`when`(keysRepository).maxUsedExternalAddressIndex + Mockito.doReturn(10).`when`(keysRepository).maxWatchingExternalAddressIndex + + Assertions.assertThat(createAddress.actionNow().legacy.address) + .isEqualTo(toTransactionSchemeV3Address(basePublicKeyTriple, 0)) + + Mockito.verify(keysRepository).maxUsedExternalAddressIndex = 0 + } + + @Test + @Ignore("ignore tests that run libwallet code for now") + fun testGetExternalAddress_when_maxUsedIndex_is_equal_to_maxWatchingIndex() { + val basePublicKeyTriple = + TemplateHelpers.externalPublicKeyTriple().generateValue() + + Mockito.doReturn(basePublicKeyTriple.toPair()).`when`(keysRepository).basePublicKeyPair + Mockito.doReturn(3).`when`(keysRepository).maxUsedExternalAddressIndex + Mockito.doReturn(3).`when`(keysRepository).maxWatchingExternalAddressIndex + + Assertions.assertThat(createAddress.actionNow().legacy.address).isIn( + toTransactionSchemeV3Address(basePublicKeyTriple, 0), + toTransactionSchemeV3Address(basePublicKeyTriple, 1), + toTransactionSchemeV3Address(basePublicKeyTriple, 2), + toTransactionSchemeV3Address(basePublicKeyTriple, 3) + ) + + Mockito.verify(keysRepository, Mockito.never()).maxUsedExternalAddressIndex = + ArgumentMatchers.anyInt() + } + + @Test + fun syncExternalAddressesIndexes_when_maxUsedIndex_is_null() { + val record = ExternalAddressesRecord(0, 10) + + every { keysRepository.maxUsedExternalAddressIndex } returns null + every { keysRepository.maxWatchingExternalAddressIndex } returns 0 + every { houstonClient.fetchExternalAddressesRecord() } returns Observable.just(record) + + TestUtils.fetchItemFromObservable(syncExternalAddressIndexes.action()) + + verify { keysRepository setProperty "maxUsedExternalAddressIndex" value 0 } + verify { keysRepository setProperty "maxWatchingExternalAddressIndex" value 10 } + } + + @Test + fun syncExternalAddressesIndexes_when_local_maxUsedIndex_is_greater_than_remote() { + val record = ExternalAddressesRecord(0, 10) + + every { keysRepository.maxUsedExternalAddressIndex } returns 2 + every { keysRepository.maxWatchingExternalAddressIndex } returns 5 + every { houstonClient.updateExternalAddressesRecord(any(Int::class)) } returns + Observable.just(record) + + TestUtils.fetchItemFromObservable(syncExternalAddressIndexes.action()) + + verify { keysRepository setProperty "maxUsedExternalAddressIndex" value 2 } + verify { keysRepository setProperty "maxWatchingExternalAddressIndex" value 10 } + } +} \ No newline at end of file diff --git a/android/apollo/src/test/java/io/muun/apollo/domain/action/FetchRealTimeDataActionTest.kt b/android/apollo/src/test/java/io/muun/apollo/domain/action/FetchRealTimeDataActionTest.kt index eae5437f..f94061b5 100644 --- a/android/apollo/src/test/java/io/muun/apollo/domain/action/FetchRealTimeDataActionTest.kt +++ b/android/apollo/src/test/java/io/muun/apollo/domain/action/FetchRealTimeDataActionTest.kt @@ -1,5 +1,9 @@ package io.muun.apollo.domain.action +import io.mockk.confirmVerified +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify import io.muun.apollo.BaseTest import io.muun.apollo.TestUtils import io.muun.apollo.data.external.Gen @@ -15,33 +19,24 @@ import io.muun.apollo.domain.model.MuunFeature import io.muun.apollo.domain.model.RealTimeData import org.junit.Before import org.junit.Test -import org.mockito.Mock -import org.mockito.Mockito import rx.Observable -class FetchRealTimeDataActionTest: BaseTest() { +class FetchRealTimeDataActionTest : BaseTest() { - @Mock - private lateinit var exchangeRateWindowRepository: ExchangeRateWindowRepository + private val exchangeRateWindowRepository = mockk(relaxed = true) - @Mock - private lateinit var feeWindowRepository: FeeWindowRepository + private val feeWindowRepository = mockk(relaxed = true) - @Mock - private lateinit var blockchainHeightRepository: BlockchainHeightRepository + private val blockchainHeightRepository = mockk(relaxed = true) - @Mock - private lateinit var forwardingPoliciesRepository: ForwardingPoliciesRepository + private val forwardingPoliciesRepository = mockk(relaxed = true) - @Mock - private lateinit var minFeeRateRepository: MinFeeRateRepository + private val minFeeRateRepository = mockk(relaxed = true) - @Mock - private lateinit var featuresRepository: FeaturesRepository + private val featuresRepository = mockk(relaxed = true) - @Mock - private lateinit var houstonClient: HoustonClient + private val houstonClient = mockk() private lateinit var fetchRealTimeDataAction: FetchRealTimeDataAction @@ -77,16 +72,23 @@ class FetchRealTimeDataActionTest: BaseTest() { listOfFeatures ) - Mockito.doReturn(Observable.just(realTimeData)) - .`when`(houstonClient).fetchRealTimeData() + every { houstonClient.fetchRealTimeData() }.returns(Observable.just(realTimeData)) TestUtils.fetchItemFromObservable(fetchRealTimeDataAction.action()) - Mockito.verify(feeWindowRepository).store(feeWindow) - Mockito.verify(exchangeRateWindowRepository).storeLatest(exchangeRateWindow) - Mockito.verify(blockchainHeightRepository).store(blockchainHeight) - Mockito.verify(forwardingPoliciesRepository).store(forwardingPolicies) - Mockito.verify(minFeeRateRepository).store(minFeeRateInWeightUnits) - Mockito.verify(featuresRepository).store(listOfFeatures) + verify { feeWindowRepository.store(feeWindow) } + verify { exchangeRateWindowRepository.storeLatest(exchangeRateWindow) } + verify { blockchainHeightRepository.store(blockchainHeight) } + verify { forwardingPoliciesRepository.store(forwardingPolicies) } + verify { minFeeRateRepository.store(minFeeRateInWeightUnits) } + verify { featuresRepository.store(listOfFeatures) } + + confirmVerified(blockchainHeightRepository) + confirmVerified(forwardingPoliciesRepository) + confirmVerified(minFeeRateRepository) + confirmVerified(featuresRepository) + + // TODO we should test shouldSync() logic (e.g multiple syncs in short term don't fetch + // new data, but they do after threshold) } } \ No newline at end of file diff --git a/android/apollo/src/test/java/io/muun/apollo/domain/action/NotificationActionsTest.java b/android/apollo/src/test/java/io/muun/apollo/domain/action/NotificationActionsTest.java deleted file mode 100644 index 73431d20..00000000 --- a/android/apollo/src/test/java/io/muun/apollo/domain/action/NotificationActionsTest.java +++ /dev/null @@ -1,509 +0,0 @@ -package io.muun.apollo.domain.action; - -import io.muun.apollo.BaseTest; -import io.muun.apollo.TestExecutor; -import io.muun.apollo.data.external.AppStandbyBucketProvider; -import io.muun.apollo.data.external.NotificationService; -import io.muun.apollo.data.net.HoustonClient; -import io.muun.apollo.data.net.ModelObjectsMapper; -import io.muun.apollo.data.preferences.NotificationRepository; -import io.muun.apollo.domain.NotificationProcessor; -import io.muun.apollo.domain.action.base.AsyncActionStore; -import io.muun.apollo.domain.action.incoming_swap.FulfillIncomingSwapAction; -import io.muun.apollo.domain.action.operation.CreateOperationAction; -import io.muun.apollo.domain.action.operation.OperationMetadataMapper; -import io.muun.apollo.domain.action.operation.UpdateOperationAction; -import io.muun.apollo.domain.action.realtime.FetchRealTimeDataAction; -import io.muun.apollo.domain.model.NotificationReport; -import io.muun.common.Optional; -import io.muun.common.api.beam.notification.NotificationJson; -import io.muun.common.api.messages.FulfillIncomingSwapMessage; -import io.muun.common.api.messages.MessageOrigin; -import io.muun.common.api.messages.MessageSpec; -import io.muun.common.model.SessionStatus; - -import org.bitcoinj.params.TestNet3Params; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mock; -import rx.Completable; -import rx.Observable; -import rx.functions.Func2; -import rx.schedulers.Schedulers; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -public class NotificationActionsTest extends BaseTest { - - @Mock - private SigninActions signinActions; - - @Mock - private UserActions userActions; - - @Mock - private ContactActions contactActions; - - @Mock - private CreateOperationAction createOperationAction; - - @Mock - private UpdateOperationAction updateOperationAction; - - @Mock - private FetchRealTimeDataAction fetchRealTimeDataAction; - - @Mock - private NotificationRepository notificationRepository; - - @Mock - private HoustonClient houstonClient; - - @Mock - private AsyncActionStore asyncActionStore; - - @Mock - private OperationMetadataMapper operationMapper; - - @Mock - private FulfillIncomingSwapAction fulfillIncomingSwap; - - @Mock - private NotificationService notificationService; - - private final TestExecutor executor = new TestExecutor(); - - private final ModelObjectsMapper mapper = new ModelObjectsMapper( - TestNet3Params.get() - ); - - private NotificationProcessor notificationProcessor; - private NotificationActions notificationActions; - - private TestMessageHandler testMessageHandler; - private ExplodingMessageHandler explodingMessageHandler; - - private long savedLastProcessedId; - - @Before - public void setUp() { - notificationProcessor = spy(new NotificationProcessor( - updateOperationAction, - createOperationAction, - fetchRealTimeDataAction, - contactActions, - userActions, - signinActions, - mapper, - operationMapper, - fulfillIncomingSwap, - houstonClient, - notificationService)); - - testMessageHandler = spy(new TestMessageHandler()); - notificationProcessor.addHandler(TEST_SPEC, testMessageHandler); - - explodingMessageHandler = spy(new ExplodingMessageHandler()); - notificationProcessor.addHandler(EXPLODING_SPEC, explodingMessageHandler); - notificationProcessor.addHandler(FulfillIncomingSwapMessage.SPEC, explodingMessageHandler); - - // Provide realistic notificationRepository lastProcessedId, saving it to a variable: - notificationActions = spy(new NotificationActions( - notificationRepository, - houstonClient, - asyncActionStore, - notificationProcessor, - (AppStandbyBucketProvider) () -> AppStandbyBucketProvider.Bucket.ACTIVE, - Schedulers.from(executor) - )); - - savedLastProcessedId = 0; - - doAnswer(i -> savedLastProcessedId = i.getArgument(0)) - .when(notificationRepository).setLastProcessedId(anyLong()); - - doAnswer(i -> savedLastProcessedId) - .when(notificationRepository).getLastProcessedId(); - - // Fake always being logged in: - doReturn(Optional.of(SessionStatus.LOGGED_IN)).when(signinActions).getSessionStatus(); - } - - @Test - public void onNotificationReport_isAsync() { - // This test ensures the actual processing of a notification does not occur during the - // execution of onNotificationReport. - - // This is important: onNotificationReport will be invoked from a GCM handler that's - // expected to finish within a short timespan. - final NotificationJson notif = TestNotification.withId(1L); - - executor.pause(); - - notificationActions.onNotificationReport(reportOf(notif)); - verify(testMessageHandler, never()).call(any(), anyLong()); // should be asynchronous - - executor.resume(); - executor.waitUntilFinished(); - - verify(testMessageHandler, times(1)).call(notif, 0L); - } - - @Test - public void processReport_isSequential() { - final TestNotification notif1 = TestNotification.withId(1L); - final TestNotification notif2 = TestNotification.withId(2L); - - notificationActions.onNotificationReport(reportOf(notif1)); - notificationActions.onNotificationReport(reportOf(notif2)); - - executor.waitUntilFinished(); - - final InOrder inOrder = inOrder(testMessageHandler); - inOrder.verify(testMessageHandler).call(notif1, 0L); - inOrder.verify(testMessageHandler).call(notif2, 0L); - } - - @Test - public void processReport_correctDispatch() { - final NotificationJson notification = TestNotification.withId(1L); - - notificationActions.onNotificationReport(reportOf(notification)); - executor.waitUntilFinished(); - - verify(testMessageHandler, times(1)).call(notification, 0L); - } - - @Test - public void processReport_detectsGapBefore() { - final NotificationJson first = TestNotification.withId(2L); // gap before! - final NotificationJson second = TestNotification.withId(3L); - - doReturn(Observable.just(reportOf(first, second))) - .when(houstonClient).fetchNotificationReportAfter(anyLong()); - - final NotificationReport report = new NotificationReport( - first.previousId, - second.id, - Arrays.asList(first) - ); - - notificationActions.onNotificationReport(report); - executor.waitUntilFinished(); - - verify(houstonClient).fetchNotificationReportAfter(0L); - } - - @Test - public void processReport_detectsGapAfter() { - final NotificationJson first = TestNotification.withId(1L); - final NotificationJson second = TestNotification.withId(2L); - - doReturn(Observable.just(reportOf(second))) - .when(houstonClient).fetchNotificationReportAfter(anyLong()); - - final NotificationReport report = new NotificationReport( - first.previousId, - first.id + 1, // gap after! - Arrays.asList(first) - ); - - notificationActions.onNotificationReport(report); - executor.waitUntilFinished(); - - verify(houstonClient).fetchNotificationReportAfter(1L); - } - - @Test - public void processReport_detectNoGaps() { - savedLastProcessedId = 1L; - final NotificationJson first = TestNotification.withId(2L); - - final NotificationReport report = new NotificationReport( - first.previousId, - first.id, - Arrays.asList(first) - ); - - notificationActions.onNotificationReport(report); - executor.waitUntilFinished(); - - verify(houstonClient, never()).fetchNotificationReportAfter(savedLastProcessedId); - } - - @Test - public void processNotificationList_skipErrors() { - final List list = Arrays.asList( - TestNotification.withId(1L), - TestNotification.explodingWithId(2L), - TestNotification.withId(3L) // will be processed, even if previous fails - ); - - notificationActions.onNotificationReport(reportOf(list)); - executor.waitUntilFinished(); - - verify(testMessageHandler).call(list.get(0), 0L); - verify(explodingMessageHandler).call(list.get(1), 0L); - verify(testMessageHandler).call(list.get(2), 0L); - - verify(notificationRepository).setLastProcessedId(list.get(2).id); - } - - @Test - public void processNotificationList_fulfillIsNotSkippedOnError() { - - final List list = Arrays.asList( - TestNotification.withId(1L), - TestNotification.fulfillWithId(2L), - TestNotification.withId(3L) // will not be processed - ); - - notificationActions.onNotificationReport(reportOf(list)); - executor.waitUntilFinished(); - - verify(testMessageHandler).call(list.get(0), 0L); - verify(explodingMessageHandler).call(list.get(1), 0L); - verifyNoMoreInteractions(testMessageHandler); - - // Only the first notification is processed - verify(notificationRepository).setLastProcessedId(list.get(0).id); - } - - @Test - public void processNotificationList_confirmDelivery() { - - final List list = Arrays.asList( - TestNotification.withId(1L), - TestNotification.withId(2L) - ); - - notificationActions.onNotificationReport(reportOf(list)); - executor.waitUntilFinished(); - - verify(houstonClient).confirmNotificationsDeliveryUntil( - eq(list.get(1).id), any(), any(), any() - ); - } - - @Test - public void processNotification_updatesLastProcessedId() { - savedLastProcessedId = 1L; - final NotificationJson notification = TestNotification.withId(2L); - - notificationActions.onNotificationReport(reportOf(notification)); - executor.waitUntilFinished(); - - verify(notificationRepository, times(1)).setLastProcessedId(notification.id); - } - - @Test - public void onNotificationReport_fetchPaginated() { - - // Verify that we keep on fetching pages until there's no more new notifications, as given - // by the max id in the report. - - final NotificationJson first = TestNotification.withId(1L); - final NotificationJson second = TestNotification.withId(2L); - final NotificationJson third = TestNotification.withId(3L); - final NotificationJson fourth = TestNotification.withId(4L); - - doReturn(Observable.just(reportWithMaxId(4, second))) - .when(houstonClient).fetchNotificationReportAfter(eq(1L)); - - doReturn(Observable.just(reportWithMaxId(4, third))) - .when(houstonClient).fetchNotificationReportAfter(eq(2L)); - - doReturn(Observable.just(reportWithMaxId(4, fourth))) - .when(houstonClient).fetchNotificationReportAfter(eq(3L)); - - notificationActions.onNotificationReport(reportWithMaxId(4, first)); - executor.waitUntilFinished(); - - verify(houstonClient).fetchNotificationReportAfter(1L); - verify(houstonClient).fetchNotificationReportAfter(2L); - verify(houstonClient).fetchNotificationReportAfter(3L); - - verify(houstonClient).confirmNotificationsDeliveryUntil( - eq(4L), any(), any(), any() - ); - - verify(notificationRepository).setLastProcessedId(fourth.id); - } - - @Test - public void onNotificationReport_emptyPreviewWithHigherMaxCausesFetch() { - - // If we get an empty preview, we should fetch unprocessed notifications from the backend. - // This happens if our notifications are to big to fit in a FCM message. - - final NotificationJson first = TestNotification.withId(1L); - final NotificationJson second = TestNotification.withId(2L); - final NotificationJson third = TestNotification.withId(3L); - final NotificationJson fourth = TestNotification.withId(4L); - - // We return single element pages to force several pages to be fetched - doReturn(Observable.just(reportWithMaxId(4, third))) - .when(houstonClient).fetchNotificationReportAfter(eq(2L)); - - doReturn(Observable.just(reportWithMaxId(4, fourth))) - .when(houstonClient).fetchNotificationReportAfter(eq(3L)); - - // First process a normal report to seed - notificationActions.onNotificationReport(reportOf(first, second)); - notificationActions.onNotificationReport(reportWithMaxId(4)); - executor.waitUntilFinished(); - - verify(houstonClient).fetchNotificationReportAfter(2L); - verify(houstonClient).fetchNotificationReportAfter(3L); - } - - @Test - public void onNotificationReport_emptyPreviewWithSameMaxDoesNothing() { - - // If we get an empty preview with the same max id, we should do nothing. - - final NotificationJson first = TestNotification.withId(1L); - final NotificationJson second = TestNotification.withId(2L); - - // First process a normal report to seed - notificationActions.onNotificationReport(reportOf(first, second)); - notificationActions.onNotificationReport(reportWithMaxId(2)); - executor.waitUntilFinished(); - - verify(houstonClient, never()).fetchNotificationReportAfter(anyLong()); - } - - @Test - public void onNotificationReport_failureWithPagination() { - // Verify that failure to process one notification won't stop us from fetching remaining - // pages. - - final NotificationJson first = TestNotification.withId(1L); - final NotificationJson second = TestNotification.explodingWithId(2L); - final NotificationJson third = TestNotification.withId(3L); - - doReturn(Observable.just(reportWithMaxId(3, third))) - .when(houstonClient).fetchNotificationReportAfter(eq(2L)); - - notificationActions.onNotificationReport(reportWithMaxId(3, first, second)); - executor.waitUntilFinished(); - - verify(notificationRepository, times(1)).setLastProcessedId(first.id); - verify(notificationRepository, times(1)).setLastProcessedId(third.id); - } - - @Test - public void onNotificationReport_moreThanOnePageWithReport() { - - // Verify that if we get a report with a previousId that's several pages bigger than our - // max processed id, we'll fetch all the pages in-between. - - final NotificationJson first = TestNotification.withId(1L); - final NotificationJson second = TestNotification.withId(2L); - final NotificationJson third = TestNotification.withId(3L); - - doReturn(Observable.just(reportWithMaxId(3, first))) - .when(houstonClient).fetchNotificationReportAfter(eq(0L)); - doReturn(Observable.just(reportWithMaxId(3, second))) - .when(houstonClient).fetchNotificationReportAfter(eq(1L)); - doReturn(Observable.just(reportWithMaxId(3, third))) - .when(houstonClient).fetchNotificationReportAfter(eq(2L)); - - notificationActions.onNotificationReport(reportWithMaxId(3, third)); - executor.waitUntilFinished(); - - verify(houstonClient).fetchNotificationReportAfter(0L); - verify(houstonClient).fetchNotificationReportAfter(1L); - verify(houstonClient).fetchNotificationReportAfter(2L); - - verify(notificationRepository).setLastProcessedId(first.id); - verify(notificationRepository).setLastProcessedId(second.id); - verify(notificationRepository).setLastProcessedId(third.id); - } - - private static class TestMessageHandler implements Func2 { - public Completable call(NotificationJson notificationJson, Long retries) { - return Completable.complete(); - } - } - - private static class ExplodingMessageHandler - implements Func2 { - public Completable call(NotificationJson json, Long retries) { - return Completable.error(new RuntimeException()); - } - } - - private static class TestNotification extends NotificationJson { - public static TestNotification withId(long id) { - return new TestNotification(id, TEST_SPEC); - } - - public static TestNotification explodingWithId(long id) { - return new TestNotification(id, EXPLODING_SPEC); - } - - public static TestNotification fulfillWithId(long id) { - return new TestNotification(id, FulfillIncomingSwapMessage.SPEC); - } - - private TestNotification(Long id, MessageSpec spec) { - super( - id, - id - 1, - "414e27a7-a59d-44be-a0f3-41440d1de669", - "414e27a7-a59d-44be-a0f3-41440d1de669", - spec.messageType, - new HashMap(), - null - ); - } - } - - public static final MessageSpec TEST_SPEC = new MessageSpec( - "test", - SessionStatus.CREATED, - MessageOrigin.ANY - ); - - public static final MessageSpec EXPLODING_SPEC = new MessageSpec( - "BOOM!", - SessionStatus.CREATED, - MessageOrigin.ANY - ); - - private NotificationReport reportWithMaxId(long maxId, NotificationJson ...notifs) { - return new NotificationReport( - notifs.length == 0 ? maxId : notifs[0].previousId, - maxId, - Arrays.asList(notifs) - ); - } - - private NotificationReport reportOf(NotificationJson ...notifs) { - return reportOf(Arrays.asList(notifs)); - } - - private NotificationReport reportOf(List notifs) { - return new NotificationReport( - notifs.get(0).previousId, - notifs.get(notifs.size() - 1).id, - notifs - ); - } -} \ No newline at end of file diff --git a/android/apollo/src/test/java/io/muun/apollo/domain/action/NotificationActionsTest.kt b/android/apollo/src/test/java/io/muun/apollo/domain/action/NotificationActionsTest.kt new file mode 100644 index 00000000..7ebaa47b --- /dev/null +++ b/android/apollo/src/test/java/io/muun/apollo/domain/action/NotificationActionsTest.kt @@ -0,0 +1,512 @@ +package io.muun.apollo.domain.action + +import io.mockk.called +import io.mockk.confirmVerified +import io.mockk.every +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify +import io.mockk.verifySequence +import io.muun.apollo.BaseTest +import io.muun.apollo.TestExecutor +import io.muun.apollo.data.external.AppStandbyBucketProvider +import io.muun.apollo.data.external.NotificationService +import io.muun.apollo.data.net.HoustonClient +import io.muun.apollo.data.net.ModelObjectsMapper +import io.muun.apollo.data.preferences.NotificationRepository +import io.muun.apollo.domain.NotificationProcessor +import io.muun.apollo.domain.action.base.AsyncActionStore +import io.muun.apollo.domain.action.incoming_swap.FulfillIncomingSwapAction +import io.muun.apollo.domain.action.operation.CreateOperationAction +import io.muun.apollo.domain.action.operation.OperationMetadataMapper +import io.muun.apollo.domain.action.operation.UpdateOperationAction +import io.muun.apollo.domain.action.realtime.FetchRealTimeDataAction +import io.muun.apollo.domain.model.NotificationReport +import io.muun.common.Optional +import io.muun.common.api.beam.notification.NotificationJson +import io.muun.common.api.messages.FulfillIncomingSwapMessage +import io.muun.common.api.messages.MessageOrigin +import io.muun.common.api.messages.MessageSpec +import io.muun.common.model.SessionStatus +import org.bitcoinj.params.TestNet3Params +import org.junit.Before +import org.junit.Test +import rx.Completable +import rx.Observable +import rx.functions.Func2 +import rx.schedulers.Schedulers + +class NotificationActionsTest : BaseTest() { + + private val signinActions = mockk(relaxed = true) + private val contactActions = mockk(relaxed = true) + private val userActions = mockk(relaxed = true) + private val createOperationAction = mockk(relaxed = true) + private val updateOperationAction = mockk(relaxed = true) + private val fetchRealTimeDataAction = mockk(relaxed = true) + private val notificationRepository = mockk(relaxed = true) + private val asyncActionStore = mockk(relaxed = true) + private val operationMapper = mockk(relaxed = true) + private val fulfillIncomingSwap = mockk(relaxed = true) + private val notificationService = mockk(relaxed = true) + + private val houstonClient = mockk(relaxed = true) + + private val executor = TestExecutor() + private val mapper = ModelObjectsMapper(TestNet3Params.get()) + + private lateinit var notificationActions: NotificationActions + private lateinit var testMessageHandler: TestMessageHandler + private lateinit var explodingMessageHandler: ExplodingMessageHandler + + private var savedLastProcessedId: Long = 0 + + @Before + fun setUp() { + + every { + houstonClient.confirmNotificationsDeliveryUntil(any(), any(), any(), any()) + } returns + Observable.just(null) + + val notificationProcessor = NotificationProcessor( + updateOperationAction, + createOperationAction, + fetchRealTimeDataAction, + contactActions, + userActions, + signinActions, + mapper, + operationMapper, + fulfillIncomingSwap, + houstonClient, + notificationService + ) + testMessageHandler = spyk(TestMessageHandler()) + notificationProcessor.addHandler(TEST_SPEC, testMessageHandler) + explodingMessageHandler = spyk(ExplodingMessageHandler()) + notificationProcessor.addHandler(EXPLODING_SPEC, explodingMessageHandler) + notificationProcessor.addHandler(FulfillIncomingSwapMessage.SPEC, explodingMessageHandler) + + // Provide realistic notificationRepository lastProcessedId, saving it to a variable: + notificationActions = spyk( + NotificationActions( + notificationRepository, + houstonClient, + asyncActionStore, + notificationProcessor, + { AppStandbyBucketProvider.Bucket.ACTIVE }, + Schedulers.from(executor) + ) + ) + savedLastProcessedId = 0 + + every { + notificationRepository setProperty "lastProcessedId" value any(Long::class) + } answers { + savedLastProcessedId = firstArg() + } + + every { notificationRepository.lastProcessedId } answers { + savedLastProcessedId + } +// every { notificationRepository getProperty "lastProcessedId" } propertyType Long::class answers { +// savedLastProcessedId +// } + + // Fake always being logged in: + every { signinActions.sessionStatus } returns Optional.of(SessionStatus.LOGGED_IN) + } + + @Test + fun onNotificationReport_isAsync() { + // This test ensures the actual processing of a notification does not occur during the + // execution of onNotificationReport. + + // This is important: onNotificationReport will be invoked from a GCM handler that's + // expected to finish within a short timespan. + val notif: NotificationJson = TestNotification.withId(1L) + executor.pause() + notificationActions.onNotificationReport(reportOf(notif)) + + verify { testMessageHandler wasNot called } + + executor.resume() + executor.waitUntilFinished() + + verify(exactly = 1) { testMessageHandler.call(notif, 0L) } + } + + @Test + fun processReport_isSequential() { + val notif1 = TestNotification.withId(1L) + val notif2 = TestNotification.withId(2L) + + notificationActions.onNotificationReport(reportOf(notif1)) + notificationActions.onNotificationReport(reportOf(notif2)) + + executor.waitUntilFinished() + + verifySequence { + testMessageHandler.call(notif1, any()) + testMessageHandler.call(notif2, any()) + } + } + + @Test + fun processReport_correctDispatch() { + val notification: NotificationJson = TestNotification.withId(1L) + + notificationActions.onNotificationReport(reportOf(notification)) + executor.waitUntilFinished() + + verify(exactly = 1) { testMessageHandler.call(notification, 0L) } + } + + @Test + fun processReport_detectsGapBefore() { + val first: NotificationJson = TestNotification.withId(2L) // gap before! + val second: NotificationJson = TestNotification.withId(3L) + + every { + houstonClient.fetchNotificationReportAfter(any(Long::class)) + } returns + Observable.just(reportOf(first, second)) + + val report = NotificationReport(first.previousId, second.id, listOf(first)) + notificationActions.onNotificationReport(report) + executor.waitUntilFinished() + + verify(exactly = 1) { houstonClient.fetchNotificationReportAfter(0L) } + } + + @Test + fun processReport_detectsGapAfter() { + val first: NotificationJson = TestNotification.withId(1L) + val second: NotificationJson = TestNotification.withId(2L) + + every { + houstonClient.fetchNotificationReportAfter(any(Long::class)) + } returns + Observable.just(reportOf(second)) + + val report = NotificationReport(first.previousId, first.id + 1, listOf(first)) + notificationActions.onNotificationReport(report) + executor.waitUntilFinished() + + verify(exactly = 1) { houstonClient.fetchNotificationReportAfter(1L) } + } + + @Test + fun processReport_detectNoGaps() { + savedLastProcessedId = 1L + val first: NotificationJson = TestNotification.withId(2L) + val report = NotificationReport(first.previousId, first.id, listOf(first)) + + notificationActions.onNotificationReport(report) + executor.waitUntilFinished() + + + verify(exactly = 0) { houstonClient.fetchNotificationReportAfter(savedLastProcessedId) } + } + + @Test + fun processNotificationList_skipErrors() { + val list = listOf( + TestNotification.withId(1L), + TestNotification.explodingWithId(2L), + TestNotification.withId(3L) // will be processed, even if previous fails + ) + + notificationActions.onNotificationReport(reportOf(list)) + executor.waitUntilFinished() + + + verify(exactly = 1) { testMessageHandler.call(list[0], 0L) } + verify(exactly = 1) { explodingMessageHandler.call(list[1], 0L) } + verify(exactly = 1) { testMessageHandler.call(list[2], 0L) } + verify(exactly = 1) { + notificationRepository setProperty "lastProcessedId" value list[2].id + } + } + + @Test + fun processNotificationList_fulfillIsNotSkippedOnError() { + val list = listOf( + TestNotification.withId(1L), + TestNotification.fulfillWithId(2L), + TestNotification.withId(3L) // will not be processed + ) + + notificationActions.onNotificationReport(reportOf(list)) + executor.waitUntilFinished() + + verify(exactly = 1) { testMessageHandler.call(list[0], 0L) } + verify(exactly = 1) { explodingMessageHandler.call(list[1], 0L) } + + confirmVerified(testMessageHandler) + + // Only the first notification is processed + verify(exactly = 1) { + notificationRepository setProperty "lastProcessedId" value list[0].id + } + } + + @Test + fun processNotificationList_confirmDelivery() { + val list = listOf( + TestNotification.withId(1L), + TestNotification.withId(2L) + ) + + notificationActions.onNotificationReport(reportOf(list)) + executor.waitUntilFinished() + + verify { + houstonClient.confirmNotificationsDeliveryUntil( + list[1].id, + any(), + any(), + any() + ) + } + } + + @Test + fun processNotification_updatesLastProcessedId() { + savedLastProcessedId = 1L + val notification: NotificationJson = TestNotification.withId(2L) + + notificationActions.onNotificationReport(reportOf(notification)) + executor.waitUntilFinished() + + verify(exactly = 1) { + notificationRepository setProperty "lastProcessedId" value notification.id + } + } + + @Test + fun onNotificationReport_fetchPaginated() { + + // Verify that we keep on fetching pages until there's no more new notifications, as given + // by the max id in the report. + val first: NotificationJson = TestNotification.withId(1L) + val second: NotificationJson = TestNotification.withId(2L) + val third: NotificationJson = TestNotification.withId(3L) + val fourth: NotificationJson = TestNotification.withId(4L) + + every { + houstonClient.fetchNotificationReportAfter(1L) + } returns + Observable.just(reportWithMaxId(4, second)) + + every { + houstonClient.fetchNotificationReportAfter(2L) + } returns + Observable.just(reportWithMaxId(4, third)) + + every { + houstonClient.fetchNotificationReportAfter(3L) + } returns + Observable.just(reportWithMaxId(4, fourth)) + + notificationActions.onNotificationReport(reportWithMaxId(4, first)) + executor.waitUntilFinished() + + verify(exactly = 1) { houstonClient.fetchNotificationReportAfter(1L) } + verify(exactly = 1) { houstonClient.fetchNotificationReportAfter(2L) } + verify(exactly = 1) { houstonClient.fetchNotificationReportAfter(3L) } + verify { + houstonClient.confirmNotificationsDeliveryUntil( + 4L, + any(), + any(), + any() + ) + } + verify(exactly = 1) { + notificationRepository setProperty "lastProcessedId" value fourth.id + } + } + + @Test + fun onNotificationReport_emptyPreviewWithHigherMaxCausesFetch() { + + // If we get an empty preview, we should fetch unprocessed notifications from the backend. + // This happens if our notifications are to big to fit in a FCM message. + val first: NotificationJson = TestNotification.withId(1L) + val second: NotificationJson = TestNotification.withId(2L) + val third: NotificationJson = TestNotification.withId(3L) + val fourth: NotificationJson = TestNotification.withId(4L) + + // We return single element pages to force several pages to be fetched + + every { + houstonClient.fetchNotificationReportAfter(2L) + } returns + Observable.just(reportWithMaxId(4, third)) + + every { + houstonClient.fetchNotificationReportAfter(3L) + } returns + Observable.just(reportWithMaxId(4, fourth)) + + // First process a normal report to seed + notificationActions.onNotificationReport(reportOf(first, second)) + notificationActions.onNotificationReport(reportWithMaxId(4)) + executor.waitUntilFinished() + + verify(exactly = 1) { houstonClient.fetchNotificationReportAfter(2L) } + verify(exactly = 1) { houstonClient.fetchNotificationReportAfter(3L) } + } + + @Test + fun onNotificationReport_emptyPreviewWithSameMaxDoesNothing() { + + // If we get an empty preview with the same max id, we should do nothing. + val first: NotificationJson = TestNotification.withId(1L) + val second: NotificationJson = TestNotification.withId(2L) + + // First process a normal report to seed + notificationActions.onNotificationReport(reportOf(first, second)) + notificationActions.onNotificationReport(reportWithMaxId(2)) + executor.waitUntilFinished() + + verify(exactly = 0) { houstonClient.fetchNotificationReportAfter(any(Long::class)) } + } + + @Test + fun onNotificationReport_failureWithPagination() { + // Verify that failure to process one notification won't stop us from fetching remaining + // pages. + val first: NotificationJson = TestNotification.withId(1L) + val second: NotificationJson = TestNotification.explodingWithId(2L) + val third: NotificationJson = TestNotification.withId(3L) + + every { + houstonClient.fetchNotificationReportAfter(2L) + } returns + Observable.just(reportWithMaxId(3, third)) + + notificationActions.onNotificationReport(reportWithMaxId(3, first, second)) + executor.waitUntilFinished() + + + verify(exactly = 1) { + notificationRepository setProperty "lastProcessedId" value first.id + } + verify(exactly = 1) { + notificationRepository setProperty "lastProcessedId" value third.id + } + } + + @Test + fun onNotificationReport_moreThanOnePageWithReport() { + + // Verify that if we get a report with a previousId that's several pages bigger than our + // max processed id, we'll fetch all the pages in-between. + val first: NotificationJson = TestNotification.withId(1L) + val second: NotificationJson = TestNotification.withId(2L) + val third: NotificationJson = TestNotification.withId(3L) + + every { + houstonClient.fetchNotificationReportAfter(0L) + } returns + Observable.just(reportWithMaxId(3, first)) + every { + houstonClient.fetchNotificationReportAfter(1L) + } returns + Observable.just(reportWithMaxId(3, second)) + every { + houstonClient.fetchNotificationReportAfter(2L) + } returns + Observable.just(reportWithMaxId(3, third)) + + notificationActions.onNotificationReport(reportWithMaxId(3, third)) + executor.waitUntilFinished() + + verify(exactly = 1) { houstonClient.fetchNotificationReportAfter(0L) } + verify(exactly = 1) { houstonClient.fetchNotificationReportAfter(1L) } + verify(exactly = 1) { houstonClient.fetchNotificationReportAfter(2L) } + verify(exactly = 1) { + notificationRepository setProperty "lastProcessedId" value first.id + } + verify(exactly = 1) { + notificationRepository setProperty "lastProcessedId" value second.id + } + verify(exactly = 1) { + notificationRepository setProperty "lastProcessedId" value third.id + } + } + + private class TestMessageHandler : Func2 { + override fun call(notificationJson: NotificationJson, retries: Long): Completable { + return Completable.complete() + } + } + + private class ExplodingMessageHandler : Func2 { + override fun call(json: NotificationJson, retries: Long): Completable { + return Completable.error(RuntimeException()) + } + } + + private class TestNotification private constructor(id: Long, spec: MessageSpec) : + NotificationJson( + id, + id - 1, + "414e27a7-a59d-44be-a0f3-41440d1de669", + "414e27a7-a59d-44be-a0f3-41440d1de669", + spec.messageType, + HashMap(), + null + ) { + + companion object { + fun withId(id: Long): TestNotification { + return TestNotification(id, TEST_SPEC) + } + + fun explodingWithId(id: Long): TestNotification { + return TestNotification(id, EXPLODING_SPEC) + } + + fun fulfillWithId(id: Long): TestNotification { + return TestNotification(id, FulfillIncomingSwapMessage.SPEC) + } + } + } + + private fun reportWithMaxId(maxId: Long, vararg notifs: NotificationJson): NotificationReport { + return NotificationReport( + if (notifs.isEmpty()) maxId else notifs[0].previousId, + maxId, + listOf(*notifs) + ) + } + + private fun reportOf(vararg notifs: NotificationJson): NotificationReport { + return reportOf(listOf(*notifs)) + } + + private fun reportOf(notifs: List): NotificationReport { + return NotificationReport( + notifs[0].previousId, + notifs[notifs.size - 1].id, + notifs + ) + } + + companion object { + val TEST_SPEC = MessageSpec( + "test", + SessionStatus.CREATED, + MessageOrigin.ANY + ) + val EXPLODING_SPEC = MessageSpec( + "BOOM!", + SessionStatus.CREATED, + MessageOrigin.ANY + ) + } +} \ No newline at end of file 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 deleted file mode 100644 index bea2283b..00000000 --- a/android/apollo/src/test/java/io/muun/apollo/domain/action/OperationActionsTest.java +++ /dev/null @@ -1,290 +0,0 @@ -package io.muun.apollo.domain.action; - -import io.muun.apollo.BaseTest; -import io.muun.apollo.data.db.operation.OperationDao; -import io.muun.apollo.data.db.public_profile.PublicProfileDao; -import io.muun.apollo.data.net.HoustonClient; -import io.muun.apollo.data.preferences.KeysRepository; -import io.muun.apollo.domain.action.operation.CreateOperationAction; -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.Operation; -import io.muun.apollo.domain.model.PaymentRequest; -import io.muun.apollo.domain.model.PreparedPayment; -import io.muun.apollo.domain.model.tx.PartiallySignedTransaction; -import io.muun.apollo.template.TemplateHelpers; -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.OperationDirection; - -import androidx.core.util.Pair; -import br.com.six2six.fixturefactory.Fixture; -import org.bitcoinj.core.Transaction; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.mockito.Mock; -import rx.Completable; -import rx.Observable; - -import java.util.List; - -import static io.muun.apollo.TestUtils.fetchItemFromObservable; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -@Ignore -public class OperationActionsTest extends BaseTest { - - private static final String[] ALL_LABELS = {"incoming internal", "outgoing internal"}; - - @Mock - private OperationDao operationDao; - - @Mock - private PublicProfileDao publicProfileDao; - - @Mock - private KeysRepository keysRepository; - - @Mock - private ContactActions contactActions; - - @Mock - private HoustonClient houstonClient; - - @Mock - private CreateOperationAction createOperationAction; - - @Mock - private SubmitPaymentAction submitPaymentAction; - - @Mock - private OperationMetadataMapper operationMapper; - - private OperationActions operationActions; - - @Before - public void setUp() { - - operationActions = spy(new OperationActions( - createOperationAction, - operationDao, - houstonClient, - operationMapper - )); - } - - @Test - public void fetchReplaceOperations() { - - final List remote = Fixture.from(Operation.class).gimme(2, ALL_LABELS); - - doReturn(Observable.just(remote)) - .when(houstonClient).fetchOperations(); - - doReturn(Completable.complete()) - .when(operationDao).deleteAll(); - - fetchItemFromObservable(operationActions.fetchReplaceOperations()); - - verify(createOperationAction, times(remote.size())) - .saveOperation(argThat(remote::contains)); - } - - @Test - public void buildPaymentToContact() { - - final Contact contact = Fixture.from(Contact.class).gimme("valid"); - final PublicKey publicKey = contact.publicKey; - final MuunAddress contactAddress = new MuunAddress( - TemplateHelpers.address().generateValue(), - publicKey.getAbsoluteDerivationPath() + "/" + publicKey.getLastLevelIndex() - ); - - final long someFee = 123456; - - final PaymentRequest payReq = PaymentRequest.toContact( - contact - ); - - doReturn(contact.publicProfile) - .when(publicProfileDao).fetchOneByHid(contact.getHid()); - - doReturn(contactAddress) - .when(contactActions).getAddressForContact(contact); - - final PreparedPayment preparedPayment = new PreparedPayment( - null, - null, - null, - null, - null, - PaymentRequest.Type.TO_CONTACT, - contact, - null, - null - ); - final Operation operation = - submitPaymentAction.buildOperation(preparedPayment); - - // check direction - assertThat(operation.direction).isEqualTo(OperationDirection.OUTGOING); - assertThat(operation.isExternal).isFalse(); - assertThat(operation.senderIsExternal).isFalse(); - assertThat(operation.receiverIsExternal).isFalse(); - - // check receiver address - assertThat(operation.receiverAddress).isEqualTo(contactAddress.getAddress()); - assertThat(operation.receiverAddressDerivationPath).isEqualTo( - contactAddress.getDerivationPath() - ); - - // check fee - assertThat(operation.fee.inSatoshis).isEqualTo(someFee); - } - - @Test - public void buildPaymentToAddress() { - - final PaymentRequest payReq = PaymentRequest.toAddress( - TemplateHelpers.address().generateValue() - ); - - final long someFee = 123456; - - final PreparedPayment preparedPayment = new PreparedPayment( - null, - null, - null, - null, - null, - PaymentRequest.Type.TO_ADDRESS, - null, - payReq.getAddress(), - null - ); - final Operation operation = submitPaymentAction.buildOperation(preparedPayment); - - // check direction - assertThat(operation.direction).isEqualTo(OperationDirection.OUTGOING); - assertThat(operation.isExternal).isTrue(); - assertThat(operation.senderIsExternal).isFalse(); - assertThat(operation.receiverIsExternal).isTrue(); - - // check receiver address - assertThat(operation.receiverAddress).isEqualTo(payReq.getAddress()); - assertThat(operation.receiverAddressDerivationPath).isEqualTo(null); - - // check fee - assertThat(operation.fee.inSatoshis).isEqualTo(someFee); - } - - @Test - public void submitNewOperation() { - - final PrivateKey privateKey = TemplateHelpers.privateKey().generateValue(); - - final Operation operation = Fixture.from(Operation.class).gimme("outgoing internal"); - final Operation createdOperation = shallowCopy(operation); - - // On Apollo's side, before submitting, operation HID is NO_HID: - // TODO we need Kotlin data classes and their copy method and optional params!! - operation.mergeWithUpdate(new Operation( - operation.getId(), - Operation.NO_HID, - operation.direction, - operation.isExternal, - operation.senderProfile, - operation.senderIsExternal, - operation.receiverProfile, - operation.receiverIsExternal, - operation.receiverAddress, - operation.receiverAddressDerivationPath, - operation.amount, - operation.fee, - operation.confirmations, - operation.hash, - operation.description, - operation.metadata, - operation.status, - operation.creationDate, - operation.exchangeRateWindowHid, - operation.swap, - operation.incomingSwap, - operation.isRbf - )); - - final PartiallySignedTransaction unsignedTx = mock(PartiallySignedTransaction.class); - - final Pair newOperationRes = new Pair<>( - createdOperation, - unsignedTx - ); - - final Transaction signedTx = mock(Transaction.class); - final String hash = TemplateHelpers.hash256().generateValue(); - - doReturn(hash) - .when(signedTx).getHashAsString(); - - doReturn(Observable.just(privateKey)) - .when(keysRepository).getBasePrivateKey(); - -// doReturn(Observable.just(newOperationRes)) -// .when(houstonClient).newOperation(any(), any(), any()); - -// doReturn(signedTx) -// .when(bitcoinActions).signTransaction(privateKey, unsignedTx); - - doReturn(Observable.just(null)) - .when(houstonClient) - .pushTransaction(signedTx.getHashAsString(), createdOperation.getHid()); - - //doReturn(Observable.just(null)) - // .when(operationActions).onRemoteOperationCreated(operation, - // transactionSizeRepository.getNextTransactionSize()); - - //fetchItemFromObservable(operationActions.submitPayment(operation)); - - assertThat(operation.getHid()).isEqualTo(createdOperation.getHid()); - assertThat(operation.hash).isEqualTo(hash); -// assertThat(operation.status).isEqualTo(OperationStatus.SIGNED); - } - - private Operation shallowCopy(Operation source) { - return new Operation( - source.getId(), - source.getHid(), - source.direction, - source.isExternal, - source.senderProfile, - source.senderIsExternal, - source.receiverProfile, - source.receiverIsExternal, - source.receiverAddress, - source.receiverAddressDerivationPath, - source.amount, - source.fee, - source.confirmations, - source.hash, - source.description, - source.metadata, - source.status, - source.creationDate, - source.exchangeRateWindowHid, - source.swap, - source.incomingSwap, - source.isRbf - ); - } - - -} \ No newline at end of file diff --git a/android/apollo/src/test/java/io/muun/apollo/domain/action/incoming_swap/FulfillIncomingSwapActionTest.kt b/android/apollo/src/test/java/io/muun/apollo/domain/action/incoming_swap/FulfillIncomingSwapActionTest.kt index 0d3f7d77..a5cf06fb 100644 --- a/android/apollo/src/test/java/io/muun/apollo/domain/action/incoming_swap/FulfillIncomingSwapActionTest.kt +++ b/android/apollo/src/test/java/io/muun/apollo/domain/action/incoming_swap/FulfillIncomingSwapActionTest.kt @@ -1,6 +1,11 @@ package io.muun.apollo.domain.action.incoming_swap -import com.nhaarman.mockitokotlin2.* +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify import io.muun.apollo.BaseTest import io.muun.apollo.data.db.incoming_swap.IncomingSwapDao import io.muun.apollo.data.db.operation.OperationDao @@ -11,7 +16,6 @@ import io.muun.apollo.data.preferences.KeysRepository import io.muun.apollo.domain.libwallet.errors.UnfulfillableIncomingSwapError import io.muun.apollo.domain.model.IncomingSwap import io.muun.apollo.domain.model.IncomingSwapFulfillmentData -import io.muun.apollo.domain.model.Operation import io.muun.apollo.domain.model.Preimage import io.muun.common.api.error.ErrorCode import io.muun.common.crypto.hd.PrivateKey @@ -21,29 +25,19 @@ import io.muun.common.utils.RandomGenerator import org.bitcoinj.params.RegTestParams import org.junit.Before import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.verify -import org.mockito.junit.MockitoJUnitRunner import rx.Completable import rx.Observable import rx.Single -@RunWith(MockitoJUnitRunner::class) -class FulfillIncomingSwapActionTest: BaseTest() { +class FulfillIncomingSwapActionTest : BaseTest() { - @Mock - private lateinit var houstonClient: HoustonClient + private val houstonClient = mockk(relaxed = true) - @Mock - private lateinit var operationDao: OperationDao + private val operationDao = mockk(relaxed = true) - @Mock - private lateinit var keysRepository: KeysRepository + private val keysRepository = mockk(relaxed = true) - @Mock - private lateinit var incomingSwapDao: IncomingSwapDao + private val incomingSwapDao = mockk(relaxed = true) private lateinit var action: FulfillIncomingSwapAction @@ -51,21 +45,25 @@ class FulfillIncomingSwapActionTest: BaseTest() { @Before fun setUp() { - whenever(Globals.INSTANCE.network).thenReturn(params) - whenever(keysRepository.basePrivateKey) - .thenReturn(Observable.just(PrivateKey.getNewRootPrivateKey(params))) + // Required until we kotlinize BaseTest + // TODO: kotlinize BaseTest and remove this + Globals.INSTANCE = mockk() - whenever(keysRepository.baseMuunPublicKey) - .thenReturn(PrivateKey.getNewRootPrivateKey(params).publicKey) + every { Globals.INSTANCE.network } returns params - whenever(incomingSwapDao.store(any())) - .then { invocation -> Observable.just( - invocation.getArgument(0) - ) } + every { + keysRepository.basePrivateKey + } returns + Observable.just(PrivateKey.getNewRootPrivateKey(params)) - whenever(operationDao.store(any())) - .then { invocation -> Observable.just(invocation.getArgument(0)) } + every { + keysRepository.baseMuunPublicKey + } returns + PrivateKey.getNewRootPrivateKey(params).publicKey + + every { incomingSwapDao.store(any()) } answers { Observable.just(firstArg()) } + every { operationDao.store(any()) } answers { Observable.just(firstArg()) } action = FulfillIncomingSwapAction( houstonClient, @@ -78,41 +76,49 @@ class FulfillIncomingSwapActionTest: BaseTest() { @Test(expected = java.lang.RuntimeException::class) fun unknownSwap() { - whenever(operationDao.fetchByIncomingSwapUuid(eq("fake"))) - .thenReturn(Observable.error(RuntimeException())) + every { + operationDao.fetchByIncomingSwapUuid("fake") + } returns + Observable.error(RuntimeException()) action.actionNow("fake") } @Test fun unfulfillableSwap() { - val swap = Mockito.spy(Gen.incomingSwap()) + val swap = spyk(Gen.incomingSwap()) val operation = Gen.operation(swap) - whenever(operationDao.fetchByIncomingSwapUuid(eq(swap.houstonUuid))) - .thenReturn(Observable.just(operation)) + every { + operationDao.fetchByIncomingSwapUuid(swap.houstonUuid) + } returns + Observable.just(operation) - doThrow(UnfulfillableIncomingSwapError(swap.houstonUuid, java.lang.RuntimeException())) - .`when`(swap).verifyFulfillable(any(), eq(params)) + every { + swap.verifyFulfillable(any(), params) + } throws + UnfulfillableIncomingSwapError(swap.houstonUuid, java.lang.RuntimeException()) - whenever(houstonClient.fetchFulfillmentData(eq(swap.houstonUuid))) - .thenReturn(Single.just(null)) + every { + houstonClient.fetchFulfillmentData(swap.houstonUuid) + } returns + Single.just(null) - whenever(houstonClient.expireInvoice(eq(swap.getPaymentHash()))) - .thenReturn(Completable.complete()) + every { + houstonClient.expireInvoice(swap.getPaymentHash()) + } returns + Completable.complete() action.action(swap.houstonUuid).toBlocking().subscribe() - verify(houstonClient).expireInvoice(eq(swap.getPaymentHash())) - verify(houstonClient, never()).pushFulfillmentTransaction( - any(), any() - ) + verify(exactly = 1) { houstonClient.expireInvoice(swap.getPaymentHash()) } + verify(exactly = 0) { houstonClient.pushFulfillmentTransaction(any(), any()) } } @Test fun onchainFulfillment() { val preimage = RandomGenerator.getBytes(32) - val swap = Mockito.spy(Gen.incomingSwap()) + val swap = spyk(Gen.incomingSwap()) val operation = Gen.operation(swap) val fulfillmentData = IncomingSwapFulfillmentData( ByteArray(0), @@ -121,82 +127,85 @@ class FulfillIncomingSwapActionTest: BaseTest() { 1 ) - whenever(operationDao.fetchByIncomingSwapUuid(eq(swap.houstonUuid))) - .thenReturn(Observable.just(operation)) + every { + operationDao.fetchByIncomingSwapUuid(swap.houstonUuid) + } returns + Observable.just(operation) - doNothing().whenever(swap).verifyFulfillable( - any(), - eq(params) - ) + every { swap.verifyFulfillable(any(), params) } just Runs - whenever(houstonClient.fetchFulfillmentData(eq(swap.houstonUuid))) - .thenReturn(Single.just(fulfillmentData)) + every { + houstonClient.fetchFulfillmentData(swap.houstonUuid) + } returns + Single.just(fulfillmentData) - doReturn(IncomingSwap.FulfillmentResult(ByteArray(0), Preimage.fromBytes(preimage))) - .`when`(swap).fulfill(eq(fulfillmentData), any(), any(), eq(params)) + every { + swap.fulfill(fulfillmentData, any(), any(), params) + } returns + IncomingSwap.FulfillmentResult(ByteArray(0), Preimage.fromBytes(preimage)) - whenever(houstonClient.pushFulfillmentTransaction(eq(swap.houstonUuid), any())) - .thenReturn(Completable.complete()) + every { + houstonClient.pushFulfillmentTransaction(swap.houstonUuid, any()) + } returns + Completable.complete() action.action(swap.houstonUuid).toBlocking().subscribe() - verify(houstonClient, never()).expireInvoice(eq(swap.getPaymentHash())) - verify(houstonClient).pushFulfillmentTransaction( - eq(swap.houstonUuid), any() - ) + verify(exactly = 0) { houstonClient.expireInvoice(swap.getPaymentHash()) } + verify(exactly = 1) { houstonClient.pushFulfillmentTransaction(any(), any()) } } @Test fun fullDebtFulfilment() { val preimage = RandomGenerator.getBytes(32) - val swap = Mockito.spy(Gen.incomingSwap(paymentHash = Hashes.sha256(preimage), htlc = null)) + val swap = spyk(Gen.incomingSwap(paymentHash = Hashes.sha256(preimage), htlc = null)) val operation = Gen.operation(swap) - whenever(operationDao.fetchByIncomingSwapUuid(eq(swap.houstonUuid))) - .thenReturn(Observable.just(operation)) + every { + operationDao.fetchByIncomingSwapUuid(swap.houstonUuid) + } returns + Observable.just(operation) - doNothing().whenever(swap).verifyFulfillable( - any(), - eq(params) - ) + every { swap.verifyFulfillable(any(), params) } just Runs - doReturn(IncomingSwap.FulfillmentResult(byteArrayOf(), Preimage.fromBytes(preimage))) - .`when`(swap).fulfillFullDebt() + every { + swap.fulfillFullDebt() + } returns + IncomingSwap.FulfillmentResult(byteArrayOf(), Preimage.fromBytes(preimage)) - whenever(houstonClient.fulfillIncomingSwap(eq(swap.houstonUuid), eq(preimage))) - .thenReturn(Completable.complete()) + every { + houstonClient.fulfillIncomingSwap(swap.houstonUuid, preimage) + } returns + Completable.complete() action.action(swap.houstonUuid).toBlocking().subscribe() - verify(houstonClient).fulfillIncomingSwap(eq(swap.houstonUuid), eq(preimage)) - verify(houstonClient, never()).expireInvoice(eq(swap.getPaymentHash())) - verify(houstonClient, never()).fetchFulfillmentData(eq(swap.houstonUuid)) - verify(houstonClient, never()).pushFulfillmentTransaction( - eq(swap.houstonUuid), any() - ) + verify(exactly = 1) { houstonClient.fulfillIncomingSwap(swap.houstonUuid, preimage) } + verify(exactly = 0) { houstonClient.expireInvoice(swap.getPaymentHash()) } + verify(exactly = 0) { houstonClient.fetchFulfillmentData(swap.houstonUuid) } + verify(exactly = 0) { houstonClient.pushFulfillmentTransaction(swap.houstonUuid, any()) } } @Test fun onchainAlreadyFulfilled() { - val swap = Mockito.spy(Gen.incomingSwap()) + val swap = spyk(Gen.incomingSwap()) val operation = Gen.operation(swap) - whenever(operationDao.fetchByIncomingSwapUuid(eq(swap.houstonUuid))) - .thenReturn(Observable.just(operation)) + every { + operationDao.fetchByIncomingSwapUuid(swap.houstonUuid) + } returns + Observable.just(operation) - doNothing().whenever(swap).verifyFulfillable( - any(), - eq(params) - ) + every { swap.verifyFulfillable(any(), params) } just Runs - whenever(houstonClient.fetchFulfillmentData(eq(swap.houstonUuid))) - .thenReturn(Single.error(HttpException(ErrorCode.INCOMING_SWAP_ALREADY_FULFILLED))) + every { + houstonClient.fetchFulfillmentData(swap.houstonUuid) + } returns + Single.error(HttpException(ErrorCode.INCOMING_SWAP_ALREADY_FULFILLED)) action.action(swap.houstonUuid).toBlocking().subscribe() - verify(houstonClient, never()).expireInvoice(eq(swap.getPaymentHash())) - verify(houstonClient, never()).pushFulfillmentTransaction( - eq(swap.houstonUuid), any() - ) + verify(exactly = 0) { houstonClient.expireInvoice(swap.getPaymentHash()) } + verify(exactly = 0) { houstonClient.pushFulfillmentTransaction(swap.houstonUuid, any()) } } } \ No newline at end of file diff --git a/android/apolloui/build.gradle b/android/apolloui/build.gradle index 5dc350d6..e1599e6a 100644 --- a/android/apolloui/build.gradle +++ b/android/apolloui/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.nav_version = "2.4.1" + ext.nav_version = "2.7.7" // Next version upgrade requires bump minSdk to 21 dependencies { // Add third party gradle plugins classpath 'com.google.gms:google-services:4.3.15' // Google Services plugin @@ -10,8 +10,6 @@ buildscript { } plugins { - id 'com.github.ben-manes.versions' version '0.15.0' - id 'org.owasp.dependencycheck' version '5.3.2.1' id 'org.jetbrains.kotlin.plugin.serialization' id 'org.jetbrains.kotlin.android' } @@ -84,6 +82,8 @@ static def configExternalLinks(productFlavor, String host) { android { + namespace "io.muun.apollo" + compileSdk 34 buildFeatures { @@ -94,8 +94,8 @@ android { applicationId "io.muun.apollo" minSdk 19 targetSdk 34 - versionCode 1202 - versionName "52.2" + versionCode 1203 + versionName "52.3" // 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/ @@ -332,6 +332,8 @@ android { ext { version_butterknife = '10.1.0' + version_mockk = '1.13.7' // Latest version targeting kotlin 1.8.20 + version_lifecycle = '2.6.2' // Upcoming versions require AGP 8.3.1 } dependencies { @@ -368,8 +370,12 @@ dependencies { // We need to specify versions for customtabs and cardview because some dependencies use them implementation 'androidx.browser:browser:1.0.0' implementation 'androidx.cardview:cardview:1.0.0' - implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" - implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1" + + // Lifecycles only (without ViewModel or LiveData) + implementation "androidx.lifecycle:lifecycle-runtime:$version_lifecycle" + // ProcessLifecycleOwner provides a lifecycle for the whole application process + implementation "androidx.lifecycle:lifecycle-process:$version_lifecycle" + // for WebView implementation 'androidx.webkit:webkit:1.1.0' @@ -395,6 +401,8 @@ dependencies { testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0" testImplementation 'org.assertj:assertj-core:3.8.0' testImplementation 'junit:junit:4.12' + testImplementation "io.mockk:mockk:$version_mockk" + // Can't use Jake Wharton's threeten lib for test. For more info see: // https://github.com/JakeWharton/ThreeTenABP/issues/47 testImplementation 'org.threeten:threetenbp:1.6.8' @@ -419,11 +427,11 @@ dependencies { // Android Navigation Component - // 1. Kotlin Implementation - implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" - implementation "androidx.navigation:navigation-ui-ktx:$nav_version" + // 1. Views/Fragments Integration + implementation "androidx.navigation:navigation-fragment:$nav_version" + implementation "androidx.navigation:navigation-ui:$nav_version" - // 2. Feature module Support + // 2. Feature module Support for Fragments implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version" // 3. Testing Navigation diff --git a/android/apolloui/proguard/proguard-facebook-stetho.pro b/android/apolloui/proguard/proguard-facebook-stetho.pro deleted file mode 100644 index 0c57172a..00000000 --- a/android/apolloui/proguard/proguard-facebook-stetho.pro +++ /dev/null @@ -1,4 +0,0 @@ -# Updated as of Stetho 1.1.1 -# -# Note: Doesn't include Javascript console lines. See https://github.com/facebook/stetho/tree/master/stetho-js-rhino#proguard --keep class com.facebook.stetho.** { *; } diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/data/preferences/UserPreferencesRepositoryTest.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/data/preferences/UserPreferencesRepositoryTest.kt index 8eeb9511..d4365eca 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/data/preferences/UserPreferencesRepositoryTest.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/data/preferences/UserPreferencesRepositoryTest.kt @@ -42,7 +42,7 @@ class UserPreferencesRepositoryTest { fun we_can_safely_remove_user_preferences_from_Apollo() { // 1. Store "old" UserPreferences. Notice the presence of "lightningDefaultForReceiving" - userPreferencesRepo.sharedPreferences + userPreferencesRepo.getSharedPreferencesForTesting() .edit() .putString( TEST_KEY, @@ -66,12 +66,13 @@ class UserPreferencesRepositoryTest { } private fun checkOldUserPreferencesRead() { - val pref: Preference = userPreferencesRepo.rxSharedPreferences - .getObject( - TEST_KEY, - OldStoredUserPreferences(), - JsonPreferenceAdapter(OldStoredUserPreferences::class.java) - ) + val pref: Preference = + userPreferencesRepo.getRxSharedPreferencesForTesting() + .getObject( + TEST_KEY, + OldStoredUserPreferences(), + JsonPreferenceAdapter(OldStoredUserPreferences::class.java) + ) val oldPrefs = pref.asObservable().toBlocking().first() @@ -102,12 +103,13 @@ class UserPreferencesRepositoryTest { } private fun checkNewUserPreferencesRead() { - val preference: Preference = userPreferencesRepo.rxSharedPreferences - .getObject( - TEST_KEY, - StoredUserPreferences(), - JsonPreferenceAdapter(StoredUserPreferences::class.java) - ) + val preference: Preference = + userPreferencesRepo.getRxSharedPreferencesForTesting() + .getObject( + TEST_KEY, + StoredUserPreferences(), + JsonPreferenceAdapter(StoredUserPreferences::class.java) + ) val first = preference.asObservable().map { it.toModel() }.toBlocking().first() diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/BaseInstrumentationTest.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/BaseInstrumentationTest.kt index 102bf9bf..c3e308e1 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/BaseInstrumentationTest.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/BaseInstrumentationTest.kt @@ -77,7 +77,7 @@ open class BaseInstrumentationTest : WithMuunInstrumentationHelpers { userRepository = dataComponent.userRepository() authRepository = dataComponent.authRepository() signupDraftManager = dataComponent.signupDraftManager() - navigator = Navigator(logoutActions, UserSelector(userRepository)) + navigator = Navigator(UserSelector(userRepository)) syncApplicationDataAction = dataComponent.syncApplicationDataAction() diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/IncomingSwapTests.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/IncomingSwapTests.kt index b96f9bd6..fbcb2f29 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/IncomingSwapTests.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/IncomingSwapTests.kt @@ -33,7 +33,7 @@ open class IncomingSwapTests : BaseInstrumentationTest() { val amountInSats: Long = 400_000 autoFlows.receiveMoneyFromLNWithAmountLessInvoice(amountInSats) - homeScreen.checkBalanceCloseTo(BitcoinUtils.satoshisToBitcoins(amountInSats)) + homeScreen.waitUntilBalanceCloseTo(BitcoinUtils.satoshisToBitcoins(amountInSats)) autoFlows.logOut() @@ -41,12 +41,12 @@ open class IncomingSwapTests : BaseInstrumentationTest() { autoFlows.signIn(user.email, user.password, pin = user.pin) - homeScreen.checkBalanceCloseTo(BitcoinUtils.satoshisToBitcoins(amountInSats)) + homeScreen.waitUntilBalanceCloseTo(BitcoinUtils.satoshisToBitcoins(amountInSats)) val note = "spendAllFunds" autoFlows.spendAllFunds(note) - homeScreen.checkBalanceCloseTo(BitcoinUtils.satoshisToBitcoins(0)) + homeScreen.waitUntilBalanceCloseTo(BitcoinUtils.satoshisToBitcoins(0)) LappClient().generateBlocks(1) diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/LnUrlWithdrawTests.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/LnUrlWithdrawTests.kt index c34e313a..8b2ec809 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/LnUrlWithdrawTests.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/LnUrlWithdrawTests.kt @@ -25,7 +25,7 @@ open class LnUrlWithdrawTests : BaseInstrumentationTest() { autoFlows.lnUrlWithdrawViaReceive() // We should be at home by now - homeScreen.checkBalanceCloseTo(balanceBefore.add(lnurlWithdrawAmount)) + homeScreen.waitUntilBalanceCloseTo(balanceBefore.add(lnurlWithdrawAmount)) } @Test @@ -37,7 +37,7 @@ open class LnUrlWithdrawTests : BaseInstrumentationTest() { autoFlows.lnUrlWithdrawViaSend() // We should be at home by now - homeScreen.checkBalanceCloseTo(balanceBefore.add(lnurlWithdrawAmount)) + homeScreen.waitUntilBalanceCloseTo(balanceBefore.add(lnurlWithdrawAmount)) } @Test @@ -49,7 +49,7 @@ open class LnUrlWithdrawTests : BaseInstrumentationTest() { autoFlows.lnUrlWithdrawViaSend(LappClient.LnUrlVariant.SLOW) // We should be at home by now - homeScreen.checkBalanceCloseTo(balanceBefore) // balance should not change immediately + homeScreen.waitUntilBalanceCloseTo(balanceBefore) // balance should not change immediately homeScreen.waitUntilBalanceEquals(balanceBefore.add(lnurlWithdrawAmount)) } @@ -112,7 +112,7 @@ open class LnUrlWithdrawTests : BaseInstrumentationTest() { .press() // We should be at home by now - homeScreen.checkBalanceCloseTo(balanceBefore) // balance should not change + homeScreen.waitUntilBalanceCloseTo(balanceBefore) // balance should not change } @Test @@ -130,7 +130,7 @@ open class LnUrlWithdrawTests : BaseInstrumentationTest() { .press() // We should be at home by now - homeScreen.checkBalanceCloseTo(balanceBefore) // balance should not change + homeScreen.waitUntilBalanceCloseTo(balanceBefore) // balance should not change } @Test @@ -148,7 +148,7 @@ open class LnUrlWithdrawTests : BaseInstrumentationTest() { .press() // We should be at home by now - homeScreen.checkBalanceCloseTo(balanceBefore) // balance should not change + homeScreen.waitUntilBalanceCloseTo(balanceBefore) // balance should not change } @Test @@ -166,7 +166,7 @@ open class LnUrlWithdrawTests : BaseInstrumentationTest() { .press() // We should be at home by now - homeScreen.checkBalanceCloseTo(balanceBefore) // balance should not change + homeScreen.waitUntilBalanceCloseTo(balanceBefore) // balance should not change } @Test @@ -180,6 +180,6 @@ open class LnUrlWithdrawTests : BaseInstrumentationTest() { } // We should be at home by now - homeScreen.checkBalanceCloseTo(balanceBefore.add(lnurlWithdrawAmount)) + homeScreen.waitUntilBalanceCloseTo(balanceBefore.add(lnurlWithdrawAmount)) } } \ No newline at end of file diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/NewOperationTests.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/NewOperationTests.kt index c99078a2..53ef11a2 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/NewOperationTests.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/presentation/NewOperationTests.kt @@ -384,7 +384,7 @@ open class NewOperationTests : BaseInstrumentationTest() { newOpScreen.submit() - homeScreen.checkBalanceCloseTo(balanceBefore.subtract(total)) + homeScreen.waitUntilBalanceCloseTo(balanceBefore.subtract(total)) autoFlows.checkOperationDetails(amount, description, fee) { homeScreen.goToOperationDetail(description, isPending = true) 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 37b1dd0c..6a9b8f67 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 @@ -462,7 +462,7 @@ class AutoFlows( val total = newOpScreen.confirmedTotal newOpScreen.submit() - homeScreen.checkBalanceCloseTo(balanceBefore.subtract(total)) + homeScreen.waitUntilBalanceCloseTo(balanceBefore.subtract(total)) checkOperationDetails(amount, description, fee) { homeScreen.goToOperationDetail(description, isPending = true) @@ -556,7 +556,7 @@ class AutoFlows( newOpScreen.submit() - homeScreen.checkBalanceCloseTo(balanceBefore.subtract(total)) + homeScreen.waitUntilBalanceCloseTo(balanceBefore.subtract(total)) // Account for the special case where swap is completed REALLY fast @@ -834,7 +834,7 @@ class AutoFlows( balanceBefore.subtract(total) } - homeScreen.checkBalanceCloseTo(expectedBalance) + homeScreen.waitUntilBalanceCloseTo(expectedBalance) } private fun goToSettingsAndClickDeleteWallet() { diff --git a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/HomeScreen.kt b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/HomeScreen.kt index 5a356e16..b63167ee 100644 --- a/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/HomeScreen.kt +++ b/android/apolloui/src/androidTest/java/io/muun/apollo/utils/screens/HomeScreen.kt @@ -14,8 +14,8 @@ import io.muun.apollo.utils.WithMuunInstrumentationHelpers import io.muun.apollo.utils.WithMuunInstrumentationHelpers.Companion.balanceNotEqualsErrorMessage import io.muun.common.utils.Preconditions import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.within import javax.money.MonetaryAmount +import kotlin.math.abs import kotlin.random.Random class HomeScreen( @@ -44,16 +44,20 @@ class HomeScreen( .isTrue } - fun checkBalanceCloseTo(expectedBalance: MonetaryAmount) { + fun waitUntilBalanceCloseTo(expectedBalance: MonetaryAmount) { + pullNotificationsUntil(20000) { + checkBalanceCloseTo(expectedBalance) + } + } + private fun checkBalanceCloseTo(expectedBalance: MonetaryAmount): Boolean { waitUntilVisible() val actualBalance = balanceInBtc assertThat(actualBalance.currency).isEqualTo(expectedBalance.currency) - assertThat(actualBalance.number.toDouble()) - .isCloseTo(expectedBalance.number.toDouble(), within(0.00001)) + return abs(actualBalance.number.toDouble() - expectedBalance.number.toDouble()) < 0.00001 } fun goToReceive() { diff --git a/android/apolloui/src/main/assets/config.properties b/android/apolloui/src/main/assets/config.properties index b7775ce7..3a7315f8 100644 --- a/android/apolloui/src/main/assets/config.properties +++ b/android/apolloui/src/main/assets/config.properties @@ -4,5 +4,4 @@ database.filename = muun.db database.version = 38 # Network -net.timeoutInSec = 20 -net.interceptors.stetho = true \ No newline at end of file +net.timeoutInSec = 20 \ No newline at end of file diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/ApolloApplication.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/ApolloApplication.java index d3008853..d6dfb3d1 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/ApolloApplication.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/ApolloApplication.java @@ -12,6 +12,7 @@ import io.muun.apollo.data.logging.Crashlytics; import io.muun.apollo.data.logging.LoggingContext; import io.muun.apollo.data.logging.MuunTree; +import io.muun.apollo.data.preferences.FeaturesRepository; import io.muun.apollo.data.preferences.UserRepository; import io.muun.apollo.data.preferences.migration.PreferencesMigrationManager; import io.muun.apollo.domain.ApplicationLockManager; @@ -86,6 +87,9 @@ public abstract class ApolloApplication extends Application @Inject BackgroundTimesService backgroundTimesService; + @Inject + FeaturesRepository featuresRepository; + @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); @@ -100,6 +104,7 @@ public void onCreate() { HeapDumper.init(this); Crashlytics.init(this); + StrictMode.INSTANCE.init(); ensureCurrencyServicesLoaded(); @@ -116,10 +121,10 @@ public void onCreate() { setNightMode(); - initializeLibwallet(); - setupDebugTools(); + initializeLibwallet(); + migrateSharedPreferences(); gcmKeepAlive(); @@ -228,6 +233,9 @@ private void initializeStaticSingletons() { UserFacingErrorMessages.INSTANCE = new UserFacingErrorMessagesImpl(this); } + /** + * TODO: should be done off the main thread. + */ private void initializeDagger() { applicationComponent = DaggerApplicationComponent.builder() .dataComponent(getDataComponent()) @@ -239,7 +247,7 @@ private void initializeDagger() { private void initializeLibwallet() { libwalletDataDirectory.ensureExists(); final String filesDir = libwalletDataDirectory.getPath().getAbsolutePath(); - LibwalletBridge.init(filesDir); + LibwalletBridge.init(filesDir, featuresRepository); } /** @@ -261,6 +269,9 @@ private void gcmKeepAlive() { this.sendBroadcast(new Intent("com.google.android.intent.action.MCS_HEARTBEAT")); } + /** + * TODO: should be done off the main Thread. + */ private void ensureCurrencyServicesLoaded() { // This method eagerly loads the CurrencyProviderSpi instances available in the application // space, instead of letting the ServiceLoader create them lazily as needed. diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/ApolloDebugApplication.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/ApolloDebugApplication.java index 44240746..8fe88630 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/ApolloDebugApplication.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/ApolloDebugApplication.java @@ -3,11 +3,8 @@ import io.muun.apollo.data.external.Globals; import io.muun.apollo.presentation.ui.utils.OS; -import com.facebook.stetho.Stetho; -import com.facebook.stetho.timber.StethoTree; import com.github.moduth.blockcanary.BlockCanary; import com.github.moduth.blockcanary.BlockCanaryContext; -import timber.log.Timber; public class ApolloDebugApplication extends ApolloApplication { @@ -18,9 +15,6 @@ protected void setupDebugTools() { // The release flavor should override this with an empty method on ApolloProdApplication. // We are leaving the extra check for redundancy. if (Globals.INSTANCE.isDebugBuild()) { - Stetho.initializeWithDefaults(this); - Timber.plant(new StethoTree()); - if (!OS.INSTANCE.requiresPendingIntentMutabilityFlags()) { // For latest android versions (require mutability flags), don't install blockCanary BlockCanary.install(this, new AppBlockCanaryContext()).start(); diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/BundledEmojiCompatConfig.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/BundledEmojiCompatConfig.kt index dd831a7d..df7d5b30 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/BundledEmojiCompatConfig.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/BundledEmojiCompatConfig.kt @@ -1,7 +1,6 @@ package io.muun.apollo.presentation.app import android.content.Context -import androidx.annotation.RequiresApi import androidx.emoji2.text.EmojiCompat import androidx.emoji2.text.EmojiCompat.MetadataRepoLoader import androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback @@ -27,7 +26,6 @@ class BundledEmojiCompatConfig(ctx: Context) : EmojiCompat.Config(BundledMetadat private val mContext: Context - @RequiresApi(19) override fun load(loaderCallback: MetadataRepoLoaderCallback) { Preconditions.checkNotNull(loaderCallback, "loaderCallback cannot be null") val runnable = InitRunnable(mContext, loaderCallback) @@ -41,7 +39,6 @@ class BundledEmojiCompatConfig(ctx: Context) : EmojiCompat.Config(BundledMetadat } } - @RequiresApi(19) private class InitRunnable( private val mContext: Context, private val mLoaderCallback: MetadataRepoLoaderCallback, diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/GlobalsImpl.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/GlobalsImpl.kt index 80328d32..3919b12f 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/GlobalsImpl.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/GlobalsImpl.kt @@ -38,15 +38,6 @@ class GlobalsImpl : Globals() { override val deviceManufacturer: String get() = Build.MANUFACTURER - override val fingerprint: String - get() = Build.FINGERPRINT - - override val hardware: String - get() = Build.HARDWARE - - override val bootloader: String - get() = Build.BOOTLOADER - override val muunLinkHost: String get() = BuildConfig.MUUN_LINK_HOST 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 f2944d04..c288b2bf 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 @@ -3,7 +3,6 @@ import io.muun.apollo.BuildConfig; import io.muun.apollo.R; import io.muun.apollo.data.external.Globals; -import io.muun.apollo.domain.action.LogoutActions; import io.muun.apollo.domain.analytics.AnalyticsEvent; import io.muun.apollo.domain.analytics.NewOperationOrigin; import io.muun.apollo.domain.model.FeedbackCategory; @@ -77,12 +76,10 @@ public class Navigator { private static final String PLAY_STORE_PACKAGE_NAME = "io.muun.apollo"; - private final LogoutActions logoutActions; private final UserSelector userSel; @Inject - public Navigator(LogoutActions logoutActions, UserSelector userSelector) { - this.logoutActions = logoutActions; + public Navigator(UserSelector userSelector) { this.userSel = userSelector; } @@ -468,15 +465,6 @@ public void navigateToTaprootSetup(Context context) { context.startActivity(intent); } - /** - * Immediately logs out the user, then navigates back to the launcher. - * CAREFUL! Code after this call can fail when it finds empty repositories/databases. - */ - public void navigateToLogout(@NotNull Context context) { - logoutActions.destroyRecoverableWallet(); - navigateToLauncher(context); - } - /** * Restart the application. */ @@ -489,14 +477,11 @@ public void navigateToLauncher(@NotNull Context context) { } /** - * Immediately logs out the user, then navigates back to the delete wallet success screen. + * Navigates to the delete wallet success screen. + * Meant to be used after successfully deleting a wallet. * CAREFUL! Code after this call can fail when it finds empty repositories/databases. */ - public void navigateToDeleteWallet(@NotNull Context context) { - final Optional maybeSupportId = userSel.getOptional().flatMap(User::getSupportId); - - logoutActions.dangerouslyDestroyWallet(); - + public void navigateToDeleteWallet(@NotNull Context context, Optional maybeSupportId) { final Intent intent = SuccessDeleteWalletActivity .getStartActivityIntent(context, maybeSupportId); diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/NotificationServiceImpl.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/NotificationServiceImpl.kt index e5954457..d1765e44 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/NotificationServiceImpl.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/NotificationServiceImpl.kt @@ -1,14 +1,17 @@ package io.muun.apollo.presentation.app +import android.Manifest import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.Color import androidx.annotation.DrawableRes +import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.work.Data @@ -396,6 +399,19 @@ class NotificationServiceImpl @Inject constructor( } } + if (OS.supportsNotificationRuntimePermission()) { + val notiPermission = ActivityCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS + ) + + if (notiPermission != PackageManager.PERMISSION_GRANTED) { + // We don't have notification runtime permission so don't show notification. + // Trying to so would throw SecurityException. + return + } + } + NotificationManagerCompat .from(context) .notify(notificationId, builder.build()) diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/StrictMode.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/StrictMode.kt index dfbdf2be..047c7df4 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/app/StrictMode.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/app/StrictMode.kt @@ -1,5 +1,7 @@ package io.muun.apollo.presentation.app +import android.os.Build +import android.os.StrictMode import io.muun.apollo.data.external.Globals /** @@ -13,29 +15,61 @@ object StrictMode { if (Globals.INSTANCE.isDebugBuild) { // https://developer.android.com/reference/android/os/StrictMode.ThreadPolicy.Builder - android.os.StrictMode.setThreadPolicy( - android.os.StrictMode.ThreadPolicy.Builder() + StrictMode.setThreadPolicy( + StrictMode.ThreadPolicy.Builder() .detectAll() - .permitDiskReads() // allow for now, we do it plenty in App#OnCreate TODO fix it - .permitDiskWrites()// allow for now, we do it plenty in App#OnCreate TODO fix it +// TODO fix this +// .permitDiskReads() // allow for now, we do it plenty in App#OnCreate +// .permitDiskWrites() // allow for now, we do it plenty in App#OnCreate .penaltyLog() .penaltyFlashScreen() - .penaltyDialog() .penaltyDropBox() - .penaltyDeath() +// .penaltyDialog() // too aggressive for now, even for debug +// .penaltyDeath() // too aggressive for now, even for debug .build() ) // https://developer.android.com/reference/android/os/StrictMode.VmPolicy.Builder - android.os.StrictMode.setVmPolicy( - android.os.StrictMode.VmPolicy.Builder() + StrictMode.setVmPolicy( + StrictMode.VmPolicy.Builder() .detectAll() + .customDetect() .penaltyLog() - .penaltyDeath() + .penaltyDropBox() // we can put upper bound for instance num of specific class, help detect leaks // .setClassInstanceLimit(kclass, instanceLimit) .build() ) } } + + /** + * Customized detection, based on { @link android.os.StrictMode.VmPolicy.Builder.detectAll }, + * which is final, and uses many private or hidden methods. Main hurdle is + * #detectUntaggedSockets() which is super noisy (and useless?). As the rest of this class, + * leaving some commented out code to explicitly state our stand regarding it. + */ + private fun StrictMode.VmPolicy.Builder.customDetect(): StrictMode.VmPolicy.Builder { + detectLeakedSqlLiteObjects() + detectActivityLeaks() + detectLeakedClosableObjects() + detectLeakedRegistrationObjects() + detectFileUriExposure() + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + detectContentUriWithoutPermission() +// Removing as requires silly workaround (https://github.com/square/okhttp/issues/3537) +// detectUntaggedSockets() + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + detectCredentialProtectedWhileLocked() + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + detectUnsafeIntentLaunch() + detectIncorrectContextUse() + } + + return this + } } \ No newline at end of file diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/model/UiOperation.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/model/UiOperation.kt index 0110ff18..f623fcac 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/model/UiOperation.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/model/UiOperation.kt @@ -23,7 +23,7 @@ import io.muun.common.exception.MissingCaseError import io.muun.common.model.OperationStatus import io.muun.common.utils.Preconditions import libwallet.Libwallet -import java.util.* +import java.util.Locale import java.util.regex.Pattern import javax.money.MonetaryAmount @@ -375,7 +375,7 @@ abstract class UiOperation( if (operation.hash == null || !HEX_NUMBER.matcher(operation.hash!!).find()) { context.getString(R.string.not_available) } else { - linkBuilder.transactionLink(operation.hash) + linkBuilder.transactionLink(operation.hash!!) } /** @@ -385,7 +385,7 @@ abstract class UiOperation( if (operation.receiverAddress == null) { context.getString(R.string.not_available) } else { - linkBuilder.addressLink(operation.receiverAddress) + linkBuilder.addressLink(operation.receiverAddress!!) } /** diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/activity/extension/ApplicationLockExtension.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/activity/extension/ApplicationLockExtension.java index e6d0cdbb..c4c76f69 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/activity/extension/ApplicationLockExtension.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/activity/extension/ApplicationLockExtension.java @@ -96,7 +96,7 @@ private void showLockOverlay() { lockOverlay.attachToRoot(); disableSoftInput(); - analytics.report(new AnalyticsEvent.S_PIN_LOCKED()); + analytics.report(new AnalyticsEvent.S_PIN_LOCKED(getActivity())); } updateLockOverlayAttempts(); diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/activity/operations/OperationsActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/activity/operations/OperationsActivity.kt index 2cc21be9..010590ae 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/activity/operations/OperationsActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/activity/operations/OperationsActivity.kt @@ -4,9 +4,11 @@ import android.content.Context import android.content.Intent import android.transition.Slide import android.view.Gravity +import android.view.LayoutInflater import android.view.animation.DecelerateInterpolator -import butterknife.BindView +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.ActivityOperationsBinding import io.muun.apollo.presentation.ui.base.ParentPresenter import io.muun.apollo.presentation.ui.base.SingleFragmentActivity import io.muun.apollo.presentation.ui.base.SingleFragmentPresenter @@ -24,8 +26,11 @@ class OperationsActivity Intent(context, OperationsActivity::class.java) } - @BindView(R.id.header) - lateinit var headerView: MuunHeader + private val binding: ActivityOperationsBinding + get() = getBinding() as ActivityOperationsBinding + + private val headerView: MuunHeader + get() = binding.header override fun inject() { component.inject(this) @@ -34,6 +39,10 @@ class OperationsActivity override fun getLayoutResource(): Int = R.layout.activity_operations + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ActivityOperationsBinding::inflate + } + override fun getFragmentsContainer() = R.id.container diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BasePresenter.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BasePresenter.java index bcca5f40..6611218a 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BasePresenter.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/BasePresenter.java @@ -10,6 +10,7 @@ import io.muun.apollo.domain.ClipboardManager; import io.muun.apollo.domain.EmailReportManager; import io.muun.apollo.domain.action.base.ActionState; +import io.muun.apollo.domain.action.session.LogoutAction; import io.muun.apollo.domain.analytics.Analytics; import io.muun.apollo.domain.analytics.AnalyticsEvent; import io.muun.apollo.domain.errors.DeprecatedClientVersionError; @@ -37,6 +38,7 @@ import android.content.pm.PackageManager; import android.net.NetworkInfo; import android.os.Bundle; +import android.util.Log; import androidx.annotation.CallSuper; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -99,6 +101,9 @@ public class BasePresenter implements Presenter { @Inject protected LogoutOptionsSelector logoutOptionsSel; + @Inject + protected LogoutAction logout; + @Inject protected Analytics analytics; @@ -262,7 +267,12 @@ public void handleError(Throwable error) { Timber.e(error); } - analytics.report(new AnalyticsEvent.E_ERROR(AnalyticsEvent.ERROR_TYPE.GENERIC)); + analytics.report(new AnalyticsEvent.E_ERROR( + AnalyticsEvent.ERROR_TYPE.GENERIC, + error.getClass().getSimpleName(), + error.getLocalizedMessage(), + Log.getStackTraceString(error).replace("\n", " ") + )); // Our current error handling logic is this: // - If error is one of our known fatal error -> handleFatalError() @@ -306,7 +316,7 @@ protected boolean handleFatalError(Throwable error) { view.showErrorDialog( R.string.secure_storage_error, - () -> navigator.navigateToLogout(getContext()) + this::logout ); } else { @@ -316,6 +326,11 @@ protected boolean handleFatalError(Throwable error) { return true; } + protected void logout() { + logout.run(); + navigator.navigateToLauncher(getContext()); + } + /** * Handle some common, known non fatal errors. * Returns a boolean informing whether the received error was handled or not. @@ -368,7 +383,6 @@ protected Optional takeLongArgument(@Nullable Bundle bundle, @NotNull Stri } final long value = bundle.getLong(key); - bundle.remove(key); return Optional.of(value); } @@ -379,8 +393,9 @@ protected Optional takeLongArgument(@Nullable Bundle bundle, @NotNull Stri * * @param condition The condition to be tested. * @param fieldName The name of the argument. + * @return whether condition was met or not */ - protected void checkArgument(boolean condition, String fieldName) { + protected boolean checkArgument(boolean condition, String fieldName) { if (!condition) { @@ -395,7 +410,11 @@ protected void checkArgument(boolean condition, String fieldName) { () -> showErrorReportDialog(error, false), () -> view.finishActivity() ); + + return false; } + + return true; } /** @@ -466,7 +485,8 @@ protected boolean shouldCheckClientState() { protected Observable.Transformer, T> handleStates( @Nullable Action1 handleLoading, - @Nullable Action1 handleError) { + @Nullable Action1 handleError + ) { return observable -> observable .doOnNext(state -> { @@ -488,7 +508,11 @@ protected Observable.Transformer, T> handleStates( */ private void showErrorReportDialog(Throwable error, boolean standalone) { - analytics.report(new AnalyticsEvent.E_ERROR_REPORT_DIALOG()); + analytics.report(new AnalyticsEvent.E_ERROR_REPORT_DIALOG( + error.getClass().getSimpleName(), + error.getLocalizedMessage(), + Log.getStackTraceString(error).replace("\n", " ") + )); final MuunDialog.Builder builder = new MuunDialog.Builder() .layout(R.layout.dialog_custom_layout) diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/SingleFragmentActivityImpl.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/SingleFragmentActivityImpl.kt index ccf385dd..ca402795 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/SingleFragmentActivityImpl.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/base/SingleFragmentActivityImpl.kt @@ -2,8 +2,10 @@ package io.muun.apollo.presentation.ui.base import android.content.Context import android.content.Intent -import butterknife.BindView +import android.view.LayoutInflater +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.ActivitySingleFragmentBinding import io.muun.apollo.presentation.ui.view.MuunHeader import timber.log.Timber @@ -26,8 +28,11 @@ class SingleFragmentActivityImpl : } } - @BindView(R.id.header) - lateinit var headerView: MuunHeader + private val binding: ActivitySingleFragmentBinding + get() = getBinding() as ActivitySingleFragmentBinding + + private val headerView: MuunHeader + get() = binding.header override fun inject() { component.inject(this) @@ -36,6 +41,10 @@ class SingleFragmentActivityImpl : override fun getLayoutResource(): Int = R.layout.activity_single_fragment + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ActivitySingleFragmentBinding::inflate + } + override fun getFragmentsContainer() = R.id.container diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/export_keys/EmergencyKitActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/export_keys/EmergencyKitActivity.kt index 41aa370d..2a418b07 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/export_keys/EmergencyKitActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/export_keys/EmergencyKitActivity.kt @@ -2,9 +2,11 @@ package io.muun.apollo.presentation.ui.export_keys import android.content.Context import android.content.Intent +import android.view.LayoutInflater import androidx.fragment.app.Fragment -import butterknife.BindView +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.ExportKeysActivityBinding import io.muun.apollo.domain.analytics.AnalyticsEvent import io.muun.apollo.presentation.ui.activity.extension.MuunDialog import io.muun.apollo.presentation.ui.base.SingleFragmentActivity @@ -28,8 +30,11 @@ class EmergencyKitActivity : SingleFragmentActivity(), Em } } - @BindView(R.id.export_keys_header) - lateinit var headerView: MuunHeader + private val binding: ExportKeysActivityBinding + get() = getBinding() as ExportKeysActivityBinding + + private val headerView: MuunHeader + get() = binding.exportKeysHeader override fun inject() { component.inject(this) @@ -38,6 +43,10 @@ class EmergencyKitActivity : SingleFragmentActivity(), Em override fun getLayoutResource() = R.layout.export_keys_activity + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ExportKeysActivityBinding::inflate + } + override fun getFragmentsContainer() = R.id.fragment_container diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/create_email_help/CreateEmailHelpFragment.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/create_email_help/CreateEmailHelpFragment.kt index 2754ad66..5cb42c12 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/create_email_help/CreateEmailHelpFragment.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/create_email_help/CreateEmailHelpFragment.kt @@ -1,17 +1,23 @@ package io.muun.apollo.presentation.ui.fragments.create_email_help +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import android.widget.TextView -import butterknife.BindView +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.FragmentCreateEmailHelpBinding import io.muun.apollo.presentation.ui.base.SingleFragment import io.muun.apollo.presentation.ui.utils.StyledStringRes import io.muun.apollo.presentation.ui.view.MuunHeader class CreateEmailHelpFragment : SingleFragment() { - @BindView(R.id.text) - lateinit var textView: TextView + private val binding: FragmentCreateEmailHelpBinding + get() = getBinding() as FragmentCreateEmailHelpBinding + + private val textView: TextView + get() = binding.text override fun inject() = component.inject(this) @@ -19,6 +25,10 @@ class CreateEmailHelpFragment : SingleFragment() { override fun getLayoutResource() = R.layout.fragment_create_email_help + override fun bindingInflater(): (LayoutInflater, ViewGroup, Boolean) -> ViewBinding { + return FragmentCreateEmailHelpBinding::inflate + } + override fun initializeUi(view: View) { val styledRes = StyledStringRes( requireContext(), diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/create_password/CreatePasswordFragment.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/create_password/CreatePasswordFragment.kt index 406b77ed..92f07cf6 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/create_password/CreatePasswordFragment.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/fragments/create_password/CreatePasswordFragment.kt @@ -1,8 +1,11 @@ package io.muun.apollo.presentation.ui.fragments.create_password +import android.view.LayoutInflater import android.view.View -import butterknife.BindView +import android.view.ViewGroup +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.FragmentCreatePasswordBinding import io.muun.apollo.domain.errors.UserFacingError import io.muun.apollo.presentation.ui.base.SingleFragment import io.muun.apollo.presentation.ui.view.MuunButton @@ -11,14 +14,17 @@ import io.muun.apollo.presentation.ui.view.MuunTextInput class CreatePasswordFragment : SingleFragment(), CreatePasswordView { - @BindView(R.id.create_password_input) - lateinit var passwordInput: MuunTextInput + private val binding: FragmentCreatePasswordBinding + get() = getBinding() as FragmentCreatePasswordBinding - @BindView(R.id.create_password_confirm_input) - lateinit var passwordConfirmInput: MuunTextInput + private val passwordInput: MuunTextInput + get() = binding.createPasswordInput - @BindView(R.id.create_password_confirm) - lateinit var confirmButton: MuunButton + private val passwordConfirmInput: MuunTextInput + get() = binding.createPasswordConfirmInput + + private val confirmButton: MuunButton + get() = binding.createPasswordConfirm override fun inject() { component.inject(this) @@ -27,23 +33,19 @@ class CreatePasswordFragment : SingleFragment(), Create override fun getLayoutResource() = R.layout.fragment_create_password + override fun bindingInflater(): (LayoutInflater, ViewGroup, Boolean) -> ViewBinding { + return FragmentCreatePasswordBinding::inflate + } + override fun initializeUi(view: View) { 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) { - validatePassword() - } + validatePassword() } 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) { - validatePassword() - } + validatePassword() } confirmButton.isEnabled = false 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 7e8f47a6..3e80b9d7 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 @@ -362,7 +362,7 @@ open class SettingsFragment : SingleFragment(), SettingsView .layout(R.layout.dialog_custom_layout2) .title(R.string.settings_logout_alert_title) .message(R.string.settings_logout_alert_body) - .positiveButton(R.string.settings_logout_alert_yes) { presenter.logout() } + .positiveButton(R.string.settings_logout_alert_yes) { presenter.logoutUser() } .negativeButton(R.string.settings_logout_alert_no, null) .build() showDialog(muunDialog) 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 a4d6cdec..e578c031 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 @@ -6,12 +6,12 @@ import io.muun.apollo.data.external.NotificationService import io.muun.apollo.domain.NightModeManager import io.muun.apollo.domain.action.UserActions import io.muun.apollo.domain.action.base.ActionState +import io.muun.apollo.domain.action.session.LogoutAction import io.muun.apollo.domain.action.user.DeleteWalletAction import io.muun.apollo.domain.action.user.UpdateProfilePictureAction import io.muun.apollo.domain.analytics.AnalyticsEvent import io.muun.apollo.domain.analytics.AnalyticsEvent.S_SETTINGS import io.muun.apollo.domain.analytics.AnalyticsEvent.WalletDeleteState -import io.muun.apollo.domain.errors.MuunError import io.muun.apollo.domain.errors.delete_wallet.NonEmptyWalletDeleteException import io.muun.apollo.domain.errors.delete_wallet.UnsettledOperationsWalletDeleteException import io.muun.apollo.domain.libwallet.UAF_TAPROOT @@ -28,6 +28,7 @@ import io.muun.apollo.presentation.ui.base.SingleFragmentPresenter import io.muun.apollo.presentation.ui.base.di.PerFragment import io.muun.apollo.presentation.ui.settings.bitcoin.BitcoinSettingsFragment import io.muun.apollo.presentation.ui.settings.lightning.LightningSettingsFragment +import io.muun.common.Optional import io.muun.common.api.messages.EventCommunicationMessage.Event import io.muun.common.utils.Preconditions import rx.Observable @@ -39,6 +40,7 @@ import javax.money.CurrencyUnit class SettingsPresenter @Inject constructor( private val bitcoinUnitSel: BitcoinUnitSelector, private val updateProfilePictureAction: UpdateProfilePictureAction, + private val logoutAction: LogoutAction, private val deleteWallet: DeleteWalletAction, private val userActions: UserActions, private val exchangeRateSelector: ExchangeRateSelector, @@ -118,8 +120,8 @@ class SettingsPresenter @Inject constructor( val observable = deleteWallet .state .compose(handleStates(view::setLoading, this::handleWalletDeleteError)) - .doOnNext { - onWalletDeleted() + .doOnNext { maybeSupportId: Optional -> + onWalletDeleted(maybeSupportId) } subscribeTo(observable) } @@ -159,18 +161,16 @@ class SettingsPresenter @Inject constructor( /** * Call to logout. */ - fun logout() { + fun logoutUser() { analytics.report(AnalyticsEvent.E_LOG_OUT()) analytics.resetUserProperties() - // We need to "capture" auth header to fire (and forget) notifyLogout request - val jwt = getJwt() - navigator.navigateToLogout(context) + logoutAction.run() + navigator.navigateToLauncher(context) // We need to finish this activity, or the session status check will immediately raise // the SessionExpired error -- even though this was a regular logout. view.finishActivity() - userActions.notifyLogoutAction.run(jwt) } /** @@ -184,11 +184,11 @@ class SettingsPresenter @Inject constructor( /** * Perform successful delete wallet follow up actions (e.g cleanup, navigation, etc...). */ - private fun onWalletDeleted() { + private fun onWalletDeleted(maybeSupportId: Optional) { analytics.report(AnalyticsEvent.E_WALLET_DELETE(WalletDeleteState.SUCCESS)) analytics.resetUserProperties() - navigator.navigateToDeleteWallet(context) + navigator.navigateToDeleteWallet(context, maybeSupportId) // We need to finish this activity, or the session status check will immediately raise // the SessionExpired error -- even though this is expected from deleteWallet. @@ -199,10 +199,14 @@ class SettingsPresenter @Inject constructor( * Handle the tap on the log out button. */ fun handleLogoutRequest() { + + if (!userSel.getOptional().isPresent) { + Timber.e("This shouldn't happen. Already logged out user") + return + } + // TODO: this should not be using a blocking observable. Not terrible, not ideal. - val options = logoutOptionsSel.watch() - .toBlocking() - .first() + val options = logoutOptionsSel.get() val shouldBlockAndExplain = options.isLogoutBlocked() Preconditions.checkArgument(options.isRecoverable()) @@ -213,10 +217,14 @@ class SettingsPresenter @Inject constructor( * Handle the tap on the delete wallet button. */ fun handleDeleteWalletRequest() { + + if (!userSel.getOptional().isPresent) { + Timber.e("This shouldn't happen. Already logged out user") + return + } + // TODO: this should not be using a blocking observable. Not terrible, not ideal. - val options = logoutOptionsSel.watch() - .toBlocking() - .first() + val options = logoutOptionsSel.get() view.handleDeleteWallet(options.canDeleteWallet(), options.isRecoverable()) } @@ -225,15 +233,6 @@ class SettingsPresenter @Inject constructor( return S_SETTINGS() } - private fun getJwt(): String { - val serverJwt = authRepository.serverJwt - if (!serverJwt.isPresent) { - // Shouldn't happen but isn't the worst if it does. We wanna know 'cause probably a bug - Timber.e(MuunError("Auth token expected to be present")) - } - return serverJwt.get() - } - fun navigateToLightningSettings() { navigator.navigateToFragment(context, LightningSettingsFragment::class.java) } diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/high_fees/HighFeesExplanationActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/high_fees/HighFeesExplanationActivity.kt index a0b5e44d..613220a0 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/high_fees/HighFeesExplanationActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/high_fees/HighFeesExplanationActivity.kt @@ -3,8 +3,10 @@ package io.muun.apollo.presentation.ui.high_fees import android.content.Context import android.content.Intent import android.graphics.Color -import butterknife.BindView +import android.view.LayoutInflater +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.ActivityHighFeesBinding import io.muun.apollo.presentation.ui.base.BaseActivity import io.muun.apollo.presentation.ui.base.BasePresenter import io.muun.apollo.presentation.ui.base.BaseView @@ -17,8 +19,11 @@ class HighFeesExplanationActivity : BaseActivity>(), Bas Intent(context, HighFeesExplanationActivity::class.java) } - @BindView(R.id.muun_header) - lateinit var header: MuunHeader + private val binding: ActivityHighFeesBinding + get() = getBinding() as ActivityHighFeesBinding + + private val header: MuunHeader + get() = binding.muunHeader override fun inject() { component.inject(this) @@ -28,6 +33,10 @@ class HighFeesExplanationActivity : BaseActivity>(), Bas return R.layout.activity_high_fees } + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ActivityHighFeesBinding::inflate + } + override fun initializeUi() { super.initializeUi() header.attachToActivity(this) diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/home/HomeActivity.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/home/HomeActivity.java index 94f160dd..583f7634 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/home/HomeActivity.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/home/HomeActivity.java @@ -1,6 +1,7 @@ package io.muun.apollo.presentation.ui.home; import io.muun.apollo.R; +import io.muun.apollo.databinding.HomeActivityBinding; import io.muun.apollo.domain.analytics.AnalyticsEvent.SECURITY_CENTER_ORIGIN; import io.muun.apollo.domain.model.Operation; import io.muun.apollo.presentation.ui.activity.extension.MuunDialog; @@ -16,14 +17,16 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; import androidx.navigation.NavController; import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.ui.NavigationUI; -import butterknife.BindView; +import androidx.viewbinding.ViewBinding; import com.google.android.material.bottomnavigation.BottomNavigationView; +import kotlin.jvm.functions.Function1; import java.util.Objects; import javax.validation.constraints.NotNull; @@ -53,13 +56,15 @@ public static Intent getStartActivityIntent(@NotNull Context context) { } static String SHOW_WELCOME_TO_MUUN = "SHOW_WELCOME_TO_MUUN"; - public static String NEW_OP_ID = "NEW_OP_ID"; - @BindView(R.id.home_header) - MuunHeader header; + public static String NEW_OP_ID = "NEW_OP_ID"; private NavController navController; + private HomeActivityBinding binding() { + return (HomeActivityBinding) getBinding(); + } + @Override protected void inject() { getComponent().inject(this); @@ -70,6 +75,11 @@ protected int getLayoutResource() { return R.layout.home_activity; } + @Override + protected Function1 bindingInflater() { + return HomeActivityBinding::inflate; + } + @Override protected int getMenuResource() { return R.menu.home_activity; @@ -77,7 +87,7 @@ protected int getMenuResource() { @Override public MuunHeader getHeader() { - return header; + return binding().homeHeader; } @Override diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/intro/LnUrlIntroActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/intro/LnUrlIntroActivity.kt index 09ededb2..b78cbab7 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/intro/LnUrlIntroActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/intro/LnUrlIntroActivity.kt @@ -2,25 +2,20 @@ package io.muun.apollo.presentation.ui.lnurl.intro import android.content.Context import android.content.Intent -import butterknife.BindView -import butterknife.OnClick +import android.view.LayoutInflater +import androidx.viewbinding.ViewBinding import io.muun.apollo.R -import io.muun.apollo.domain.model.OperationUri -import io.muun.apollo.presentation.ui.base.BasePresenter -import io.muun.apollo.presentation.ui.base.BaseView +import io.muun.apollo.databinding.ActivityLnurlIntroBinding import io.muun.apollo.presentation.ui.base.SingleFragmentActivity import io.muun.apollo.presentation.ui.view.MuunHeader -class LnUrlIntroActivity: SingleFragmentActivity() { +class LnUrlIntroActivity : SingleFragmentActivity() { companion object { fun getStartActivityIntent(context: Context) = Intent(context, LnUrlIntroActivity::class.java) } - @BindView(R.id.lnurl_intro_header) - lateinit var muunHeader: MuunHeader - override fun inject() { component.inject(this) } @@ -28,8 +23,14 @@ class LnUrlIntroActivity: SingleFragmentActivity() { override fun getLayoutResource(): Int = R.layout.activity_lnurl_intro - override fun getHeader(): MuunHeader = - muunHeader + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ActivityLnurlIntroBinding::inflate + } + + private val binding: ActivityLnurlIntroBinding + get() = getBinding() as ActivityLnurlIntroBinding + + override fun getHeader(): MuunHeader = binding.lnurlIntroHeader override fun initializeUi() { super.initializeUi() @@ -38,10 +39,14 @@ class LnUrlIntroActivity: SingleFragmentActivity() { header.showTitle(R.string.home_footer_action_receive) header.setNavigation(MuunHeader.Navigation.BACK) header.setElevated(true) + + binding.lnurlIntroAction.setOnClickListener { + onContinueClick() + } } - @OnClick(R.id.lnurl_intro_action) - fun onContinueClick() { + + private fun onContinueClick() { presenter.continueWithLnUrlFlow() } } diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/withdraw/LnUrlWithdrawActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/withdraw/LnUrlWithdrawActivity.kt index e942304c..0a69da69 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/withdraw/LnUrlWithdrawActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/withdraw/LnUrlWithdrawActivity.kt @@ -2,10 +2,13 @@ package io.muun.apollo.presentation.ui.lnurl.withdraw import android.content.Context import android.content.Intent +import android.view.LayoutInflater import android.view.View import androidx.core.content.ContextCompat -import butterknife.BindView +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.ActivityLnurlWithdrawBinding +import io.muun.apollo.databinding.FragmentRecoveryToolBinding import io.muun.apollo.domain.analytics.AnalyticsEvent import io.muun.apollo.domain.model.LnUrlWithdraw import io.muun.apollo.domain.model.lnurl.LnUrlError @@ -42,14 +45,17 @@ class LnUrlWithdrawActivity: SingleFragmentActivity(), L val zebedeeCountryLink = "https://zebedee.io/countries/" } - @BindView(R.id.lnurl_withdraw_header) - lateinit var muunHeader: MuunHeader + private val binding: ActivityLnurlWithdrawBinding + get() = getBinding() as ActivityLnurlWithdrawBinding - @BindView(R.id.lnurl_withdraw_loading) - lateinit var loadingView: LoadingView + private val muunHeader: MuunHeader + get() = binding.lnurlWithdrawHeader - @BindView(R.id.lnurl_withdraw_action) - lateinit var actionButton: MuunButton + private val loadingView: LoadingView + get() = binding.lnurlWithdrawLoading + + private val actionButton: MuunButton + get() = binding.lnurlWithdrawAction override fun inject() { component.inject(this) @@ -58,6 +64,10 @@ class LnUrlWithdrawActivity: SingleFragmentActivity(), L override fun getLayoutResource(): Int = R.layout.activity_lnurl_withdraw + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ActivityLnurlWithdrawBinding::inflate + } + override fun getHeader(): MuunHeader = muunHeader diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/withdraw/confirm/LnUrlWithdrawConfirmActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/withdraw/confirm/LnUrlWithdrawConfirmActivity.kt index 05cdbc34..ea5bd628 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/withdraw/confirm/LnUrlWithdrawConfirmActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/lnurl/withdraw/confirm/LnUrlWithdrawConfirmActivity.kt @@ -3,13 +3,14 @@ package io.muun.apollo.presentation.ui.lnurl.withdraw.confirm import android.content.Context import android.content.Intent import android.os.Bundle -import butterknife.BindView -import butterknife.OnClick +import android.view.LayoutInflater +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.ActivityLnurlWithdrawConfirmBinding import io.muun.apollo.presentation.ui.base.SingleFragmentActivity import io.muun.apollo.presentation.ui.view.MuunHeader -class LnUrlWithdrawConfirmActivity: SingleFragmentActivity() { +class LnUrlWithdrawConfirmActivity : SingleFragmentActivity() { companion object { const val ARG_LNURL = "lnurl" @@ -22,9 +23,6 @@ class LnUrlWithdrawConfirmActivity: SingleFragmentActivity ViewBinding { + return ActivityLnurlWithdrawConfirmBinding::inflate + } + + private val binding: ActivityLnurlWithdrawConfirmBinding + get() = getBinding() as ActivityLnurlWithdrawConfirmBinding + override fun getHeader(): MuunHeader = - muunHeader + binding.lnurlWithdrawConfirmHeader + + private fun getBackButton() = + binding.lnurlWithdrawConfirmBackAction + + private fun getConfirmButton() = + binding.lnurlWithdrawConfirmAction + override fun initializeUi() { super.initializeUi() @@ -42,15 +54,22 @@ class LnUrlWithdrawConfirmActivity: SingleFragmentActivity(), MigrationView { Intent(context, MigrationActivity::class.java) } - @BindView(R.id.migration_loading) - lateinit var loadingView: LoadingView + private val binding: ActivityMigrationBinding + get() = getBinding() as ActivityMigrationBinding + + private val loadingView: LoadingView + get() = binding.migrationLoading override fun inject() { component.inject(this) @@ -25,6 +30,10 @@ class MigrationActivity : BaseActivity(), MigrationView { return R.layout.activity_migration } + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ActivityMigrationBinding::inflate + } + override fun initializeUi() { super.initializeUi() diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationActivity.kt index dc832443..6a28d006 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/new_operation/NewOperationActivity.kt @@ -6,15 +6,16 @@ import android.os.Bundle import android.os.Handler import android.text.Html import android.text.TextUtils +import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment -import butterknife.BindView -import butterknife.BindViews +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.ActivityNewOperationBinding import io.muun.apollo.domain.analytics.NewOperationOrigin import io.muun.apollo.domain.libwallet.adapt import io.muun.apollo.domain.libwallet.destinationPubKey @@ -85,101 +86,100 @@ class NewOperationActivity : SingleFragmentActivity(), } // Components: - @BindView(R.id.root_view) - lateinit var root: ConstraintLayout + private val root: ConstraintLayout + get() = binding.rootView - @BindView(R.id.overlay_container) - lateinit var overlayContainer: ViewGroup + private val overlayContainer: ViewGroup + get() = binding.overlayContainer - @BindView(R.id.new_operation_header) - lateinit var muunHeader: MuunHeader + private val muunHeader: MuunHeader + get() = binding.newOperationHeader - @BindView(R.id.scrollable_layout) - lateinit var scrollableLayout: View + private val scrollableLayout: View + get() = binding.scrollableLayout - @BindView(R.id.new_operation_resolving) - lateinit var resolvingSpinner: View + private val resolvingSpinner: View + get() = binding.newOperationResolving - @BindView(R.id.muun_amount_input) - lateinit var amountInput: MuunAmountInput + private val amountInput: MuunAmountInput + get() = binding.muunAmountInput - @BindView(R.id.use_all_funds) - lateinit var useAllFundsView: TextView + private val useAllFundsView: TextView + get() = binding.useAllFunds - @BindView(R.id.muun_note_input) - lateinit var descriptionInput: TextInputWithBackHandling + private val descriptionInput: TextInputWithBackHandling + get() = binding.muunNoteInput - @BindView(R.id.muun_next_step_button) - lateinit var actionButton: MuunButton + private val actionButton: MuunButton + get() = binding.muunNextStepButton - @BindView(R.id.new_operation_receiver) - lateinit var receiver: MuunPill + private val receiver: MuunPill + get() = binding.newOperationReceiver - @BindView(R.id.target_address) - lateinit var receiverAddress: TextView + private val receiverAddress: TextView + get() = binding.targetAddress - @BindView(R.id.selected_amount) - lateinit var selectedAmount: TextView + private val selectedAmount: TextView + get() = binding.selectedAmount - @BindView(R.id.amount_label) - lateinit var amountLabel: TextView + private val amountLabel: TextView + get() = binding.amountLabel - @BindView(R.id.separator_amount) - lateinit var amountSeparator: View + private val amountSeparator: View + get() = binding.separatorAmount - @BindView(R.id.notes_content) - lateinit var descriptionContent: TextView + private val descriptionContent: TextView + get() = binding.notesContent - @BindView(R.id.notes_label) - lateinit var descriptionLabel: TextView + private val descriptionLabel: TextView + get() = binding.notesLabel - @BindView(R.id.fee_label) - lateinit var feeLabel: TextView + private val feeLabel: TextView + get() = binding.feeLabel - @BindView(R.id.fee_amount) - lateinit var feeAmount: TextView + private val feeAmount: TextView + get() = binding.feeAmount - @BindView(R.id.total_amount) - lateinit var totalAmount: TextView + private val totalAmount: TextView + get() = binding.totalAmount - @BindView(R.id.total_label) - lateinit var totalLabel: TextView + private val totalLabel: TextView + get() = binding.totalLabel + private val statusMessage: StatusMessage + get() = binding.statusMessage - @BindView(R.id.status_message) - lateinit var statusMessage: StatusMessage - - @BindView(R.id.insufficient_funds_message) - lateinit var insufficientFundsMessage: StatusMessage + private val insufficientFundsMessage: StatusMessage + get() = binding.insufficientFundsMessage // Groups (to toggle visibility): - @BindViews(R.id.muun_amount_input, R.id.use_all_funds) - lateinit var amountEditableViews: Array - - @BindViews(R.id.amount_label, R.id.selected_amount, R.id.separator_amount) - lateinit var amountSelectedViews: Array - - @BindViews(R.id.status_message, R.id.insufficient_funds_message) - lateinit var statusMessageViews: Array - - @BindViews( - R.id.fee_label, - R.id.fee_amount, - R.id.total_label, - R.id.total_amount, - R.id.notes_label, - R.id.notes_content - ) - lateinit var noteEnteredViews: Array + private val amountEditableViews: Array + get() = arrayOf(binding.muunAmountInput, binding.useAllFunds) + + private val amountSelectedViews: Array + get() = arrayOf(binding.amountLabel, binding.selectedAmount, binding.separatorAmount) + + private val statusMessageViews: Array + get() = arrayOf(binding.statusMessage, binding.insufficientFundsMessage) + + private val noteEnteredViews: Array + get() = arrayOf( + binding.feeLabel, + binding.feeAmount, + binding.totalLabel, + binding.totalAmount, + binding.notesLabel, + binding.notesContent, + ) // Lightning specific - @BindView(R.id.invoice_expiration_countdown) - lateinit var invoiceExpirationCountdown: TextView + private val invoiceExpirationCountdown: TextView + get() = binding.invoiceExpirationCountdown - @BindView(R.id.one_conf_notice_banner) - lateinit var noticeBanner: NoticeBanner + private val noticeBanner: NoticeBanner + get() = binding.oneConfNoticeBanner - @BindView(R.id.button_layout_anchor) - lateinit var buttonLayoutAnchor: View + private val buttonLayoutAnchor: View + get() = binding.buttonLayoutAnchor private var countdownTimer: MuunCountdownTimer? = null @@ -190,6 +190,14 @@ class NewOperationActivity : SingleFragmentActivity(), override fun getLayoutResource(): Int = R.layout.activity_new_operation + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ActivityNewOperationBinding::inflate + } + + override fun getBinding(): ActivityNewOperationBinding { + return super.getBinding() as ActivityNewOperationBinding + } + override fun getHeader(): MuunHeader = muunHeader @@ -520,8 +528,8 @@ class NewOperationActivity : SingleFragmentActivity(), MuunDialog.Builder() .title(R.string.new_operation_abort_alert_title) .message(R.string.new_operation_abort_alert_body) - .positiveButton(R.string.abort) { presenter.finishAndGoHome() } - .negativeButton(R.string.cancel, null) + .positiveButton(R.string.yes_cancel) { presenter.finishAndGoHome() } + .negativeButton(R.string.no, null) .onDismiss { presenter.cancelAbort() } .build() .let(this::showDialog) @@ -655,6 +663,7 @@ class NewOperationActivity : SingleFragmentActivity(), receiver.visibility = View.GONE // 2. Start invoice expiration countdown + countdownTimer?.cancel() countdownTimer = MuunCountdownTimer(invoice.remainingMillis(), this) countdownTimer?.start() buttonLayoutAnchor.visibility = View.VISIBLE @@ -761,4 +770,4 @@ class NewOperationActivity : SingleFragmentActivity(), header.setNavigation(Navigation.BACK) overlayContainer.visibility = View.GONE } -} \ No newline at end of file +} diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailPresenter.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailPresenter.java index d56dfcc9..6d516410 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailPresenter.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/operation_detail/OperationDetailPresenter.java @@ -27,6 +27,7 @@ public class OperationDetailPresenter extends BasePresenter public static final String OPERATION_ID_KEY = "OPERATION_ID"; private final OperationActions operationActions; + private final BitcoinUnitSelector bitcoinUnitSel; private final LinkBuilder linkBuilder; @@ -43,10 +44,12 @@ public class OperationDetailPresenter extends BasePresenter * Constructor. */ @Inject - public OperationDetailPresenter(OperationActions operationActions, - BitcoinUnitSelector bitcoinUnitSel, - LinkBuilder linkBuilder, - BlockchainHeightRepository blockchainHeightRepository) { + public OperationDetailPresenter( + OperationActions operationActions, + BitcoinUnitSelector bitcoinUnitSel, + LinkBuilder linkBuilder, + BlockchainHeightRepository blockchainHeightRepository + ) { this.operationActions = operationActions; this.bitcoinUnitSel = bitcoinUnitSel; @@ -59,12 +62,15 @@ public void setUp(@NotNull Bundle arguments) { super.setUp(arguments); final Optional maybeOperationId = takeLongArgument(arguments, OPERATION_ID_KEY); - checkArgument(maybeOperationId.isPresent(), "operationId"); - bitcoinUnit = bitcoinUnitSel.get(); + final boolean precondition = checkArgument(maybeOperationId.isPresent(), "operationId"); + + if (precondition) { + bitcoinUnit = bitcoinUnitSel.get(); - operationId = maybeOperationId.get(); - bindOperation(); + operationId = maybeOperationId.get(); + bindOperation(); + } } private void bindOperation() { @@ -72,10 +78,11 @@ private void bindOperation() { .fetchOperationById(operationId) .doOnNext(this::reportOperationDetail) .map(op -> UiOperation.fromOperation( - op, - linkBuilder, - bitcoinUnit, - getContext()) + op, + linkBuilder, + bitcoinUnit, + getContext() + ) ) .compose(getAsyncExecutor()) .doOnNext(view::setOperation); diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/recovery_code/SetupRecoveryCodeActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/recovery_code/SetupRecoveryCodeActivity.kt index 7b070774..07b8a012 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/recovery_code/SetupRecoveryCodeActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/recovery_code/SetupRecoveryCodeActivity.kt @@ -2,8 +2,10 @@ package io.muun.apollo.presentation.ui.recovery_code import android.content.Context import android.content.Intent -import butterknife.BindView +import android.view.LayoutInflater +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.RecoveryCodeActivityBinding import io.muun.apollo.domain.analytics.AnalyticsEvent import io.muun.apollo.domain.model.user.User import io.muun.apollo.presentation.ui.activity.extension.MuunDialog @@ -29,8 +31,11 @@ internal class SetupRecoveryCodeActivity : SingleFragmentActivity ViewBinding { + return RecoveryCodeActivityBinding::inflate + } + override fun getFragmentsContainer(): Int = R.id.fragment_container diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/security_logout/SecurityLogoutPresenter.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/security_logout/SecurityLogoutPresenter.java index 8ff9aa1a..0065918a 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/security_logout/SecurityLogoutPresenter.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/security_logout/SecurityLogoutPresenter.java @@ -1,7 +1,6 @@ package io.muun.apollo.presentation.ui.security_logout; -import io.muun.apollo.domain.action.LogoutActions; -import io.muun.apollo.domain.action.UserActions; +import io.muun.apollo.domain.action.session.LogoutAction; import io.muun.apollo.domain.analytics.AnalyticsEvent; import io.muun.apollo.domain.errors.MuunError; import io.muun.apollo.presentation.ui.base.BasePresenter; @@ -18,27 +17,21 @@ @PerActivity public class SecurityLogoutPresenter extends BasePresenter { - private final LogoutActions logoutActions; - private final UserActions userActions; + private final LogoutAction logout; /** * Constructor. */ @Inject - public SecurityLogoutPresenter(LogoutActions logoutActions, UserActions userActions) { - this.logoutActions = logoutActions; - this.userActions = userActions; + public SecurityLogoutPresenter(LogoutAction logout) { + this.logout = logout; } @Override public void setUp(Bundle arguments) { super.setUp(arguments); - - // TODO we should extract this to an action and refactor SettingsPresenter - // We need to "capture" auth header to fire (and forget) notifyLogout request - final String jwt = getJwt(); - logoutActions.destroyRecoverableWallet(); - userActions.notifyLogoutAction.run(jwt); + // TODO this should probably happen BEFORE navigating to this screen, right? + logout.run(); } /** diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_amount/SelectAmountActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_amount/SelectAmountActivity.kt index ed3329ac..3a2b8262 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_amount/SelectAmountActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_amount/SelectAmountActivity.kt @@ -3,10 +3,11 @@ package io.muun.apollo.presentation.ui.select_amount import android.content.Context import android.content.Intent import android.os.Bundle -import butterknife.BindView -import butterknife.OnClick +import android.view.LayoutInflater +import androidx.viewbinding.ViewBinding import io.muun.apollo.R import io.muun.apollo.data.serialization.SerializationUtils +import io.muun.apollo.databinding.ActivitySelectAmountBinding import io.muun.apollo.domain.model.BitcoinAmount import io.muun.apollo.domain.model.BitcoinUnit import io.muun.apollo.presentation.ui.base.BaseActivity @@ -73,14 +74,17 @@ class SelectAmountActivity : BaseActivity(), SelectAmount } } - @BindView(R.id.select_amount_header) - lateinit var header: MuunHeader + private val binding: ActivitySelectAmountBinding + get() = getBinding() as ActivitySelectAmountBinding - @BindView(R.id.muun_amount_input) - lateinit var amountInput: MuunAmountInput + private val header: MuunHeader + get() = binding.selectAmountHeader - @BindView(R.id.confirm_amount_button) - lateinit var confirmButton: MuunButton + private val amountInput: MuunAmountInput + get() = binding.muunAmountInput + + private val confirmButton: MuunButton + get() = binding.confirmAmountButton override fun inject() { component.inject(this) @@ -90,6 +94,10 @@ class SelectAmountActivity : BaseActivity(), SelectAmount return R.layout.activity_select_amount } + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ActivitySelectAmountBinding::inflate + } + override fun initializeUi() { super.initializeUi() @@ -97,6 +105,10 @@ class SelectAmountActivity : BaseActivity(), SelectAmount header.showTitle(getString(R.string.select_amount_title)) header.setNavigation(MuunHeader.Navigation.BACK) + confirmButton.setOnClickListener { + onConfirmButtonClick() + } + amountInput.isEnabled = false // Wait for ExchangeRateProvider to be fully init } @@ -160,8 +172,7 @@ class SelectAmountActivity : BaseActivity(), SelectAmount return true } - @OnClick(R.id.confirm_amount_button) - fun onConfirmButtonClick() { + private fun onConfirmButtonClick() { presenter.confirmSelectedAmount() } diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_bitcoin_unit/SelectBitcoinUnitActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_bitcoin_unit/SelectBitcoinUnitActivity.kt index 35eb08c4..44cb54f6 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_bitcoin_unit/SelectBitcoinUnitActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_bitcoin_unit/SelectBitcoinUnitActivity.kt @@ -2,9 +2,11 @@ package io.muun.apollo.presentation.ui.select_bitcoin_unit import android.content.Context import android.content.Intent -import butterknife.BindView +import android.view.LayoutInflater +import androidx.viewbinding.ViewBinding import icepick.State import io.muun.apollo.R +import io.muun.apollo.databinding.ActivitySelectBitcoinUnitBinding import io.muun.apollo.domain.model.BitcoinUnit import io.muun.apollo.presentation.ui.base.BaseActivity import io.muun.apollo.presentation.ui.utils.UiUtils @@ -19,14 +21,17 @@ class SelectBitcoinUnitActivity: BaseActivity(), Sel Intent(context, SelectBitcoinUnitActivity::class.java) } - @BindView(R.id.select_currency_header) - lateinit var header: MuunHeader + private val binding: ActivitySelectBitcoinUnitBinding + get() = getBinding() as ActivitySelectBitcoinUnitBinding - @BindView(R.id.bitcoin_unit_btc) - lateinit var bitcoinUnitItem: MuunSettingItem + private val header: MuunHeader + get() = binding.selectCurrencyHeader - @BindView(R.id.bitcoin_unit_sat) - lateinit var satoshisUnitItem: MuunSettingItem + private val bitcoinUnitItem: MuunSettingItem + get() = binding.bitcoinUnitBtc + + private val satoshisUnitItem: MuunSettingItem + get() = binding.bitcoinUnitSat @State @JvmField @@ -38,6 +43,10 @@ class SelectBitcoinUnitActivity: BaseActivity(), Sel override fun getLayoutResource() = R.layout.activity_select_bitcoin_unit + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ActivitySelectBitcoinUnitBinding::inflate + } + override fun initializeUi() { super.initializeUi() diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_night_mode/SelectNightModeActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_night_mode/SelectNightModeActivity.kt index 2478d03a..101624d2 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_night_mode/SelectNightModeActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/select_night_mode/SelectNightModeActivity.kt @@ -2,10 +2,12 @@ package io.muun.apollo.presentation.ui.select_night_mode import android.content.Context import android.content.Intent +import android.view.LayoutInflater import android.view.View import androidx.appcompat.app.AppCompatDelegate -import butterknife.BindView +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.ActivitySelectNightModeBinding import io.muun.apollo.domain.model.NightMode import io.muun.apollo.presentation.ui.base.BaseActivity import io.muun.apollo.presentation.ui.utils.OS @@ -15,24 +17,29 @@ import io.muun.apollo.presentation.ui.view.MuunHeader import io.muun.apollo.presentation.ui.view.MuunHeader.Navigation import io.muun.apollo.presentation.ui.view.MuunSettingItem -class SelectNightModeActivity: BaseActivity(), SelectNightModeView { +class SelectNightModeActivity : BaseActivity(), SelectNightModeView { companion object { fun getStartActivityIntent(context: Context) = Intent(context, SelectNightModeActivity::class.java) } - @BindView(R.id.select_dark_mode_header) - lateinit var header: MuunHeader + private val binding: ActivitySelectNightModeBinding + get() = getBinding() as ActivitySelectNightModeBinding - @BindView(R.id.night_mode_dark) - lateinit var darkModeItem: MuunSettingItem + private val header: MuunHeader + get() = binding.selectDarkModeHeader - @BindView(R.id.night_mode_light) - lateinit var lightModeItem: MuunSettingItem - @BindView(R.id.night_mode_follow_system) - lateinit var followSystemItem: MuunSettingItem + private val darkModeItem: MuunSettingItem + get() = binding.nightModeDark + + + private val lightModeItem: MuunSettingItem + get() = binding.nightModeLight + + private val followSystemItem: MuunSettingItem + get() = binding.nightModeFollowSystem override fun inject() = component.inject(this) @@ -40,6 +47,10 @@ class SelectNightModeActivity: BaseActivity(), SelectN override fun getLayoutResource() = R.layout.activity_select_night_mode + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ActivitySelectNightModeBinding::inflate + } + override fun initializeUi() { super.initializeUi() @@ -102,12 +113,12 @@ class SelectNightModeActivity: BaseActivity(), SelectN presenter.reportNightModeChange(NightMode.DARK) } - NightMode.LIGHT -> { + NightMode.LIGHT -> { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) presenter.reportNightModeChange(NightMode.LIGHT) } - NightMode.FOLLOW_SYSTEM -> { + NightMode.FOLLOW_SYSTEM -> { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) presenter.reportNightModeChange(NightMode.FOLLOW_SYSTEM) } diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/send/SendActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/send/SendActivity.kt index 28bd749e..eda7200d 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/send/SendActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/send/SendActivity.kt @@ -3,13 +3,15 @@ package io.muun.apollo.presentation.ui.send import android.content.Context import android.content.Intent import android.os.Bundle +import android.view.LayoutInflater import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.LinearLayout -import butterknife.BindView +import androidx.viewbinding.ViewBinding import io.muun.apollo.BuildConfig import io.muun.apollo.R +import io.muun.apollo.databinding.ActivitySendBinding import io.muun.apollo.domain.model.OperationUri import io.muun.apollo.domain.model.P2PState import io.muun.apollo.presentation.ui.base.SingleFragmentActivity @@ -29,29 +31,32 @@ class SendActivity : SingleFragmentActivity(), SendView { Intent(context, SendActivity::class.java) } - @BindView(R.id.header) - lateinit var muunHeader: MuunHeader + private val binding: ActivitySendBinding + get() = getBinding() as ActivitySendBinding + private val muunHeader: MuunHeader + get() = binding.header - @BindView(R.id.paste_button) - lateinit var pasteButton: MuunButton + private val pasteButton: MuunButton + get() = binding.pasteButton - @BindView(R.id.uri_paster) - lateinit var uriPaster: MuunUriPaster + private val uriPaster: MuunUriPaster + get() = binding.uriPaster - @BindView(R.id.uri_input) - lateinit var uriInput: MuunUriInput + private val uriInput: MuunUriInput + get() = binding.uriInput - @BindView(R.id.uri_status_message) - lateinit var uriStatusMessage: StatusMessage + private val uriStatusMessage: StatusMessage + get() = binding.uriStatusMessage - @BindView(R.id.contact_list) - lateinit var contactList: MuunContactList + private val contactList: MuunContactList + get() = binding.contactList - @BindView(R.id.confirm) - lateinit var confirmButton: MuunButton - @BindView(R.id.button_layout) - lateinit var buttonLayout: MuunButtonLayout + private val confirmButton: MuunButton + get() = binding.confirm + + private val buttonLayout: MuunButtonLayout + get() = binding.buttonLayout override fun inject() = component.inject(this) @@ -59,6 +64,10 @@ class SendActivity : SingleFragmentActivity(), SendView { override fun getLayoutResource() = R.layout.activity_send + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return ActivitySendBinding::inflate + } + override fun getHeader() = muunHeader 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 26268521..85858f13 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,10 +1,13 @@ package io.muun.apollo.presentation.ui.settings.edit_password import android.text.TextUtils +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import android.widget.CheckBox -import butterknife.BindView +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.FragmentChangePasswordBinding import io.muun.apollo.domain.errors.UserFacingError import io.muun.apollo.presentation.ui.activity.extension.MuunDialog import io.muun.apollo.presentation.ui.base.BaseActivity @@ -15,24 +18,31 @@ import io.muun.common.Rules class ChangePasswordFragment : SingleFragment(), ChangePasswordView { - @BindView(R.id.change_password_input) - lateinit var passwordInput: MuunTextInput + private val binding: FragmentChangePasswordBinding + get() = getBinding() as FragmentChangePasswordBinding - @BindView(R.id.change_password_confirm_input) - lateinit var passwordConfirmInput: MuunTextInput + private val passwordInput: MuunTextInput + get() = binding.changePasswordInput - @BindView(R.id.change_password_condition) - lateinit var condition: CheckBox + private val passwordConfirmInput: MuunTextInput + get() = binding.changePasswordConfirmInput - @BindView(R.id.change_password_continue) - lateinit var continueButton: MuunButton + private val condition: CheckBox + get() = binding.changePasswordCondition + + private val continueButton: MuunButton + get() = binding.changePasswordContinue override fun inject() { component.inject(this) } override fun getLayoutResource(): Int { - return R.layout.change_password_fragment + return R.layout.fragment_change_password + } + + override fun bindingInflater(): (LayoutInflater, ViewGroup, Boolean) -> ViewBinding { + return FragmentChangePasswordBinding::inflate } override fun initializeUi(view: View) { @@ -40,19 +50,11 @@ class ChangePasswordFragment : SingleFragment(), Change 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() - } + 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() - } + validateInputs() } condition.setOnCheckedChangeListener { _, _ -> @@ -117,7 +119,10 @@ class ChangePasswordFragment : SingleFragment(), Change } private fun onContinueButtonClick() { - presenter.submitPassword(passwordInput.text.toString(), passwordConfirmInput.text.toString()) + presenter.submitPassword( + passwordInput.text.toString(), + passwordConfirmInput.text.toString() + ) } private fun isValidPassword(password: String) = diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/setup_password/SetupPasswordActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/setup_password/SetupPasswordActivity.kt index 950db7cd..9e47bbf4 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/setup_password/SetupPasswordActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/setup_password/SetupPasswordActivity.kt @@ -3,8 +3,10 @@ package io.muun.apollo.presentation.ui.setup_password import android.annotation.SuppressLint import android.content.Context import android.content.Intent -import butterknife.BindView +import android.view.LayoutInflater +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.ActivitySetupPasswordBinding import io.muun.apollo.domain.model.user.User import io.muun.apollo.presentation.ui.activity.extension.MuunDialog import io.muun.apollo.presentation.ui.base.SingleFragmentActivity @@ -25,8 +27,11 @@ class SetupPasswordActivity : SingleFragmentActivity ViewBinding { + return ActivitySetupPasswordBinding::inflate + } + override fun isPresenterPersistent() = true diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/single_action/SingleActionPresenter.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/single_action/SingleActionPresenter.java index 48fc9971..bb08e713 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/single_action/SingleActionPresenter.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/single_action/SingleActionPresenter.java @@ -18,8 +18,7 @@ public SingleActionPresenter() { * Executes a REQUEST_UPDATE action from SingleActionActivity. */ public void requestUpdateAction() { - navigator.navigateToLogout(getContext()); - + logout(); navigator.openPlayStore(getContext()); } @@ -27,7 +26,7 @@ public void requestUpdateAction() { * Executes a REQUEST_RESTART action from SingleActionActivity. */ public void requestRestartAction() { - navigator.navigateToLogout(getContext()); + logout(); } /** diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/single_action/V2SingleActionActivity.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/single_action/V2SingleActionActivity.java index e5095cf6..48bcc637 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/single_action/V2SingleActionActivity.java +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/single_action/V2SingleActionActivity.java @@ -1,6 +1,7 @@ package io.muun.apollo.presentation.ui.single_action; import io.muun.apollo.R; +import io.muun.apollo.databinding.SingleActionActivityV2Binding; import io.muun.apollo.presentation.ui.base.SingleFragment; import io.muun.apollo.presentation.ui.base.SingleFragmentActivity; import io.muun.apollo.presentation.ui.fragments.need_recovery_code.NeedRecoveryCodeFragment; @@ -12,6 +13,9 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.view.LayoutInflater; +import androidx.viewbinding.ViewBinding; +import kotlin.jvm.functions.Function1; import javax.validation.constraints.NotNull; @@ -47,6 +51,11 @@ protected int getLayoutResource() { return R.layout.single_action_activity_v2; } + @Override + protected Function1 bindingInflater() { + return SingleActionActivityV2Binding::inflate; + } + @Override protected int getFragmentsContainer() { return R.id.fragment_container; diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/taproot_setup/TaprootSetupActivity.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/taproot_setup/TaprootSetupActivity.kt index e3e47da2..6bd46ecc 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/taproot_setup/TaprootSetupActivity.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/taproot_setup/TaprootSetupActivity.kt @@ -3,8 +3,10 @@ package io.muun.apollo.presentation.ui.taproot_setup import android.content.Context import android.content.Intent import android.os.Bundle -import butterknife.BindView +import android.view.LayoutInflater +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.TaprootSetupActivityBinding import io.muun.apollo.presentation.ui.base.SingleFragment import io.muun.apollo.presentation.ui.base.SingleFragmentActivity import io.muun.apollo.presentation.ui.fragments.ek_save.EmergencyKitSaveFragment @@ -24,8 +26,11 @@ class TaprootSetupActivity : SingleFragmentActivity(), Ta } } - @BindView(R.id.header) - lateinit var headerView: MuunHeader + private val binding: TaprootSetupActivityBinding + get() = getBinding() as TaprootSetupActivityBinding + + private val headerView: MuunHeader + get() = binding.header override fun inject() { component.inject(this) @@ -37,6 +42,10 @@ class TaprootSetupActivity : SingleFragmentActivity(), Ta override fun getLayoutResource() = R.layout.taproot_setup_activity + override fun bindingInflater(): (LayoutInflater) -> ViewBinding { + return TaprootSetupActivityBinding::inflate + } + override fun getFragmentsContainer() = R.id.fragment_container diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/utils/LinkBuilder.java b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/utils/LinkBuilder.java deleted file mode 100644 index e8090f5d..00000000 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/utils/LinkBuilder.java +++ /dev/null @@ -1,102 +0,0 @@ -package io.muun.apollo.presentation.ui.utils; - -import io.muun.apollo.domain.analytics.Analytics; -import io.muun.apollo.domain.analytics.AnalyticsEvent; -import io.muun.apollo.domain.model.SubmarineSwapReceiver; -import io.muun.apollo.presentation.app.di.PerApplication; -import io.muun.apollo.presentation.ui.view.RichText; -import io.muun.common.bitcoinj.NetworkParametersHelper; - -import android.content.Context; -import android.text.TextUtils; -import org.bitcoinj.core.NetworkParameters; - -import javax.inject.Inject; - -@PerApplication -public class LinkBuilder { - - private final Context context; - private final NetworkParameters network; - private final Analytics analytics; - - @Inject - LinkBuilder(Context context, NetworkParameters network, Analytics analytics) { - this.context = context; - this.network = network; - this.analytics = analytics; - } - - /** - * Create a RichText containing a clickable link to a transaction in a block explorer. - */ - public RichText transactionLink(String txId) { - return createLink(txId, rawTransactionLink(txId), "block_explorer_tx"); - } - - /** - * Create a plain-text link to a transaction in a block explorer. - */ - public String rawTransactionLink(String txId) { - final String urlRoot = isTestnet(network) - ? "https://mempool.space/testnet/tx/" - : "https://mempool.space/tx/"; - - return urlRoot + txId; - } - - /** - * Create a RichText containing a clickable link to an address in a block explorer. - */ - public RichText addressLink(String address) { - - final String urlRoot = isTestnet(network) - ? "https://mempool.space/testnet/address/" - : "https://mempool.space/address/"; - - return createLink(address, urlRoot + address, "block_explorer_address"); - } - - /** - * Create a RichText containing a clickable link to a lightning node in an explorer. - */ - public RichText lightningNodeLink(SubmarineSwapReceiver receiver, String linkText) { - - final String urlRoot = isTestnet(network) - ? "https://1ml.com/testnet/node/" - : "https://1ml.com/node/"; - - return createLink(linkText, urlRoot + receiver.getPublicKey(), "node_explorer"); - } - - /** - * Create a RichText containing a clickable link to the recovery tool repository. - */ - public RichText recoveryToolLink() { - final String url = "https://github.com/muun/recovery"; - - return createLink(url, url, "recovery_tool"); - } - - private String getLnLinkText(SubmarineSwapReceiver receiver) { - if (!TextUtils.isEmpty(receiver.getAlias())) { - return receiver.getAlias(); - } - - return receiver.getFormattedDestination(); - } - - /** - * Create a RichText containing a clickable link. - */ - private RichText createLink(String text, String url, String trackingName) { - return new RichText(text).setLink(() -> { - ExtensionsKt.openInBrowser(context, url); - analytics.report(new AnalyticsEvent.E_OPEN_WEB(trackingName, url)); - }); - } - - private static boolean isTestnet(NetworkParameters network) { - return NetworkParametersHelper.isTestingNetwork(network); - } -} diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/utils/LinkBuilder.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/utils/LinkBuilder.kt new file mode 100644 index 00000000..80ed0774 --- /dev/null +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/utils/LinkBuilder.kt @@ -0,0 +1,98 @@ +package io.muun.apollo.presentation.ui.utils + +import android.content.Context +import android.text.TextUtils +import io.muun.apollo.domain.analytics.Analytics +import io.muun.apollo.domain.analytics.AnalyticsEvent.E_OPEN_WEB +import io.muun.apollo.domain.model.SubmarineSwapReceiver +import io.muun.apollo.presentation.app.di.PerApplication +import io.muun.apollo.presentation.ui.view.RichText +import io.muun.common.bitcoinj.NetworkParametersHelper +import org.bitcoinj.core.NetworkParameters +import javax.inject.Inject + +@PerApplication +class LinkBuilder @Inject internal constructor( + private val context: Context, + private val network: NetworkParameters, + private val analytics: Analytics, +) { + + companion object { + private fun isTestnet(network: NetworkParameters): Boolean { + return NetworkParametersHelper.isTestingNetwork(network) + } + } + + /** + * Create a RichText containing a clickable link to a transaction in a block explorer. + */ + fun transactionLink(txId: String): RichText { + return createLink(txId, rawTransactionLink(txId), "block_explorer_tx") + } + + /** + * Create a plain-text link to a transaction in a block explorer. + */ + fun rawTransactionLink(txId: String): String { + val urlRoot = if (isTestnet(network)) { + "https://mempool.space/testnet/tx/" + } else { + "https://mempool.space/tx/" + } + + return "$urlRoot$txId?mode=details" + } + + /** + * Create a RichText containing a clickable link to an address in a block explorer. + */ + fun addressLink(address: String): RichText { + val urlRoot = if (isTestnet(network)) { + "https://mempool.space/testnet/address/" + } else { + "https://mempool.space/address/" + } + + return createLink(address, urlRoot + address, "block_explorer_address") + } + + /** + * Create a RichText containing a clickable link to a lightning node in an explorer. + */ + fun lightningNodeLink(receiver: SubmarineSwapReceiver, linkText: String): RichText { + val urlRoot = if (isTestnet(network)) { + "https://1ml.com/testnet/node/" + } else { + "https://1ml.com/node/" + } + + return createLink(linkText, urlRoot + receiver.publicKey, "node_explorer") + } + + /** + * Create a RichText containing a clickable link to the recovery tool repository. + */ + fun recoveryToolLink(): RichText { + val url = "https://github.com/muun/recovery" + return createLink(url, url, "recovery_tool") + } + + private fun getLnLinkText(receiver: SubmarineSwapReceiver): String? { + return if (!TextUtils.isEmpty(receiver.alias)) { + receiver.alias + } else { + receiver.formattedDestination + } + } + + /** + * Create a RichText containing a clickable link. + */ + private fun createLink(text: String, url: String, trackingName: String): RichText { + return RichText(text).setLink { + context.openInBrowser(url) + analytics.report(E_OPEN_WEB(trackingName, url)) + } + } +} \ No newline at end of file diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/NewOpBadge.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/NewOpBadge.kt index 5bf411b3..eea0192a 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/NewOpBadge.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/NewOpBadge.kt @@ -8,10 +8,10 @@ import android.view.animation.Animation import android.view.animation.AnimationUtils import android.widget.TextView import androidx.annotation.AnimRes -import butterknife.BindColor -import butterknife.BindDrawable -import butterknife.BindView +import androidx.core.content.ContextCompat +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.ViewNewOpBadgeBinding import io.muun.apollo.domain.model.BitcoinUnit import io.muun.apollo.presentation.ui.helper.BitcoinHelper import io.muun.apollo.presentation.ui.helper.isBtc @@ -24,39 +24,45 @@ import javax.money.MonetaryAmount class NewOpBadge @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, - style: Int = 0 + style: Int = 0, ) : MuunView(context, attrs, style) { + private val binding: ViewNewOpBadgeBinding + get() = _binding as ViewNewOpBadgeBinding + // Components: - @BindView(R.id.badge_text) - lateinit var text: TextView + + private val text: TextView + get() = binding.badgeText // Resources: - @BindDrawable(R.drawable.new_op_badge_blue_bkg) - lateinit var outgoingTxBkg: Drawable + private val outgoingTxBkg: Drawable? + get() = ContextCompat.getDrawable(context, R.drawable.new_op_badge_blue_bkg) - @BindColor(R.color.blue) - @JvmField - internal var outgoingTxColor: Int = 0 + private val outgoingTxColor: Int + get() = ContextCompat.getColor(context, R.color.blue) - @BindDrawable(R.drawable.new_op_badge_green_bkg) - lateinit var incomingTxBkg: Drawable + private val incomingTxBkg: Drawable? + get() = ContextCompat.getDrawable(context, R.drawable.new_op_badge_green_bkg) - @BindColor(R.color.green) - @JvmField - internal var incomingTxColor: Int = 0 + private val incomingTxColor: Int + get() = ContextCompat.getColor(context, R.color.green) override val layoutResource: Int get() = R.layout.view_new_op_badge + override fun viewBinder(): ((View) -> ViewBinding) { + return ViewNewOpBadgeBinding::bind + } + fun setAmount(amountInBtc: MonetaryAmount, bitcoinUnit: BitcoinUnit) { Preconditions.checkArgument(!amountInBtc.isZero) Preconditions.checkArgument(amountInBtc.isBtc()) val amountInSats = BitcoinUtils.bitcoinsToSatoshis(amountInBtc) var content = BitcoinHelper.formatFlexBitcoinAmount( - amountInSats, true, bitcoinUnit, locale() + amountInSats, true, bitcoinUnit, locale() ) if (amountInBtc.isPositive) { diff --git a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/NoticeBanner.kt b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/NoticeBanner.kt index 6b4b0cd3..20a32b33 100644 --- a/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/NoticeBanner.kt +++ b/android/apolloui/src/main/java/io/muun/apollo/presentation/ui/view/NoticeBanner.kt @@ -2,13 +2,15 @@ package io.muun.apollo.presentation.ui.view import android.content.Context import android.util.AttributeSet +import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.annotation.ColorRes import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat -import butterknife.BindView +import androidx.viewbinding.ViewBinding import io.muun.apollo.R +import io.muun.apollo.databinding.ViewNoticeBannerBinding import io.muun.apollo.presentation.ui.utils.UiUtils class NoticeBanner @JvmOverloads constructor(c: Context, a: AttributeSet? = null, s: Int = 0) : @@ -26,15 +28,22 @@ class NoticeBanner @JvmOverloads constructor(c: Context, a: AttributeSet? = null } } - @BindView(R.id.banner_icon) - lateinit var icon: ImageView + private val binding: ViewNoticeBannerBinding + get() = _binding as ViewNoticeBannerBinding - @BindView(R.id.banner_text) - lateinit var textView: TextView + private val icon: ImageView + get() = binding.bannerIcon + + private val textView: TextView + get() = binding.bannerText override val layoutResource: Int get() = R.layout.view_notice_banner + override fun viewBinder(): ((View) -> ViewBinding) { + return ViewNoticeBannerBinding::bind + } + override fun setUp(context: Context, attrs: AttributeSet?) { super.setUp(context, attrs) diff --git a/android/apolloui/src/main/res/layout/activity_high_fees.xml b/android/apolloui/src/main/res/layout/activity_high_fees.xml index cc7def83..1784118d 100644 --- a/android/apolloui/src/main/res/layout/activity_high_fees.xml +++ b/android/apolloui/src/main/res/layout/activity_high_fees.xml @@ -1,8 +1,6 @@ diff --git a/android/apolloui/src/main/res/layout/activity_select_bitcoin_unit.xml b/android/apolloui/src/main/res/layout/activity_select_bitcoin_unit.xml index f2c3d390..ecd27f2e 100644 --- a/android/apolloui/src/main/res/layout/activity_select_bitcoin_unit.xml +++ b/android/apolloui/src/main/res/layout/activity_select_bitcoin_unit.xml @@ -1,8 +1,6 @@ @@ -53,7 +51,7 @@ android:id="@+id/change_password_condition" android:text="@string/change_password_condition" /> - + diff --git a/android/apolloui/src/main/res/layout/fragment_create_email_help.xml b/android/apolloui/src/main/res/layout/fragment_create_email_help.xml index df6c4671..b83f6e46 100644 --- a/android/apolloui/src/main/res/layout/fragment_create_email_help.xml +++ b/android/apolloui/src/main/res/layout/fragment_create_email_help.xml @@ -1,6 +1,5 @@ diff --git a/android/apolloui/src/main/res/layout/home_activity.xml b/android/apolloui/src/main/res/layout/home_activity.xml index bafefb36..b47b0648 100644 --- a/android/apolloui/src/main/res/layout/home_activity.xml +++ b/android/apolloui/src/main/res/layout/home_activity.xml @@ -1,6 +1,5 @@ diff --git a/android/apolloui/src/main/res/layout/single_action_activity_v2.xml b/android/apolloui/src/main/res/layout/single_action_activity_v2.xml index b5d5d49a..a55a64ae 100644 --- a/android/apolloui/src/main/res/layout/single_action_activity_v2.xml +++ b/android/apolloui/src/main/res/layout/single_action_activity_v2.xml @@ -1,8 +1,6 @@ diff --git a/android/apolloui/src/main/res/layout/taproot_setup_activity.xml b/android/apolloui/src/main/res/layout/taproot_setup_activity.xml index 550a3bc8..03b96c60 100644 --- a/android/apolloui/src/main/res/layout/taproot_setup_activity.xml +++ b/android/apolloui/src/main/res/layout/taproot_setup_activity.xml @@ -1,8 +1,6 @@ + No + + Sí, cancelar Cancelar Aceptar Seguir @@ -79,7 +82,7 @@ ¿Cancelar pago? - Estás a punto de cancelar el pago y volver a la pantalla de inicio. + ¿Quieres cancelar el pago y volver a la pantalla de inicio? Pagar por lightning diff --git a/android/apolloui/src/main/res/values/strings.xml b/android/apolloui/src/main/res/values/strings.xml index 275a2e59..78f7ff4d 100644 --- a/android/apolloui/src/main/res/values/strings.xml +++ b/android/apolloui/src/main/res/values/strings.xml @@ -6,6 +6,9 @@ Muun + No + Yes + Yes, cancel Cancel Dismiss Stay @@ -83,7 +86,7 @@ Cancel payment? - You are about to cancel this payment and go back to the home screen + Do you want to cancel this payment and go back to the home screen? Pay via lightning diff --git a/build.gradle b/build.gradle index db6dc2a3..922ac599 100644 --- a/build.gradle +++ b/build.gradle @@ -6,16 +6,20 @@ buildscript { google() mavenCentral() maven { url 'https://jitpack.io' } + maven { url 'https://storage.googleapis.com/r8-releases/raw'} } dependencies { // Add third party gradle plugins + // Android Gradle Plugin (AGP) classpath 'com.android.tools.build:gradle:7.4.2' + // Add in custom r8 version, remove this when updating the android gradle plugin. + classpath 'com.android.tools:r8:8.0.28' // 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' + classpath 'com.github.muun.sqldelight:gradle-plugin:1.5.5-reproducible-build' } ext { - global_kotlin_version = '1.6.10' + global_kotlin_version = '1.8.20' } } @@ -39,8 +43,7 @@ subprojects { ext { // GLOBAL LIBRARY VERSIONS - global_version_dagger = '2.40.5' - global_version_lombok = '1.18.22' + global_version_dagger = '2.52' global_version_mockito = '3.8.0' global_version_moneta = '1.1' diff --git a/common/src/main/java/io/muun/common/Rules.java b/common/src/main/java/io/muun/common/Rules.java index b0d9ae72..821432bf 100644 --- a/common/src/main/java/io/muun/common/Rules.java +++ b/common/src/main/java/io/muun/common/Rules.java @@ -16,7 +16,7 @@ public class Rules { /** * The confirmation target (in blocks) for fee options displayed in clients. * - * @Deprecated + * {@code @Deprecated} * New clients now use dynamic fee targets based on Houston and Fee Estimator logic. * We are keeping these for historic reasons and because Houston will default to them if * Fee Estimator is unresponsive. Also, during client side migration dynamic fee targets are @@ -45,7 +45,7 @@ public class Rules { public static final double OP_MAXIMUM_FEE_RATE = 999d / VBYTE_TO_WEIGHT_UNIT_RATIO; /** Added fee rate when the min fee is above 1 sat/vbyte. */ - public static final double OP_MIN_FEE_DELTA = 2d / VBYTE_TO_WEIGHT_UNIT_RATIO; + public static final double OP_MIN_FEE_DELTA = 0.1d / VBYTE_TO_WEIGHT_UNIT_RATIO; /** * Convert sats/WU to sats/vbyte. diff --git a/common/src/main/java/io/muun/common/api/AndroidAppInfoJson.java b/common/src/main/java/io/muun/common/api/AndroidAppInfoJson.java new file mode 100644 index 00000000..18905f59 --- /dev/null +++ b/common/src/main/java/io/muun/common/api/AndroidAppInfoJson.java @@ -0,0 +1,44 @@ +package io.muun.common.api; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class AndroidAppInfoJson { + + public String name; + + public String label; + + public Integer icon; + + public Boolean debuggable; + + public Boolean persistent; + + + /** + * Json constructor. + */ + @SuppressWarnings("unused") // Jackson requires it + public AndroidAppInfoJson() { + } + + /** + * Code constructor. + */ + public AndroidAppInfoJson( + String name, + String label, + Integer icon, + Boolean debuggable, + Boolean persistent + ) { + this.name = name; + this.label = label; + this.icon = icon; + this.debuggable = debuggable; + this.persistent = persistent; + } +} diff --git a/common/src/main/java/io/muun/common/api/AndroidBuildInfoJson.java b/common/src/main/java/io/muun/common/api/AndroidBuildInfoJson.java new file mode 100644 index 00000000..115a0ebe --- /dev/null +++ b/common/src/main/java/io/muun/common/api/AndroidBuildInfoJson.java @@ -0,0 +1,91 @@ +package io.muun.common.api; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.List; +import javax.annotation.Nullable; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class AndroidBuildInfoJson { + + @Nullable + public List abis; + + @Nullable + public String fingerprint; + + @Nullable + public String hardware; + + @Nullable + public String bootloader; + + @Nullable + public String manufacturer; + + @Nullable + public String brand; + + @Nullable + public String display; + + @Nullable + public Long time; + + @Nullable + public String host; + + @Nullable + public String type; + + @Nullable + public String radioVersion; + + @Nullable + public String securityPatch; + + @Nullable + public String baseOs; + + /** + * Json constructor. + */ + @SuppressWarnings("unused") // Jackson requires it + public AndroidBuildInfoJson() { + } + + /** + * Code constructor. + */ + public AndroidBuildInfoJson( + @Nullable List abis, + @Nullable String fingerprint, + @Nullable String hardware, + @Nullable String bootloader, + @Nullable String manufacturer, + @Nullable String brand, + @Nullable String display, + @Nullable Long time, + @Nullable String host, + @Nullable String type, + @Nullable String radioVersion, + @Nullable String securityPatch, + @Nullable String baseOs + ) { + this.abis = abis; + this.fingerprint = fingerprint; + this.hardware = hardware; + this.bootloader = bootloader; + this.manufacturer = manufacturer; + this.brand = brand; + this.display = display; + this.time = time; + this.host = host; + this.type = type; + this.radioVersion = radioVersion; + this.securityPatch = securityPatch; + this.baseOs = baseOs; + } +} diff --git a/common/src/main/java/io/muun/common/api/AndroidDeviceFeaturesJson.java b/common/src/main/java/io/muun/common/api/AndroidDeviceFeaturesJson.java new file mode 100644 index 00000000..455c29ee --- /dev/null +++ b/common/src/main/java/io/muun/common/api/AndroidDeviceFeaturesJson.java @@ -0,0 +1,108 @@ +package io.muun.common.api; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import javax.annotation.Nullable; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class AndroidDeviceFeaturesJson { + + @Nullable + public AndroidDeviceFeaturesValue touch; + + @Nullable + public AndroidDeviceFeaturesValue proximity; + + @Nullable + public AndroidDeviceFeaturesValue accelerometer; + + @Nullable + public AndroidDeviceFeaturesValue gyro; + + @Nullable + public AndroidDeviceFeaturesValue compass; + + @Nullable + public AndroidDeviceFeaturesValue telephony; + + @Nullable + public AndroidDeviceFeaturesValue cdma; + + @Nullable + public AndroidDeviceFeaturesValue gsm; + + @Nullable + public AndroidDeviceFeaturesValue cameras; + + @Nullable + public AndroidDeviceFeaturesValue pc; + + @Nullable + public AndroidDeviceFeaturesValue pip; + + @Nullable + public AndroidDeviceFeaturesValue dactylogram; + + /** + * Json constructor. + */ + @SuppressWarnings("unused") // Jackson requires it + public AndroidDeviceFeaturesJson() { + } + + public enum AndroidDeviceFeaturesValue { + PRESENT, + ABSENT, + UNKNOWN + } + + + private AndroidDeviceFeaturesValue mapDeviceFeaturesValue(Integer value) { + + if (value == null) { + return null; + } + + switch (value) { + case 0: + return AndroidDeviceFeaturesValue.ABSENT; + case 1: + return AndroidDeviceFeaturesValue.PRESENT; + default: + return AndroidDeviceFeaturesValue.UNKNOWN; + } + } + + /** + * Code constructor. + */ + public AndroidDeviceFeaturesJson( + @Nullable Integer touch, + @Nullable Integer proximity, + @Nullable Integer accelerometer, + @Nullable Integer gyro, + @Nullable Integer compass, + @Nullable Integer telephony, + @Nullable Integer cdma, + @Nullable Integer gsm, + @Nullable Integer cameras, + @Nullable Integer pc, + @Nullable Integer pip, + @Nullable Integer dactylogram + ) { + this.touch = mapDeviceFeaturesValue(touch); + this.proximity = mapDeviceFeaturesValue(proximity); + this.accelerometer = mapDeviceFeaturesValue(accelerometer); + this.gyro = mapDeviceFeaturesValue(gyro); + this.compass = mapDeviceFeaturesValue(compass); + this.telephony = mapDeviceFeaturesValue(telephony); + this.cdma = mapDeviceFeaturesValue(cdma); + this.gsm = mapDeviceFeaturesValue(gsm); + this.cameras = mapDeviceFeaturesValue(cameras); + this.pc = mapDeviceFeaturesValue(pc); + this.pip = mapDeviceFeaturesValue(pip); + this.dactylogram = mapDeviceFeaturesValue(dactylogram); + } +} \ No newline at end of file diff --git a/common/src/main/java/io/muun/common/api/ClientJson.java b/common/src/main/java/io/muun/common/api/ClientJson.java index 05c581b0..5f48ee07 100644 --- a/common/src/main/java/io/muun/common/api/ClientJson.java +++ b/common/src/main/java/io/muun/common/api/ClientJson.java @@ -66,7 +66,8 @@ public class ClientJson { @Since(apolloVersion = 1003) @Deprecated( atApolloVersion = Supports.RefreshTotalInternalStorageAndRam.APOLLO, - atFalconVersion = Supports.RefreshTotalInternalStorageAndRam.FALCON) + atFalconVersion = Supports.RefreshTotalInternalStorageAndRam.FALCON + ) @Nullable // Before that ;) (and after deprecation) public Long totalRamStorage; @@ -170,6 +171,42 @@ public class ClientJson { @Nullable // Before that ;) public String googlePlayVersionName; + // @TODO: Add @Since with release version + @Nullable // Before that ;) + public AndroidBuildInfoJson androidBuildInfo; + + // @TODO: Add @Since with release version + @Nullable // Before that ;) + public AndroidAppInfoJson androidAppInfo; + + // @TODO: Add @Since with release version + @Nullable // Before that ;) + public AndroidDeviceFeaturesJson androidDeviceFeatures; + + // @TODO: Add @Since with release version + @Nullable // Before that ;) + public String androidSignatureHash; + + // @TODO: Add @Since with release version + @Nullable // Before that ;) + public Boolean androidQuickEmuProps; + + // @TODO: Add @Since with release version + @Nullable // Before that ;) + public Integer androidEmArchitecture; + + // @TODO: Add @Since with release version + @Nullable // Before that ;) + public Boolean androidSecurityEnhancedBuild; + + // @TODO: Add @Since with release version + @Nullable // Before that ;) + public Boolean androidBridgeRootService; + + // @TODO: Add @Since with release version + @Nullable // Before that ;) + public Long androidAppSize; + /** * Json constructor. */ @@ -180,38 +217,47 @@ public ClientJson() { /** * Apollo constructor. */ - public ClientJson(final ClientTypeJson type, - final String buildType, - final int version, - @Nullable final String versionName, - @Nullable final String deviceModel, - @Nullable final Long timezoneOffsetInSeconds, - @Nullable final String language, - @Nullable final String bigQueryPseudoId, - @Nullable final Boolean isRootHint, - @Nullable String androidId, - final long androidCreationTimestampInMilliseconds, - @Nullable List systemUsersInfo, - @SuppressWarnings("NullableProblems") - final Map drmProviderClientIds, - final long androidElapsedRealtimeAtSessionCreationInMillis, - final long androidUptimeAtSessionCreationInMillis, - @Nullable final String installSource, - @Nullable final String installInitiatingPackageName, - @Nullable final String installInitiatingPackageSigningInfo, - @Nullable final String osBuildFingerprint, - @Nullable final String hardwareName, - @Nullable final String systemBootloaderVersion, - final int bootCount, - @Nullable final String glEsVersion, - @Nullable final Map cpuInfoLegacy, - @Nullable final List> cpuCommonInfo, - @Nullable final List>> cpuPerProcessorInfo, - @Nullable final Long googlePlayServicesVersionCode, - @Nullable final String googlePlayServicesVersionName, - @Nullable final Integer googlePlayServicesClientVersionCode, - @Nullable final Long googlePlayVersionCode, - @Nullable final String googlePlayVersionName + public ClientJson( + final ClientTypeJson type, + final String buildType, + final int version, + @Nullable final String versionName, + @Nullable final String deviceModel, + @Nullable final Long timezoneOffsetInSeconds, + @Nullable final String language, + @Nullable final String bigQueryPseudoId, + @Nullable final Boolean isRootHint, + @Nullable String androidId, + final long androidCreationTimestampInMilliseconds, + @Nullable List systemUsersInfo, + @SuppressWarnings("NullableProblems") final Map drmProviderClientIds, + final long androidElapsedRealtimeAtSessionCreationInMillis, + final long androidUptimeAtSessionCreationInMillis, + @Nullable final String installSource, + @Nullable final String installInitiatingPackageName, + @Nullable final String installInitiatingPackageSigningInfo, + @Nullable final String osBuildFingerprint, + @Nullable final String hardwareName, + @Nullable final String systemBootloaderVersion, + final int bootCount, + @Nullable final String glEsVersion, + @Nullable final Map cpuInfoLegacy, + @Nullable final List> cpuCommonInfo, + @Nullable final List>> cpuPerProcessorInfo, + @Nullable final Long googlePlayServicesVersionCode, + @Nullable final String googlePlayServicesVersionName, + @Nullable final Integer googlePlayServicesClientVersionCode, + @Nullable final Long googlePlayVersionCode, + @Nullable final String googlePlayVersionName, + @Nullable final AndroidBuildInfoJson androidBuildInfo, + @Nullable final AndroidAppInfoJson androidAppInfo, + @Nullable final AndroidDeviceFeaturesJson androidDeviceFeatures, + @Nullable final String androidSignatureHash, + @Nullable final Boolean androidQuickEmuProps, + @Nullable final Integer androidEmachineArchitecture, + @Nullable final Boolean androidSecurityEnhancedBuild, + @Nullable final Boolean androidBridgeRootService, + @Nullable final Long androidAppSize ) { this.type = type; this.buildType = buildType; @@ -246,5 +292,14 @@ public ClientJson(final ClientTypeJson type, this.googlePlayServicesClientVersionCode = googlePlayServicesClientVersionCode; this.googlePlayVersionCode = googlePlayVersionCode; this.googlePlayVersionName = googlePlayVersionName; + this.androidBuildInfo = androidBuildInfo; + this.androidAppInfo = androidAppInfo; + this.androidDeviceFeatures = androidDeviceFeatures; + this.androidSignatureHash = androidSignatureHash; + this.androidQuickEmuProps = androidQuickEmuProps; + this.androidEmArchitecture = androidEmachineArchitecture; + this.androidSecurityEnhancedBuild = androidSecurityEnhancedBuild; + this.androidBridgeRootService = androidBridgeRootService; + this.androidAppSize = androidAppSize; } } diff --git a/common/src/main/java/io/muun/common/api/MuunFeatureJson.java b/common/src/main/java/io/muun/common/api/MuunFeatureJson.java index bb4cebb6..943e6981 100644 --- a/common/src/main/java/io/muun/common/api/MuunFeatureJson.java +++ b/common/src/main/java/io/muun/common/api/MuunFeatureJson.java @@ -13,5 +13,6 @@ public enum MuunFeatureJson { TAPROOT_PREACTIVATION, APOLLO_BIOMETRICS, HIGH_FEES_HOME_BANNER, - HIGH_FEES_RECEIVE_FLOW; + HIGH_FEES_RECEIVE_FLOW, + EFFECTIVE_FEES_CALCULATION; } diff --git a/common/src/main/java/io/muun/common/api/OperationCreatedJson.java b/common/src/main/java/io/muun/common/api/OperationCreatedJson.java index 360ba0c1..798f1ebb 100644 --- a/common/src/main/java/io/muun/common/api/OperationCreatedJson.java +++ b/common/src/main/java/io/muun/common/api/OperationCreatedJson.java @@ -34,6 +34,7 @@ public class OperationCreatedJson { /** * Json constructor. */ + @SuppressWarnings("unused") public OperationCreatedJson() { } @@ -41,7 +42,7 @@ public OperationCreatedJson() { * Houston constructor. */ public OperationCreatedJson(OperationJson operation, - PartiallySignedTransactionJson partiallySignedTransaction, + @Nullable PartiallySignedTransactionJson partiallySignedTransaction, NextTransactionSizeJson nextTransactionSize, @Nullable MuunAddressJson changeAddress) { diff --git a/common/src/main/java/io/muun/common/api/RealTimeFeesJson.java b/common/src/main/java/io/muun/common/api/RealTimeFeesJson.java new file mode 100644 index 00000000..f6ec2f3f --- /dev/null +++ b/common/src/main/java/io/muun/common/api/RealTimeFeesJson.java @@ -0,0 +1,59 @@ +package io.muun.common.api; + +import io.muun.common.dates.MuunZonedDateTime; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.List; +import javax.validation.constraints.NotNull; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class RealTimeFeesJson { + + /** + * Each fee bump function is codified as a base64 string. + * The order of these functions is linked to the list of utxos' outpoints in realtime/fees API. + * TODO we should be able to link to UnconfirmedOutpointsJson (lives in clients module). + */ + @NotNull + public List feeBumpFunctions; + + @NotNull + public TargetFeeRatesJson targetFeeRates; + + @NotNull + public double minMempoolFeeRateInSatPerVbyte; + + @NotNull + public double minFeeRateIncrementToReplaceByFeeInSatPerVbyte; + + @NotNull + public MuunZonedDateTime computedAt; + + /** + * Json Constructor. + */ + public RealTimeFeesJson() { + } + + /** + * All args constructor. + */ + public RealTimeFeesJson( + List feeBumpFunctions, + TargetFeeRatesJson targetFeeRates, + double minMempoolFeeRateInSatPerVbyte, + double minFeeRateIncrementToReplaceByFeeInSatPerVbyte, + MuunZonedDateTime computedAt + ) { + + this.feeBumpFunctions = feeBumpFunctions; + this.targetFeeRates = targetFeeRates; + this.minMempoolFeeRateInSatPerVbyte = minMempoolFeeRateInSatPerVbyte; + this.minFeeRateIncrementToReplaceByFeeInSatPerVbyte = + minFeeRateIncrementToReplaceByFeeInSatPerVbyte; + this.computedAt = computedAt; + } +} \ No newline at end of file diff --git a/common/src/main/java/io/muun/common/api/SizeForAmountJson.java b/common/src/main/java/io/muun/common/api/SizeForAmountJson.java index e5711541..264a7b0b 100644 --- a/common/src/main/java/io/muun/common/api/SizeForAmountJson.java +++ b/common/src/main/java/io/muun/common/api/SizeForAmountJson.java @@ -24,26 +24,47 @@ public class SizeForAmountJson { @NotNull public Integer deltaInWeightUnits; + @NotNull + public String derivationPath; + + @NotNull + public Integer addressVersion; /** * Json constructor. */ public SizeForAmountJson() { + // } /** * Houston constructor. */ - public SizeForAmountJson(Long amountInSatoshis, - Long sizeInBytes, - String outpoint, - UtxoStatusJson status, - Integer deltaInWeightUnits) { + public SizeForAmountJson( + Long amountInSatoshis, + Long sizeInBytes, + String outpoint, + UtxoStatusJson status, + Integer deltaInWeightUnits, + String derivationPath, + Integer addressVersion + + ) { this.amountInSatoshis = amountInSatoshis; this.sizeInBytes = sizeInBytes; this.outpoint = outpoint; this.status = status; this.deltaInWeightUnits = deltaInWeightUnits; + this.derivationPath = derivationPath; + this.addressVersion = addressVersion; + } + + /** + * Return the transaction id associated to the given outpoint. + * Recall that outpoints are of the form "txId:outputIndex". + */ + public String getOutpointTxId() { + return outpoint.substring(0, outpoint.indexOf(":")); } } diff --git a/common/src/main/java/io/muun/common/api/SubmarineSwapFundingOutputJson.java b/common/src/main/java/io/muun/common/api/SubmarineSwapFundingOutputJson.java index 3c065085..85000e73 100644 --- a/common/src/main/java/io/muun/common/api/SubmarineSwapFundingOutputJson.java +++ b/common/src/main/java/io/muun/common/api/SubmarineSwapFundingOutputJson.java @@ -62,6 +62,7 @@ public class SubmarineSwapFundingOutputJson { /** * Json constructor. */ + @SuppressWarnings("unused") public SubmarineSwapFundingOutputJson() { } diff --git a/common/src/main/java/io/muun/common/api/TargetFeeRatesJson.java b/common/src/main/java/io/muun/common/api/TargetFeeRatesJson.java new file mode 100644 index 00000000..9452efd2 --- /dev/null +++ b/common/src/main/java/io/muun/common/api/TargetFeeRatesJson.java @@ -0,0 +1,52 @@ +package io.muun.common.api; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.SortedMap; +import java.util.TreeMap; +import javax.validation.constraints.NotNull; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class TargetFeeRatesJson { + + @NotNull + public SortedMap confTargetToTargetFeeRateInSatPerVbyte = new TreeMap<>(); + + @NotNull + public Integer fastConfTarget; + + @NotNull + public Integer mediumConfTarget; + + @NotNull + public Integer slowConfTarget; + + @NotNull + public Integer zeroConfSwapConfTarget; + + /** + * Json constructor. + */ + public TargetFeeRatesJson() { + } + + /** + * Houston constructor. + */ + public TargetFeeRatesJson( + SortedMap confTargetToTargetFeeRateInSatPerVbyte, + Integer fastConfTarget, + Integer mediumConfTarget, + Integer slowConfTarget, + Integer zeroConfSwapConfTarget + ) { + + this.confTargetToTargetFeeRateInSatPerVbyte = confTargetToTargetFeeRateInSatPerVbyte; + this.fastConfTarget = fastConfTarget; + this.mediumConfTarget = mediumConfTarget; + this.slowConfTarget = slowConfTarget; + this.zeroConfSwapConfTarget = zeroConfSwapConfTarget; + } +} diff --git a/common/src/main/java/io/muun/common/api/UnconfirmedOutpointsJson.java b/common/src/main/java/io/muun/common/api/UnconfirmedOutpointsJson.java new file mode 100644 index 00000000..70d2b6b2 --- /dev/null +++ b/common/src/main/java/io/muun/common/api/UnconfirmedOutpointsJson.java @@ -0,0 +1,33 @@ +package io.muun.common.api; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.List; +import javax.validation.constraints.NotNull; + +/** + * It contains the unconfirmed UTXOs that will be used to obtain the + * corresponding fee bump functions from realtime/fees API. The order + * passed here matches the order of the returned functions. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class UnconfirmedOutpointsJson { + + @NotNull + public List unconfirmedOutpoints; + + public UnconfirmedOutpointsJson(List unconfirmedOutpoints) { + this.unconfirmedOutpoints = unconfirmedOutpoints; + } + + /** + * Return the list of unconfirmed outpoints separated by ','. + */ + @Override + public String toString() { + return String.join(", ", unconfirmedOutpoints); + } + +} diff --git a/common/src/main/java/io/muun/common/api/UserEmailJson.java b/common/src/main/java/io/muun/common/api/UserEmailJson.java new file mode 100644 index 00000000..c0beb5b9 --- /dev/null +++ b/common/src/main/java/io/muun/common/api/UserEmailJson.java @@ -0,0 +1,27 @@ +package io.muun.common.api; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import javax.validation.constraints.NotNull; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class UserEmailJson { + + @NotNull + public String email; + + /** + * JSON constructor. + */ + public UserEmailJson() { + } + + /** + * Constructor. + */ + public UserEmailJson(String email) { + this.email = email; + } +} 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 26435839..de71bba4 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 @@ -417,7 +417,7 @@ public enum ErrorCode { 2125, StatusCode.CLIENT_FAILURE, "Derivation path is invalid" ); - private static Map errorCodeMap = new HashMap<>(); + private static final Map errorCodeMap = new HashMap<>(); static { for (ErrorCode errorCode : values()) { diff --git a/common/src/main/java/io/muun/common/api/houston/HoustonService.java b/common/src/main/java/io/muun/common/api/houston/HoustonService.java index 910cc863..2671703f 100644 --- a/common/src/main/java/io/muun/common/api/houston/HoustonService.java +++ b/common/src/main/java/io/muun/common/api/houston/HoustonService.java @@ -38,11 +38,13 @@ import io.muun.common.api.PublicProfileJson; import io.muun.common.api.RawTransaction; import io.muun.common.api.RealTimeData; +import io.muun.common.api.RealTimeFeesJson; import io.muun.common.api.SetupChallengeResponse; import io.muun.common.api.StartEmailSetupJson; import io.muun.common.api.SubmarineSwapJson; import io.muun.common.api.SubmarineSwapRequestJson; import io.muun.common.api.TransactionPushedJson; +import io.muun.common.api.UnconfirmedOutpointsJson; import io.muun.common.api.UpdateOperationMetadataJson; import io.muun.common.api.UserInvoiceJson; import io.muun.common.api.UserJson; @@ -240,6 +242,11 @@ Observable finishPasswordChange( @GET("realtime") Observable fetchRealTimeData(); + @POST("realtime/fees") + Observable fetchRealTimeFees( + @Body UnconfirmedOutpointsJson unconfirmedOutpoints + ); + @GET("operations") Observable> fetchOperations(); diff --git a/common/src/main/java/io/muun/common/api/messages/OperationUpdateMessage.java b/common/src/main/java/io/muun/common/api/messages/OperationUpdateMessage.java index 397be474..7a61424e 100644 --- a/common/src/main/java/io/muun/common/api/messages/OperationUpdateMessage.java +++ b/common/src/main/java/io/muun/common/api/messages/OperationUpdateMessage.java @@ -40,7 +40,13 @@ public class OperationUpdateMessage extends AbstractMessage { @Override public String toLog() { - return String.format("Update about tx '%s' with %s confirmations", hash, confirmations); + return String.format( + "Operation %s is now in status %s. Associated tx '%s' has now %s confirmations", + id, + status.toString(), + hash, + confirmations + ); } /** diff --git a/common/src/main/java/io/muun/common/bitcoinj/ValidationHelpers.java b/common/src/main/java/io/muun/common/bitcoinj/ValidationHelpers.java index 4503d3e0..b72b4856 100644 --- a/common/src/main/java/io/muun/common/bitcoinj/ValidationHelpers.java +++ b/common/src/main/java/io/muun/common/bitcoinj/ValidationHelpers.java @@ -28,7 +28,7 @@ public class ValidationHelpers { // We run a very simple validation, aimed at preventing common user mistakes. This expression // does NOT attempt to filter invalid emails in general. private static final Pattern emailPattern = Pattern - .compile("[^@ ]+@[^@ ]+[.][^@ ]*[a-zA-Z0-9]$"); + .compile("[^@ ]+@[^@ ]+[.][^@ ]*[a-zA-Z\\d]$"); /** * Check if an e-mail address is valid. diff --git a/common/src/main/java/io/muun/common/crypto/hd/DerivationPathUtils.java b/common/src/main/java/io/muun/common/crypto/hd/DerivationPathUtils.java index 985a3b60..a5de7488 100644 --- a/common/src/main/java/io/muun/common/crypto/hd/DerivationPathUtils.java +++ b/common/src/main/java/io/muun/common/crypto/hd/DerivationPathUtils.java @@ -87,9 +87,9 @@ public static List parsePath(@NotNull String absolutePath) { indexString = childParts[1]; } - final Integer index; + final int index; try { - index = Integer.valueOf(indexString); + index = Integer.parseInt(indexString); } catch (NumberFormatException exception) { throw new InvalidDerivationPathException(absolutePath); } diff --git a/common/src/main/java/io/muun/common/crypto/hd/MuunAddress.java b/common/src/main/java/io/muun/common/crypto/hd/MuunAddress.java index e65f10e0..cdb7d3d3 100644 --- a/common/src/main/java/io/muun/common/crypto/hd/MuunAddress.java +++ b/common/src/main/java/io/muun/common/crypto/hd/MuunAddress.java @@ -82,6 +82,7 @@ public byte[] getHash() { return toBitcoinJ().getHash(); } + @SuppressWarnings({"DanglingJavadoc", "MissingJavadocMethod"}) /** * Deprecated and commented out as a deterrent. This method "falls short" for regtest addresses: * as it uses the base58 serialization to read the data, it does not distinguish between diff --git a/common/src/main/java/io/muun/common/crypto/hd/MuunInputSubmarineSwapV101.java b/common/src/main/java/io/muun/common/crypto/hd/MuunInputSubmarineSwapV101.java index 21d29b60..d2459d34 100644 --- a/common/src/main/java/io/muun/common/crypto/hd/MuunInputSubmarineSwapV101.java +++ b/common/src/main/java/io/muun/common/crypto/hd/MuunInputSubmarineSwapV101.java @@ -23,16 +23,16 @@ public static MuunInputSubmarineSwapV101 fromJson(MuunInputSubmarineSwapV101Json } @NotNull - private String refundAddress; + private final String refundAddress; @NotNull - private byte[] swapPaymentHash256; + private final byte[] swapPaymentHash256; @NotNull - private byte[] swapServerPublicKey; + private final byte[] swapServerPublicKey; @NotNull - private long lockTime; + private final long lockTime; /** * Constructor. diff --git a/common/src/main/java/io/muun/common/crypto/hd/PrivateKey.java b/common/src/main/java/io/muun/common/crypto/hd/PrivateKey.java index 8325c972..a46fa945 100644 --- a/common/src/main/java/io/muun/common/crypto/hd/PrivateKey.java +++ b/common/src/main/java/io/muun/common/crypto/hd/PrivateKey.java @@ -10,6 +10,7 @@ import io.muun.common.crypto.hd.exception.KeyDerivationException; import io.muun.common.utils.Encodings; import io.muun.common.utils.Hashes; +import io.muun.common.utils.Preconditions; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; @@ -34,6 +35,7 @@ public class PrivateKey extends BaseKey { private static final int PRIVATE_KEY_LENGTH_IN_BYTES = 32; + private static final int FINGERPRINT_LENGTH_IN_BYTES = 4; // TODO this should live in bitcoin lib (signTransactionHash could receive this as param). @@ -76,8 +78,10 @@ public static PrivateKey getNewRootPrivateKey(@NotNull Context bitcoinContext) { /** * Deserialize a base58-encoded extended private key. */ - public static PrivateKey deserializeFromBase58(@NotNull String absoluteDerivationPath, - @NotNull String base58Serialization) { + public static PrivateKey deserializeFromBase58( + @NotNull String absoluteDerivationPath, + @NotNull String base58Serialization + ) { final NetworkParameters networkParameters = NetworkParametersHelper .getNetworkParametersForBase58Key(base58Serialization); @@ -90,36 +94,61 @@ public static PrivateKey deserializeFromBase58(@NotNull String absoluteDerivatio return new PrivateKey( absoluteDerivationPath, deterministicKey, - networkParameters); + networkParameters + ); } /** * Deserialize a private key from its compact byte representation. */ - public static PrivateKey fromCompactSerialization(@NotNull byte[] serialization, - @NotNull NetworkParameters network) { + public static PrivateKey fromCompactSerialization( + @NotNull byte[] serialization, + @NotNull NetworkParameters network + ) { // the serialization for our extended private key is the concatenation of the raw private // key and the chain code, each of them 32 bytes final byte[] privateKey = ByteUtils.subArray(serialization, 0, PRIVATE_KEY_LENGTH_IN_BYTES); final byte[] chainCode = ByteUtils.subArray(serialization, PRIVATE_KEY_LENGTH_IN_BYTES); + return PrivateKey.fromBytes32(privateKey, chainCode, network); + } + + /** + * Creates a private key from a byte32 & chainCode. + */ + public static PrivateKey fromBytes32( + @NotNull byte[] key32, + @NotNull byte[] chainCode, + NetworkParameters networkParameters + ) { + Preconditions.checkArgument( + key32 != null && key32.length == 32, + "key32 must be 32 bytes length" + ); + Preconditions.checkArgument( + chainCode != null && chainCode.length == 32, + "chainCode must be 32 bytes length" + ); + final DeterministicKey deterministicKey = new DeterministicKey( ImmutableList.of(), chainCode, - Encodings.bytesToBigInteger(privateKey), + Encodings.bytesToBigInteger(key32), null ); - return new PrivateKey("m", deterministicKey, network); + return new PrivateKey("m", deterministicKey, networkParameters); } /** * Creates an extended private key from a DeterministicKey. */ - private PrivateKey(@NotNull String absoluteDerivationPath, - @NotNull DeterministicKey deterministicKey, - NetworkParameters networkParameters) { + private PrivateKey( + @NotNull String absoluteDerivationPath, + @NotNull DeterministicKey deterministicKey, + NetworkParameters networkParameters + ) { if (deterministicKey.isPubKeyOnly()) { throw new IllegalArgumentException("No private key provided."); @@ -190,8 +219,10 @@ public PrivateKey deriveFromAbsolutePath(@NotNull String absolutePathToDerive) absolutePathToDerive); if (!isPrefix(parsedAbsoluteDerivationPath, childNumbers)) { - throw new InvalidDerivationBranchException(absoluteDerivationPath, - absolutePathToDerive); + throw new InvalidDerivationBranchException( + absoluteDerivationPath, + absolutePathToDerive + ); } final List childNumbersToDerive = childNumbers.subList( @@ -199,8 +230,10 @@ public PrivateKey deriveFromAbsolutePath(@NotNull String absolutePathToDerive) childNumbers.size() ); - final DeterministicKey derivedKey = deriveDeterministicKey(deterministicKey, - childNumbersToDerive); + final DeterministicKey derivedKey = deriveDeterministicKey( + deterministicKey, + childNumbersToDerive + ); return new PrivateKey(absolutePathToDerive, derivedKey, networkParameters); @@ -348,7 +381,7 @@ public int hashCode() { @Override public String toString() { return "PrivateKey{\n" - + "\tabsoluteDerivationPath='" + absoluteDerivationPath + "\',\n" + + "\tabsoluteDerivationPath='" + absoluteDerivationPath + "',\n" + "\tparsedAbsoluteDerivationPath=" + parsedAbsoluteDerivationPath + ",\n" + "\tdeterministicKey=" + deterministicKey + "\n" + '}'; diff --git a/common/src/main/java/io/muun/common/crypto/hd/PublicKeyTriple.java b/common/src/main/java/io/muun/common/crypto/hd/PublicKeyTriple.java index e1f813da..9081a471 100644 --- a/common/src/main/java/io/muun/common/crypto/hd/PublicKeyTriple.java +++ b/common/src/main/java/io/muun/common/crypto/hd/PublicKeyTriple.java @@ -9,15 +9,19 @@ public class PublicKeyTriple { private final PublicKey userPublicKey; + private final PublicKey muunPublicKey; + private final PublicKey swapServerPublicKey; /** * Constructor. */ - public PublicKeyTriple(PublicKey userPublicKey, - PublicKey muunPublicKey, - PublicKey swapServerPublicKey) { + public PublicKeyTriple( + PublicKey userPublicKey, + PublicKey muunPublicKey, + PublicKey swapServerPublicKey + ) { this.userPublicKey = userPublicKey; this.muunPublicKey = muunPublicKey; this.swapServerPublicKey = swapServerPublicKey; @@ -46,11 +50,12 @@ public int getLastLevelIndex() { return userPublicKey.getLastLevelIndex(); } - /* - Deprecated and commented out as a deterrent. This method "falls short" for regtest addresses: - as it uses the base58 serialization to read the data, it does not distinguish between testnet - and regtest addresses. Leaving the code commented in case someone in the future has the same - idea. + @SuppressWarnings({"DanglingJavadoc", "MissingJavadocMethod"}) + /** + * Deprecated and commented out as a deterrent. This method "falls short" for regtest addresses: + * as it uses the base58 serialization to read the data, it does not distinguish between testnet + * and regtest addresses. Leaving the code commented in case someone in the future has the same + * idea. */ //public NetworkParameters getNetworkParameters() { // return userPublicKey.getNetworkParameters(); @@ -65,7 +70,8 @@ public PublicKeyTriple deriveFromAbsolutePath(String absolutePath) return new PublicKeyTriple( userPublicKey.deriveFromAbsolutePath(absolutePath), muunPublicKey.deriveFromAbsolutePath(absolutePath), - swapServerPublicKey.deriveFromAbsolutePath(absolutePath)); + swapServerPublicKey.deriveFromAbsolutePath(absolutePath) + ); } /** @@ -85,13 +91,15 @@ public PublicKeyTriple deriveNextValidChild(int startingIndex) { /** * Derive all PublicKeys at the same index. + * * @throws KeyDerivationException if the index is invalid. */ public PublicKeyTriple deriveChild(int childIndex) throws KeyDerivationException { return new PublicKeyTriple( userPublicKey.deriveChild(childIndex), muunPublicKey.deriveChild(childIndex), - swapServerPublicKey.deriveChild(childIndex)); + swapServerPublicKey.deriveChild(childIndex) + ); } /** diff --git a/common/src/main/java/io/muun/common/model/ExchangeRateProvider.java b/common/src/main/java/io/muun/common/model/ExchangeRateProvider.java index 085fe80d..e4bba26c 100644 --- a/common/src/main/java/io/muun/common/model/ExchangeRateProvider.java +++ b/common/src/main/java/io/muun/common/model/ExchangeRateProvider.java @@ -3,7 +3,7 @@ import io.muun.common.Optional; import io.muun.common.api.ExchangeRateWindow; -import org.javamoney.moneta.ExchangeRateBuilder; +import org.javamoney.moneta.convert.ExchangeRateBuilder; import org.javamoney.moneta.spi.AbstractRateProvider; import org.javamoney.moneta.spi.DefaultNumberValue; diff --git a/common/src/main/java/io/muun/common/model/SizeForAmount.java b/common/src/main/java/io/muun/common/model/SizeForAmount.java index 484a9764..7e194a10 100644 --- a/common/src/main/java/io/muun/common/model/SizeForAmount.java +++ b/common/src/main/java/io/muun/common/model/SizeForAmount.java @@ -21,20 +21,30 @@ public class SizeForAmount { public int deltaInWeightUnits; + public String derivationPath; + + public Integer addressVersion; + /** * Manual constructor. */ - public SizeForAmount(long amountInSatoshis, - int sizeInBytes, - String outpoint, - UtxoStatus status, - int deltaInWeightUnits) { + public SizeForAmount( + long amountInSatoshis, + int sizeInBytes, + String outpoint, + UtxoStatus status, + int deltaInWeightUnits, + String derivationPath, + Integer addressVersion + ) { this.amountInSatoshis = amountInSatoshis; this.sizeInBytes = sizeInBytes; this.outpoint = outpoint; this.status = status; this.deltaInWeightUnits = deltaInWeightUnits; + this.derivationPath = derivationPath; + this.addressVersion = addressVersion; } /** @@ -88,4 +98,12 @@ public boolean equals(Object o) { return (amountInSatoshis == that.amountInSatoshis && sizeInBytes == that.sizeInBytes); } + + /** + * Return the transaction id associated to the given outpoint. + * Recall that outpoints are of the form "txId:outputIndex". + */ + public String getOutpointTxId() { + return outpoint.substring(0, outpoint.indexOf(":")); + } } diff --git a/common/src/main/java/io/muun/common/model/challenge/Challenge.java b/common/src/main/java/io/muun/common/model/challenge/Challenge.java index a601b022..1d4abebf 100644 --- a/common/src/main/java/io/muun/common/model/challenge/Challenge.java +++ b/common/src/main/java/io/muun/common/model/challenge/Challenge.java @@ -9,13 +9,13 @@ public class Challenge { @NotNull - public ChallengeType type; + public final ChallengeType type; @NotNull - public byte[] challenge; + public final byte[] challenge; @Nullable - public byte[] salt; + public final byte[] salt; /** * Constructor. diff --git a/common/src/main/java/io/muun/common/utils/Collections.java b/common/src/main/java/io/muun/common/utils/Collections.java index c80157df..f7ae2d3e 100644 --- a/common/src/main/java/io/muun/common/utils/Collections.java +++ b/common/src/main/java/io/muun/common/utils/Collections.java @@ -19,6 +19,14 @@ private Collections() { */ public static Set intersection(Collection a, Collection b) { + if (b.size() < a.size()) { + + // A few lines below we will create a copy of `a` and invoke `retainAll(b)` over + // `HashSet<>(a)`. That translates to `a.size()` calls to `b.contains` and at most + // `a.size()` removals (see https://stackoverflow.com/questions/24754881/what-is-the-time-and-space-complexity-of-method-retainall-when-used-on-hashsets/24755224#24755224 + // for a reference), so it's always best if `a` is the smaller of the two. + return intersection(b, a); + } final Set intersection = new HashSet<>(a); intersection.retainAll(b); return intersection; diff --git a/common/src/main/java/io/muun/common/utils/Encodings.java b/common/src/main/java/io/muun/common/utils/Encodings.java index 8256ac55..422f967c 100644 --- a/common/src/main/java/io/muun/common/utils/Encodings.java +++ b/common/src/main/java/io/muun/common/utils/Encodings.java @@ -315,7 +315,7 @@ public static ECPrivateKey bytesToEcPrivateKey(@Nonnull byte[] bytes) { * * @param sensitiveData array with sensitive data. */ - public static void clearArray(@NotNull byte[] sensitiveData) { + private static void clearArray(@NotNull byte[] sensitiveData) { Arrays.fill(sensitiveData, (byte) 0); } @@ -325,7 +325,7 @@ public static void clearArray(@NotNull byte[] sensitiveData) { * * @param sensitiveData array with sensitive data. */ - public static void clearArray(@NotNull char[] sensitiveData) { + private static void clearArray(@NotNull char[] sensitiveData) { Arrays.fill(sensitiveData, (char) 0); } diff --git a/common/src/main/java/io/muun/common/utils/LnInvoice.java b/common/src/main/java/io/muun/common/utils/LnInvoice.java index 8492c672..9016bf94 100644 --- a/common/src/main/java/io/muun/common/utils/LnInvoice.java +++ b/common/src/main/java/io/muun/common/utils/LnInvoice.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.Nonnull; /** * Utility class to decode and encode (and store the component data) of BOLT-11 Lighting Network @@ -64,6 +65,7 @@ public class LnInvoice { private static final int P2PKH_ADDRESS_VERSION = 17; private static final int P2SH_ADDRESS_VERSION = 18; private static final int WITNESS_V0_ADDRESS_VERSION = 0; + private static final int PAYMENT_SECRET_BYTE_LENGTH = 32; private static final Map DIVISORS; static { @@ -84,6 +86,7 @@ public class LnInvoice { aMap.put(6, new Tag("x", "expiry", "number")); aMap.put(9, new Tag("f", "fallback_address", "chain_address")); aMap.put(13, new Tag("d", "description", "string")); + aMap.put(16, new Tag("s", "payment_secret", "sha256_hash")); aMap.put(19, new Tag("n", "destination_public_key", "public_key")); aMap.put(23, new Tag("h", "description_hash", "sha256_hash")); aMap.put(24, new Tag("c", "min_final_cltv_expiry", "number")); @@ -106,6 +109,7 @@ public class LnInvoice { public final String id; // paymentHash hex-encoded public final boolean isExpired; public final Amount amount; + public final String paymentSecret; //private ??? routes; private LnInvoice(String original, @@ -117,7 +121,8 @@ private LnInvoice(String original, String destinationPubKey, ZonedDateTime expiresAt, String id, - Amount amount) { + Amount amount, + String paymentSecret) { this.original = original; this.addresses = addresses; this.cltvDelta = cltvDelta; @@ -129,6 +134,7 @@ private LnInvoice(String original, this.id = id; this.isExpired = expiresAt.compareTo(ZonedDateTime.now(ZoneOffset.UTC)) < 0; this.amount = amount; + this.paymentSecret = paymentSecret; } public ZonedDateTime getExpirationTime() { @@ -137,6 +143,7 @@ public ZonedDateTime getExpirationTime() { /** * Decodes a Bech32 encoded LN Invoice into its component data. + * If the decoding fails, throws an exception. * * @param params The network parameters that determine which network the invoice is from * @param bech32Invoice a Bech32 LN Invoice @@ -147,13 +154,7 @@ public static LnInvoice decode(NetworkParameters params, String bech32Invoice) { final String expectedHeader = getHeader(params); // Separate the Human Readable Part from the data part by decoding bech32 - final Bech32.Decoded decoded; - try { - decoded = Bech32.decode(bech32Invoice, Long.MAX_VALUE); - } catch (IllegalArgumentException e) { - // TODO throw error - throw e; - } + final Bech32.Decoded decoded = Bech32.decode(bech32Invoice, Long.MAX_VALUE); final String hrp = decoded.hrp; final byte[] data = decoded.data; @@ -234,11 +235,12 @@ public static LnInvoice decode(NetworkParameters params, String bech32Invoice) { // Let's parse the tagged fields - Long cltvDelta = 9L; // Default is 9, if not specified. + long cltvDelta = 9L; // Default is 9, if not specified. String description = null; String descriptionHash = null; final List addresses = new ArrayList<>(); byte[] paymentHash = null; + String paymentSecret = null; ZonedDateTime expiresAt = null; int cursor = 0; @@ -327,6 +329,20 @@ public static LnInvoice decode(NetworkParameters params, String bech32Invoice) { break; + case "s": + + try { + final byte[] paymentSecretEncoded = unpackBits(tagData, true); + if (paymentSecretEncoded.length != PAYMENT_SECRET_BYTE_LENGTH) { + throw new IllegalArgumentException("InvalidPaymentSecretByteLength"); + } + paymentSecret = Encodings.bytesToHex(paymentSecretEncoded); + + } catch (IllegalArgumentException e) { + throw new ParsingException("PaymentRequestPaymentSecret"); + } + break; + case "x": // Expiration Seconds try { expiresAt = paymentRequestExpiration(tagData, createdAt); @@ -374,10 +390,31 @@ public static LnInvoice decode(NetworkParameters params, String bech32Invoice) { destination, expiresAt, Encodings.bytesToHex(paymentHash), - invoiceAmount + invoiceAmount, + paymentSecret ); } + /** + * Decodes a Bech32 encoded LN Invoice into its component data. + * If the decoding fails, return null. + * + * @param params The network parameters that determine which network the invoice is from + * @param bech32Invoice a Bech32 LN Invoice + * @return a LnInvoice object with the parsed data + */ + public static LnInvoice decodeOrNull( + @Nonnull final NetworkParameters params, + @Nonnull final String bech32Invoice + ) { + try { + return decode(params, bech32Invoice); + } catch (final Exception e) { + // Invalid invoice, nothing to see here + } + return null; + } + /** * Encode an invoice intended for incoming swap test suite. */ @@ -419,6 +456,10 @@ public static String encodeForTest(final NetworkParameters networkParameters, return Bech32.encode(header, packedBytes); } + public String getDestinationPubKey() { + return destinationPubKey; + } + private static byte[] appendSignature(final PrivateKey identityKey, final String header, byte[] packedBytes) { diff --git a/common/src/main/java/io/muun/common/utils/internal/Bech32.java b/common/src/main/java/io/muun/common/utils/internal/Bech32.java index 03f8856b..8c296a5e 100644 --- a/common/src/main/java/io/muun/common/utils/internal/Bech32.java +++ b/common/src/main/java/io/muun/common/utils/internal/Bech32.java @@ -106,7 +106,7 @@ public static String encodeBech32m(String header, byte[] data) { * @param bech the bec32 encoded string * * @return an object containing the encoding, human-readable part and data part - * @throws Exception if there's a decoding error + * @throws IllegalArgumentException if there's a decoding error */ public static Decoded decode(String bech) { return decode(bech, BIP_0173_CHARACTER_LIMIT); @@ -124,7 +124,7 @@ public static Decoded decode(String bech) { * @param limit the maximum string length acceptable * * @return an object containing the encoding, human-readable part and data part - * @throws Exception if there's a decoding error + * @throws IllegalArgumentException if there's a decoding error */ public static Decoded decode(String bech, long limit) { if (!bech.equals(bech.toLowerCase()) && !bech.equals(bech.toUpperCase())) { diff --git a/common/src/test/java/io/muun/common/utils/LnInvoiceTest.java b/common/src/test/java/io/muun/common/utils/LnInvoiceTest.java index aa53a500..88725612 100644 --- a/common/src/test/java/io/muun/common/utils/LnInvoiceTest.java +++ b/common/src/test/java/io/muun/common/utils/LnInvoiceTest.java @@ -107,6 +107,10 @@ private void assertLnInvoice(LnInvoice invoice, LnInvoiceTestData.Values expecte assertThat(invoice.amount.amountInSatoshis).isEqualTo(expected.amountInSatoshis); } + if (expected.paymentSecret != null) { + assertThat(invoice.paymentSecret).isEqualTo(expected.paymentSecret); + } + // TODO assert routes } diff --git a/common/src/test/java/io/muun/common/utils/LnInvoiceTestData.java b/common/src/test/java/io/muun/common/utils/LnInvoiceTestData.java index a4089066..8ef110d3 100644 --- a/common/src/test/java/io/muun/common/utils/LnInvoiceTestData.java +++ b/common/src/test/java/io/muun/common/utils/LnInvoiceTestData.java @@ -58,6 +58,9 @@ public class Values { @JsonProperty("tokens") public long amountInSatoshis; + @JsonProperty("payment_secret") + public String paymentSecret; + /** * Json constructor. */ diff --git a/common/src/test/resources/io/muun/common/utils/ln-invoice-test-data.json b/common/src/test/resources/io/muun/common/utils/ln-invoice-test-data.json index f14de63b..c96f9b98 100644 --- a/common/src/test/resources/io/muun/common/utils/ln-invoice-test-data.json +++ b/common/src/test/resources/io/muun/common/utils/ln-invoice-test-data.json @@ -27,9 +27,10 @@ "is_expired": true, "mtokens": null, "network": "bitcoin", - "tokens": null + "tokens": null, + "payment_secret": "1111111111111111111111111111111111111111111111111111111111111111" }, - "request": "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w" + "request": "lnbc1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq9qrsgq357wnc5r2ueh7ck6q93dj32dlqnls087fxdwk8qakdyafkq3yap9us6v52vjjsrvywa6rt52cm9r9zqt8r2t7mlcwspyetp5h2tztugp9lfyql" }, { "description": "Please send $3 for a cup of coffee to the same peer, within 1 minute", @@ -43,9 +44,10 @@ "is_expired": true, "mtokens": "250000000", "network": "bitcoin", - "tokens": 250000 + "tokens": 250000, + "payment_secret": "1111111111111111111111111111111111111111111111111111111111111111" }, - "request": "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp" + "request": "lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9qrsgquk0rl77nj30yxdy8j9vdx85fkpmdla2087ne0xh8nhedh8w27kyke0lp53ut353s06fv3qfegext0eh0ymjpf39tuven09sam30g4vgpfna3rh" }, { "description": "Please send 0.0025 BTC for a cup of nonsense (ナンセンス 1杯) to the same peer, within 1 minute", @@ -59,9 +61,10 @@ "is_expired": true, "mtokens": "250000000", "network": "bitcoin", - "tokens": 250000 + "tokens": 250000, + "payment_secret": "1111111111111111111111111111111111111111111111111111111111111111" }, - "request": "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny" + "request": "lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpu9qrsgqhtjpauu9ur7fw2thcl4y9vfvh4m9wlfyz2gem29g5ghe2aak2pm3ps8fdhtceqsaagty2vph7utlgj48u0ged6a337aewvraedendscp573dxr" }, { "description": "Now send $24 for an entire list of things (hashed)", @@ -75,9 +78,10 @@ "is_expired": true, "mtokens": "2000000000", "network": "bitcoin", - "tokens": 2000000 + "tokens": 2000000, + "payment_secret": "1111111111111111111111111111111111111111111111111111111111111111" }, - "request": "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7khhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq2yxxz7" + "request": "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrsgq7ea976txfraylvgzuxs8kgcw23ezlrszfnh8r6qtfpr6cxga50aj6txm9rxrydzd06dfeawfk6swupvz4erwnyutnjq7x39ymw6j38gp7ynn44" }, { "description": "The same, on testnet, with a fallback address mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP", @@ -92,9 +96,10 @@ "is_expired": true, "mtokens": "2000000000", "network": "testnet", - "tokens": 2000000 + "tokens": 2000000, + "payment_secret": "1111111111111111111111111111111111111111111111111111111111111111" }, - "request": "lntb20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un98kmzzhznpurw9sgl2v0nklu2g4d0keph5t7tj9tcqd8rexnd07ux4uv2cjvcqwaxgj7v4uwn5wmypjd5n69z2xm3xgksg28nwht7f6zspwp3f9t" + "request": "lntb20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un989qrsgqdj545axuxtnfemtpwkc45hx9d2ft7x04mt8q7y6t0k2dge9e7h8kpy9p34ytyslj3yu569aalz2xdk8xkd7ltxqld94u8h2esmsmacgpghe9k8" }, { "description": "On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info", @@ -130,9 +135,10 @@ } ] ], - "tokens": 2000000 + "tokens": 2000000, + "payment_secret": "1111111111111111111111111111111111111111111111111111111111111111" }, - "request": "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqj9n4evl6mr5aj9f58zp6fyjzup6ywn3x6sk8akg5v4tgn2q8g4fhx05wf6juaxu9760yp46454gpg5mtzgerlzezqcqvjnhjh8z3g2qqdhhwkj" + "request": "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq9qrsgqdfjcdk6w3ak5pca9hwfwfh63zrrz06wwfya0ydlzpgzxkn5xagsqz7x9j4jwe7yj7vaf2k9lqsdk45kts2fd0fkr28am0u4w95tt2nsq76cqw0" }, { "description": "On mainnet, with fallback (P2SH) address 3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX", @@ -147,9 +153,10 @@ "is_expired": true, "mtokens": "2000000000", "network": "bitcoin", - "tokens": 2000000 + "tokens": 2000000, + "payment_secret": "1111111111111111111111111111111111111111111111111111111111111111" }, - "request": "lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppj3a24vwu6r8ejrss3axul8rxldph2q7z9kmrgvr7xlaqm47apw3d48zm203kzcq357a4ls9al2ea73r8jcceyjtya6fu5wzzpe50zrge6ulk4nvjcpxlekvmxl6qcs9j3tz0469gq5g658y" + "request": "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppj3a24vwu6r8ejrss3axul8rxldph2q7z99qrsgqz6qsgww34xlatfj6e3sngrwfy3ytkt29d2qttr8qz2mnedfqysuqypgqex4haa2h8fx3wnypranf3pdwyluftwe680jjcfp438u82xqphf75ym" }, { "description": "On mainnet, with fallback (P2WPKH) address bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", @@ -164,9 +171,10 @@ "is_expired": true, "mtokens": "2000000000", "network": "bitcoin", - "tokens": 2000000 + "tokens": 2000000, + "payment_secret": "1111111111111111111111111111111111111111111111111111111111111111" }, - "request": "lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppqw508d6qejxtdg4y5r3zarvary0c5xw7kepvrhrm9s57hejg0p662ur5j5cr03890fa7k2pypgttmh4897d3raaq85a293e9jpuqwl0rnfuwzam7yr8e690nd2ypcq9hlkdwdvycqa0qza8" + "request": "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppqw508d6qejxtdg4y5r3zarvary0c5xw7k9qrsgqt29a0wturnys2hhxpner2e3plp6jyj8qx7548zr2z7ptgjjc7hljm98xhjym0dg52sdrvqamxdezkmqg4gdrvwwnf0kv2jdfnl4xatsqmrnsse" }, { "description": "On mainnet, with fallback (P2WSH) address bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", @@ -181,9 +189,10 @@ "is_expired": true, "mtokens": "2000000000", "network": "bitcoin", - "tokens": 2000000 + "tokens": 2000000, + "payment_secret": "1111111111111111111111111111111111111111111111111111111111111111" }, - "request": "lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q28j0v3rwgy9pvjnd48ee2pl8xrpxysd5g44td63g6xcjcu003j3qe8878hluqlvl3km8rm92f5stamd3jw763n3hck0ct7p8wwj463cql26ava" + "request": "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q9qrsgq9vlvyj8cqvq6ggvpwd53jncp9nwc47xlrsnenq2zp70fq83qlgesn4u3uyf4tesfkkwwfg3qs54qe426hp3tz7z6sweqdjg05axsrjqp9yrrwc" }, { "description": "On testnet, no fallback address, small number of tokens", diff --git a/libwallet/V1.go b/libwallet/V1.go index d9a2e1f4..9d9c212f 100644 --- a/libwallet/V1.go +++ b/libwallet/V1.go @@ -3,10 +3,10 @@ package libwallet import ( "fmt" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" "github.com/muun/libwallet/addresses" "github.com/muun/libwallet/btcsuitew/txscriptw" ) diff --git a/libwallet/V3.go b/libwallet/V3.go index 9ce9526a..7de5cdd6 100644 --- a/libwallet/V3.go +++ b/libwallet/V3.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/btcutil" "github.com/muun/libwallet/addresses" "github.com/btcsuite/btcd/chaincfg" @@ -98,6 +98,6 @@ func (c *coinV3) signature(index int, tx *wire.MsgTx, userKey *HDPublicKey, muun return nil, fmt.Errorf("failed to build reedem script for signing: %w", err) } - return signNonNativeSegwitInput( + return signNonNativeSegwitInputV0( index, tx, signingKey, redeemScript, witnessScript, c.Amount) } diff --git a/libwallet/V4.go b/libwallet/V4.go index af533266..57936755 100644 --- a/libwallet/V4.go +++ b/libwallet/V4.go @@ -3,7 +3,7 @@ package libwallet import ( "fmt" - "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/btcutil" "github.com/muun/libwallet/addresses" "github.com/btcsuite/btcd/chaincfg" @@ -85,7 +85,7 @@ func (c *coinV4) signature(index int, tx *wire.MsgTx, userKey *HDPublicKey, muun return nil, err } - return signNativeSegwitInput( + return signNativeSegwitInputV0( index, tx, signingKey, witnessScript, c.Amount) } diff --git a/libwallet/V5.go b/libwallet/V5.go index c59abdcd..7a144d2f 100644 --- a/libwallet/V5.go +++ b/libwallet/V5.go @@ -1,18 +1,17 @@ package libwallet import ( - "encoding/hex" "fmt" "github.com/muun/libwallet/addresses" "github.com/muun/libwallet/btcsuitew/txscriptw" "github.com/muun/libwallet/musig" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" ) // CreateAddressV5 returns a P2TR MuunAddress using Musig with the signing and cosigning keys. @@ -44,12 +43,12 @@ func (c *coinV5) SignInput(index int, tx *wire.MsgTx, userKey *HDPrivateKey, muu userEcPriv, err := derivedUserKey.key.ECPrivKey() if err != nil { - return fmt.Errorf("failed to obtain ECPrivKey from derivedUserKey") // TODO: necessary handling? + return fmt.Errorf("failed to obtain ECPrivKey from derivedUserKey: %w", err) } muunEcPub, err := derivedMuunKey.key.ECPubKey() if err != nil { - return fmt.Errorf("failed to obtain ECPubKey from derivedMuunKey") // TODO: necessary handling? + return fmt.Errorf("failed to obtain ECPubKey from derivedMuunKey: %w", err) } sigHash, err := txscriptw.CalcTaprootSigHash(tx, c.SigHashes, index, txscript.SigHashAll) @@ -75,12 +74,12 @@ func (c *coinV5) FullySignInput(index int, tx *wire.MsgTx, userKey, muunKey *HDP userEcPriv, err := derivedUserKey.key.ECPrivKey() if err != nil { - return fmt.Errorf("failed to obtain ECPrivKey from derivedUserKey") // TODO: necessary handling? + return fmt.Errorf("failed to obtain ECPrivKey from derivedUserKey: %w", err) } muunEcPriv, err := derivedMuunKey.key.ECPrivKey() if err != nil { - return fmt.Errorf("failed to obtain ECPrivKey from derivedMuunKey") // TODO: necessary handling? + return fmt.Errorf("failed to obtain ECPrivKey from derivedMuunKey: %w", err) } sigHash, err := txscriptw.CalcTaprootSigHash(tx, c.SigHashes, index, txscript.SigHashAll) @@ -90,9 +89,16 @@ func (c *coinV5) FullySignInput(index int, tx *wire.MsgTx, userKey, muunKey *HDP var toSign [32]byte copy(toSign[:], sigHash) - userPubNonce := musig.GeneratePubNonce(c.UserSessionId) + userPubNonce, err := musig.MuSig2GenerateNonce( + musig.Musig2v040Muun, + c.UserSessionId[:], + nil, + ) + if err != nil { + return err + } - err = c.signFirstWith(index, tx, userEcPriv.PubKey(), muunEcPriv, userPubNonce, toSign) + err = c.signFirstWith(index, tx, userEcPriv.PubKey(), muunEcPriv, userPubNonce.PubNonce, toSign) if err != nil { return err } @@ -114,22 +120,30 @@ func (c *coinV5) signFirstWith( // user. We call the variables below "muunSessionId" and "muunPubNonce" to follow convention, // but Muun servers play no role in this code path and both are locally generated. muunSessionId := musig.RandomSessionId() - muunPubNonce := musig.GeneratePubNonce(muunSessionId) + muunPubNonce, err := musig.MuSig2GenerateNonce( + musig.Musig2v040Muun, + muunSessionId[:], + muunPriv.PubKey().SerializeCompressed(), + ) + if err != nil { + return fmt.Errorf("failed to generate nonce: %w", err) + } muunPartialSig, err := musig.ComputeMuunPartialSignature( - toSign, - userPub, - muunPriv, - userPubNonce, - muunSessionId, - nil, + musig.Musig2v040Muun, + toSign[:], + userPub.SerializeCompressed(), + muunPriv.Serialize(), + userPubNonce[:], + muunSessionId[:], + musig.KeySpendOnlyTweak(), ) if err != nil { return fmt.Errorf("failed to add first signature: %w", err) } - c.MuunPubNonce = muunPubNonce - c.MuunPartialSig = muunPartialSig + copy(c.MuunPubNonce[:], muunPubNonce.PubNonce[0:66]) + copy(c.MuunPartialSig[:], muunPartialSig[0:32]) return nil } @@ -143,14 +157,15 @@ func (c *coinV5) signSecondWith( toSign [32]byte, ) error { - rawCombinedSig, err := musig.AddUserSignatureAndCombine( - toSign, - userPriv, - muunPub, - c.MuunPartialSig, - c.MuunPubNonce, - userSessionId, - nil, + rawCombinedSig, err := musig.ComputeUserPartialSignature( + musig.Musig2v040Muun, + toSign[:], + userPriv.Serialize(), + muunPub.SerializeCompressed(), + c.MuunPartialSig[:], + c.MuunPubNonce[:], + userSessionId[:], + musig.KeySpendOnlyTweak(), ) if err != nil { return fmt.Errorf("failed to add second signature and combine: %w", err) @@ -161,27 +176,3 @@ func (c *coinV5) signSecondWith( tx.TxIn[index].Witness = wire.TxWitness{sig} return nil } - -type MusigNonces struct { - sessionIds [][32]byte - publicNonces [][66]byte -} - -func (m *MusigNonces) GetPubnonceHex(index int) string { - return hex.EncodeToString(m.publicNonces[index][:]) -} - -func GenerateMusigNonces(count int) *MusigNonces { - sessionIds := make([][32]byte, 0) - publicNonces := make([][66]byte, 0) - - for i := 0; i < count; i += 1 { - sessionIds = append(sessionIds, musig.RandomSessionId()) - publicNonces = append(publicNonces, musig.GeneratePubNonce(sessionIds[i])) - } - - return &MusigNonces{ - sessionIds, - publicNonces, - } -} \ No newline at end of file diff --git a/libwallet/V6.go b/libwallet/V6.go new file mode 100644 index 00000000..c0310894 --- /dev/null +++ b/libwallet/V6.go @@ -0,0 +1,177 @@ +package libwallet + +import ( + "fmt" + + "github.com/muun/libwallet/addresses" + "github.com/muun/libwallet/btcsuitew/txscriptw" + "github.com/muun/libwallet/musig" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" +) + +// CreateAddressV6 returns a P2TR MuunAddress using Musig with the signing and cosigning keys. +func CreateAddressV6(userKey, muunKey *HDPublicKey) (MuunAddress, error) { + return addresses.CreateAddressV6(&userKey.key, &muunKey.key, userKey.Path, userKey.Network.network) +} + +type coinV6 struct { + Network *chaincfg.Params + OutPoint wire.OutPoint + KeyPath string + Amount btcutil.Amount + UserSessionId [32]byte + MuunPubNonce [66]byte + MuunPartialSig [32]byte + SigHashes *txscriptw.TaprootSigHashes +} + +func (c *coinV6) SignInput(index int, tx *wire.MsgTx, userKey *HDPrivateKey, muunKey *HDPublicKey) error { + derivedUserKey, err := userKey.DeriveTo(c.KeyPath) + if err != nil { + return fmt.Errorf("failed to derive user private key: %w", err) + } + + derivedMuunKey, err := muunKey.DeriveTo(c.KeyPath) + if err != nil { + return fmt.Errorf("failed to derive muun public key: %w", err) + } + + userEcPriv, err := derivedUserKey.key.ECPrivKey() + if err != nil { + return fmt.Errorf("failed to obtain ECPrivKey from derivedUserKey: %w", err) + } + + muunEcPub, err := derivedMuunKey.key.ECPubKey() + if err != nil { + return fmt.Errorf("failed to obtain ECPubKey from derivedMuunKey: %w", err) + } + + sigHash, err := txscriptw.CalcTaprootSigHash(tx, c.SigHashes, index, txscript.SigHashAll) + if err != nil { + return fmt.Errorf("failed to create sigHash: %w", err) + } + var toSign [32]byte + copy(toSign[:], sigHash) + + return c.signSecondWith(index, tx, userEcPriv, muunEcPub, c.UserSessionId, toSign) +} + +func (c *coinV6) FullySignInput(index int, tx *wire.MsgTx, userKey, muunKey *HDPrivateKey) error { + derivedUserKey, err := userKey.DeriveTo(c.KeyPath) + if err != nil { + return fmt.Errorf("failed to derive user private key: %w", err) + } + + derivedMuunKey, err := muunKey.DeriveTo(c.KeyPath) + if err != nil { + return fmt.Errorf("failed to derive muun private key: %w", err) + } + + userEcPriv, err := derivedUserKey.key.ECPrivKey() + if err != nil { + return fmt.Errorf("failed to obtain ECPrivKey from derivedUserKey: %w", err) + } + + muunEcPriv, err := derivedMuunKey.key.ECPrivKey() + if err != nil { + return fmt.Errorf("failed to obtain ECPrivKey from derivedMuunKey: %w", err) + } + + sigHash, err := txscriptw.CalcTaprootSigHash(tx, c.SigHashes, index, txscript.SigHashAll) + if err != nil { + return fmt.Errorf("failed to create sigHash: %w", err) + } + var toSign [32]byte + copy(toSign[:], sigHash) + + userPubNonce, err := musig.MuSig2GenerateNonce( + musig.Musig2v100, + c.UserSessionId[:], + userEcPriv.PubKey().SerializeCompressed(), + ) + if err != nil { + return err + } + + err = c.signFirstWith(index, tx, userEcPriv.PubKey(), muunEcPriv, userPubNonce.PubNonce, toSign) + if err != nil { + return err + } + + return c.signSecondWith(index, tx, userEcPriv, muunEcPriv.PubKey(), c.UserSessionId, toSign) +} + +func (c *coinV6) signFirstWith( + index int, + tx *wire.MsgTx, + userPub *btcec.PublicKey, + muunPriv *btcec.PrivateKey, + userPubNonce [66]byte, + toSign [32]byte, +) error { + + // NOTE: + // This will only be called in a recovery context, where both private keys are provided by the + // user. We call the variables below "muunSessionId" and "muunPubNonce" to follow convention, + // but Muun servers play no role in this code path and both are locally generated. + muunSessionId := musig.RandomSessionId() + muunPubNonce, err := musig.MuSig2GenerateNonce( + musig.Musig2v100, + muunSessionId[:], + muunPriv.PubKey().SerializeCompressed(), + ) + if err != nil { + return fmt.Errorf("failed to generate nonce: %w", err) + } + + muunPartialSig, err := musig.ComputeMuunPartialSignature( + musig.Musig2v100, + toSign[:], + userPub.SerializeCompressed(), + muunPriv.Serialize(), + userPubNonce[:], + muunSessionId[:], + musig.KeySpendOnlyTweak(), + ) + if err != nil { + return fmt.Errorf("failed to add first signature: %w", err) + } + + copy(c.MuunPubNonce[:], muunPubNonce.PubNonce[0:66]) + copy(c.MuunPartialSig[:], muunPartialSig[0:32]) + + return nil +} + +func (c *coinV6) signSecondWith( + index int, + tx *wire.MsgTx, + userPriv *btcec.PrivateKey, + muunPub *btcec.PublicKey, + userSessionId [32]byte, + toSign [32]byte, +) error { + rawCombinedSig, err := musig.ComputeUserPartialSignature( + musig.Musig2v100, + toSign[:], + userPriv.Serialize(), + muunPub.SerializeCompressed(), + c.MuunPartialSig[:], + c.MuunPubNonce[:], + userSessionId[:], + musig.KeySpendOnlyTweak(), + ) + if err != nil { + return fmt.Errorf("failed to add second signature and combine: %w", err) + } + + sig := append(rawCombinedSig[:], byte(txscript.SigHashAll)) + + tx.TxIn[index].Witness = wire.TxWitness{sig} + return nil +} diff --git a/libwallet/address.go b/libwallet/address.go index 43a61f82..ea99961c 100644 --- a/libwallet/address.go +++ b/libwallet/address.go @@ -3,7 +3,7 @@ package libwallet import ( "fmt" "github.com/shopspring/decimal" - "io/ioutil" + "io" "math" "net/http" "net/url" @@ -203,7 +203,7 @@ func DoPaymentRequestCall(url string, network *Network) (*MuunPaymentURI, error) } defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, errors.Errorf(ErrNetwork, "Failed to read body response: %w", err) } diff --git a/libwallet/addresses/addresses.go b/libwallet/addresses/addresses.go index c66d2325..0a446897 100644 --- a/libwallet/addresses/addresses.go +++ b/libwallet/addresses/addresses.go @@ -3,8 +3,9 @@ package addresses import ( "fmt" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil/hdkeychain" + "github.com/muun/libwallet/musig" ) const ( @@ -13,6 +14,7 @@ const ( V3 = 3 V4 = 4 V5 = 5 + V6 = 6 SubmarineSwapV1 = 101 SubmarineSwapV2 = 102 IncomingSwap = 201 @@ -44,11 +46,22 @@ func Create(version int, userKey, muunKey *hdkeychain.ExtendedKey, path string, return CreateAddressV4(userKey, muunKey, path, network) case V5: return CreateAddressV5(userKey, muunKey, path, network) + case V6: + return CreateAddressV6(userKey, muunKey, path, network) default: return nil, fmt.Errorf("unknown or unsupported version %v", version) } } +func MusigVersionForAddress(addressVersion int) musig.MusigVersion { + switch addressVersion { + case V1, V2, V3, V4, V5, SubmarineSwapV1, SubmarineSwapV2, IncomingSwap: + return musig.Musig2v040Muun + default: + return musig.Musig2v100 + } +} + func (a *WalletAddress) Version() int { return a.version } diff --git a/libwallet/addresses/addresses_test.go b/libwallet/addresses/addresses_test.go index f045050f..880ffbb8 100644 --- a/libwallet/addresses/addresses_test.go +++ b/libwallet/addresses/addresses_test.go @@ -1,7 +1,7 @@ package addresses import ( - "github.com/btcsuite/btcutil/hdkeychain" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/muun/libwallet/hdpath" ) @@ -21,7 +21,7 @@ func derive(key *hdkeychain.ExtendedKey, fromPath, toPath string) *hdkeychain.Ex if index.Hardened { modifier = hdkeychain.HardenedKeyStart } - key, err = key.Child(index.Index | modifier) + key, err = key.Derive(index.Index | modifier) if err != nil { panic(err) } diff --git a/libwallet/addresses/v1.go b/libwallet/addresses/v1.go index c118c0a5..69ee702d 100644 --- a/libwallet/addresses/v1.go +++ b/libwallet/addresses/v1.go @@ -1,9 +1,9 @@ package addresses import ( + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/hdkeychain" ) // CreateAddressV1 returns a P2PKH WalletAddress from a publicKey for use in TransactionSchemeV1 diff --git a/libwallet/addresses/v2.go b/libwallet/addresses/v2.go index 1f38356d..2459e290 100644 --- a/libwallet/addresses/v2.go +++ b/libwallet/addresses/v2.go @@ -3,10 +3,10 @@ package addresses import ( "fmt" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/hdkeychain" "github.com/pkg/errors" ) diff --git a/libwallet/addresses/v2_test.go b/libwallet/addresses/v2_test.go index 30888845..dcaf9b8f 100755 --- a/libwallet/addresses/v2_test.go +++ b/libwallet/addresses/v2_test.go @@ -4,8 +4,8 @@ import ( "reflect" "testing" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil/hdkeychain" ) var network = &chaincfg.RegressionNetParams diff --git a/libwallet/addresses/v3.go b/libwallet/addresses/v3.go index 71bb8ea4..b7d59847 100644 --- a/libwallet/addresses/v3.go +++ b/libwallet/addresses/v3.go @@ -6,9 +6,9 @@ import ( "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/hdkeychain" ) func CreateAddressV3(userKey, muunKey *hdkeychain.ExtendedKey, path string, network *chaincfg.Params) (*WalletAddress, error) { diff --git a/libwallet/addresses/v3_test.go b/libwallet/addresses/v3_test.go index 5d5bd8b2..0f18f96e 100755 --- a/libwallet/addresses/v3_test.go +++ b/libwallet/addresses/v3_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - "github.com/btcsuite/btcutil/hdkeychain" + "github.com/btcsuite/btcd/btcutil/hdkeychain" ) func TestCreateAddressV3(t *testing.T) { diff --git a/libwallet/addresses/v4.go b/libwallet/addresses/v4.go index b99dee64..d9c016ff 100644 --- a/libwallet/addresses/v4.go +++ b/libwallet/addresses/v4.go @@ -4,9 +4,9 @@ import ( "crypto/sha256" "fmt" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/hdkeychain" ) // CreateAddressV4 returns a P2WSH WalletAddress from a user HD-pubkey and a Muun co-signing HD-pubkey. diff --git a/libwallet/addresses/v4_test.go b/libwallet/addresses/v4_test.go index b0293e35..c3d8a9d4 100755 --- a/libwallet/addresses/v4_test.go +++ b/libwallet/addresses/v4_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - "github.com/btcsuite/btcutil/hdkeychain" + "github.com/btcsuite/btcd/btcutil/hdkeychain" ) func TestCreateAddressV4(t *testing.T) { diff --git a/libwallet/addresses/v5.go b/libwallet/addresses/v5.go index 1b868a96..73cbc36f 100644 --- a/libwallet/addresses/v5.go +++ b/libwallet/addresses/v5.go @@ -3,13 +3,13 @@ package addresses import ( "fmt" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil/hdkeychain" "github.com/muun/libwallet/btcsuitew/btcutilw" "github.com/muun/libwallet/musig" ) -// CreateAddressV5 returns a P2TR WalletAddress using Musig with the signing and cosigning keys. +// CreateAddressV5 returns a P2TR WalletAddress using Musig2v040Muun with the signing and cosigning keys. func CreateAddressV5(userKey, muunKey *hdkeychain.ExtendedKey, path string, network *chaincfg.Params) (*WalletAddress, error) { witnessProgram, err := CreateWitnessScriptV5(userKey, muunKey) if err != nil { @@ -31,20 +31,26 @@ func CreateAddressV5(userKey, muunKey *hdkeychain.ExtendedKey, path string, netw func CreateWitnessScriptV5(userKey, muunKey *hdkeychain.ExtendedKey) ([]byte, error) { userPublicKey, err := userKey.ECPubKey() if err != nil { - return nil, err + return nil, fmt.Errorf("error getting pub key: %w", err) } - muunPublicKey, err := muunKey.ECPubKey() if err != nil { - return nil, err + return nil, fmt.Errorf("error getting pub key: %w", err) } - combined, err := musig.CombinePubKeysWithTweak(userPublicKey, muunPublicKey, nil) + pubKeys := [][]byte{ + userPublicKey.SerializeCompressed(), + muunPublicKey.SerializeCompressed(), + } + + tweak := musig.KeySpendOnlyTweak() + + aggregateKey, err := musig.Musig2CombinePubKeysWithTweak(musig.Musig2v040Muun, pubKeys, tweak) if err != nil { - return nil, err + return nil, fmt.Errorf("error combining keys: %w", err) } - xOnlyCombined := combined.SerializeCompressed()[1:] + xOnlyCombined := aggregateKey.FinalKey.SerializeCompressed()[1:] return xOnlyCombined, nil } diff --git a/libwallet/addresses/v6.go b/libwallet/addresses/v6.go new file mode 100644 index 00000000..832f059f --- /dev/null +++ b/libwallet/addresses/v6.go @@ -0,0 +1,56 @@ +package addresses + +import ( + "fmt" + + "github.com/btcsuite/btcd/btcutil/hdkeychain" + "github.com/btcsuite/btcd/chaincfg" + "github.com/muun/libwallet/btcsuitew/btcutilw" + "github.com/muun/libwallet/musig" +) + +// CreateAddressV6 returns a P2TR WalletAddress using Musig2v100 with the signing and cosigning keys. +func CreateAddressV6(userKey, muunKey *hdkeychain.ExtendedKey, path string, network *chaincfg.Params) (*WalletAddress, error) { + witnessProgram, err := CreateWitnessScriptV6(userKey, muunKey) + if err != nil { + return nil, fmt.Errorf("failed to generate witness script v5: %w", err) + } + + address, err := btcutilw.NewAddressTaprootKey(witnessProgram, network) + if err != nil { + return nil, err + } + + return &WalletAddress{ + address: address.EncodeAddress(), + version: V6, + derivationPath: path, + }, nil +} + +func CreateWitnessScriptV6(userKey, muunKey *hdkeychain.ExtendedKey) ([]byte, error) { + userPublicKey, err := userKey.ECPubKey() + if err != nil { + return nil, fmt.Errorf("error getting pub key: %w", err) + } + muunPublicKey, err := muunKey.ECPubKey() + if err != nil { + return nil, fmt.Errorf("error getting pub key: %w", err) + } + + pubKeys := [][]byte{ + userPublicKey.SerializeCompressed(), + muunPublicKey.SerializeCompressed(), + } + + tweak := musig.KeySpendOnlyTweak() + + aggregateKey, err := musig.Musig2CombinePubKeysWithTweak(musig.Musig2v100, pubKeys, tweak) + if err != nil { + return nil, fmt.Errorf("error combining keys: %w", err) + } + + xOnlyCombined := aggregateKey.FinalKey.SerializeCompressed()[1:] + + return xOnlyCombined, nil +} diff --git a/libwallet/addresses/v6_test.go b/libwallet/addresses/v6_test.go new file mode 100644 index 00000000..74fa2ebe --- /dev/null +++ b/libwallet/addresses/v6_test.go @@ -0,0 +1,34 @@ +package addresses + +import ( + "reflect" + "testing" +) + +func TestCreateAddressV6(t *testing.T) { + const ( + addressPath = "m/schema:1'/recovery:1'/external:1/17" + + v6Address = "bcrt1pn6w5h4pysxfuusld4xg8ppquewfk7mg9uzvaxjv625dq7ac6z3ys4amxtf" + basePK = "tpubDBf5wCeqg3KrLJiXaveDzD5JtFJ1ss9NVvFMx4RYS73SjwPEEawcAQ7V1B5DGM4gunWDeYNrnkc49sUaf7mS1wUKiJJQD6WEctExUQoLvrg" + baseCosigningPK = "tpubDB22PFkUaHoB7sgxh7exCivV5rAevVSzbB8WkFCCdbHq39r8xnYexiot4NGbi8PM6E1ySVeaHsoDeMYb6EMndpFrzVmuX8iQNExzwNpU61B" + basePath = "m/schema:1'/recovery:1'" + ) + + baseMuunKey := parseKey(baseCosigningPK) + muunKey := derive(baseMuunKey, basePath, addressPath) + + baseUserKey := parseKey(basePK) + userKey := derive(baseUserKey, basePath, addressPath) + + expectedAddr := &WalletAddress{address: v6Address, derivationPath: addressPath, version: 6} + + actualAddr, err := CreateAddressV6(userKey, muunKey, addressPath, network) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(actualAddr, expectedAddr) { + t.Errorf("Created v6 address %v, expected %v", actualAddr, expectedAddr) + } +} diff --git a/libwallet/bridge.go b/libwallet/bridge.go index 26fe364b..7fb56e30 100644 --- a/libwallet/bridge.go +++ b/libwallet/bridge.go @@ -5,7 +5,7 @@ type StringList struct { } func NewStringList() *StringList { - return &StringList{} + return &StringList{} } func NewStringListWithElements(elems []string) *StringList { @@ -34,6 +34,10 @@ func (l *StringList) Contains(s string) bool { return false } +func (l *StringList) ConvertToArray() []string { + return l.elems +} + type IntList struct { elems []int } diff --git a/libwallet/bridge_logs.go b/libwallet/bridge_logs.go new file mode 100644 index 00000000..e75931e8 --- /dev/null +++ b/libwallet/bridge_logs.go @@ -0,0 +1,44 @@ +package libwallet + +import ( + "io" + "log/slog" + "path/filepath" + "strings" +) + +// NewBridgeLogHandler returns a [slog.JSONHandler]] that forwards logs to the provided +// sink. +func NewBridgeLogHandler(sink AppLogSink, level slog.Level) *slog.JSONHandler { + opts := &slog.HandlerOptions{ + AddSource: true, + Level: level, + ReplaceAttr: replaceAttrs, + } + + return slog.NewJSONHandler(sink, opts) +} + +func replaceAttrs(groups []string, a slog.Attr) slog.Attr { + // Trim the values in the source key. + if a.Key == slog.SourceKey { + source := a.Value.Any().(*slog.Source) + + // Remove the directory from the source's filename. + source.File = filepath.Base(source.File) + + // Remove the module and package from the function's name. + funcName := source.Function + lastSlash := max(0, strings.LastIndexByte(funcName, '/')) + lastDot := strings.LastIndexByte(funcName[lastSlash:], '.') + lastSlash + source.Function = funcName[lastDot+1:] + } + return a +} + +// AppLogSink is the interface we provide to the containing application's log infrastructure. +// It's really just an [io.Writer] but we have to provide our own name for it to make gobind +// export it as an ObjC protocol or a Java interface. +type AppLogSink interface { + io.Writer +} diff --git a/libwallet/btcsuitew/btcutilw/address.go b/libwallet/btcsuitew/btcutilw/address.go index cde1ea45..97029dd6 100644 --- a/libwallet/btcsuitew/btcutilw/address.go +++ b/libwallet/btcsuitew/btcutilw/address.go @@ -8,8 +8,8 @@ import ( "fmt" "strings" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil" ) // DecodeAddress uses btcutil.DecodeAddress for all cases except SegWit version 1, which is handled diff --git a/libwallet/btcsuitew/txscriptw/standard.go b/libwallet/btcsuitew/txscriptw/standard.go index edfc69f1..09017682 100644 --- a/libwallet/btcsuitew/txscriptw/standard.go +++ b/libwallet/btcsuitew/txscriptw/standard.go @@ -1,8 +1,8 @@ package txscriptw import ( + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcutil" "github.com/muun/libwallet/btcsuitew/btcutilw" ) diff --git a/libwallet/challenge_keys.go b/libwallet/challenge_keys.go index 51902ca1..35088d7a 100644 --- a/libwallet/challenge_keys.go +++ b/libwallet/challenge_keys.go @@ -8,8 +8,9 @@ import ( "errors" "fmt" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcutil/base58" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/btcsuite/btcd/btcutil/base58" ) const ( @@ -51,7 +52,7 @@ func NewChallengePrivateKey(input, salt []byte) *ChallengePrivateKey { key := Scrypt256(input, salt) // 2nd return value is the pub key which we don't need right now - priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), key) + priv, _ := btcec.PrivKeyFromBytes(key) return &ChallengePrivateKey{key: priv} } @@ -60,11 +61,7 @@ func NewChallengePrivateKey(input, salt []byte) *ChallengePrivateKey { func (k *ChallengePrivateKey) SignSha(payload []byte) ([]byte, error) { hash := sha256.Sum256(payload) - sig, err := k.key.Sign(hash[:]) - - if err != nil { - return nil, fmt.Errorf("failed to sign payload: %w", err) - } + sig := ecdsa.Sign(k.key, hash[:]) return sig.Serialize(), nil } diff --git a/libwallet/challenge_keys_test.go b/libwallet/challenge_keys_test.go index aa4fb7ca..4554066b 100644 --- a/libwallet/challenge_keys_test.go +++ b/libwallet/challenge_keys_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - "github.com/btcsuite/btcutil/base58" + "github.com/btcsuite/btcd/btcutil/base58" ) func TestNewChallengePrivateKey(t *testing.T) { diff --git a/libwallet/challenge_public_key.go b/libwallet/challenge_public_key.go index 1effdf86..0b02d1ad 100644 --- a/libwallet/challenge_public_key.go +++ b/libwallet/challenge_public_key.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "fmt" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcutil/base58" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil/base58" ) type ChallengePublicKey struct { @@ -15,7 +15,7 @@ type ChallengePublicKey struct { func NewChallengePublicKeyFromSerialized(serializedKey []byte) (*ChallengePublicKey, error) { - pubKey, err := btcec.ParsePubKey(serializedKey, btcec.S256()) + pubKey, err := btcec.ParsePubKey(serializedKey) if err != nil { return nil, err } diff --git a/libwallet/docker/builder.Dockerfile b/libwallet/docker/builder.Dockerfile new file mode 100644 index 00000000..b78b6baf --- /dev/null +++ b/libwallet/docker/builder.Dockerfile @@ -0,0 +1,6 @@ +FROM golang:1.22.6-bullseye + +ENV STATICCHECK_VERSION=2024.1.1 + +# install staticcheck (linter for go projects) +RUN go install "honnef.co/go/tools/cmd/staticcheck@${STATICCHECK_VERSION}" \ No newline at end of file diff --git a/libwallet/emergency_kit.go b/libwallet/emergency_kit.go index 072e6a39..99754e3f 100755 --- a/libwallet/emergency_kit.go +++ b/libwallet/emergency_kit.go @@ -10,12 +10,12 @@ import ( const ( EKVersionNeverExported = -1 // EKVersionOnlyKeys is the encrypted keys to be written down / emailed - EKVersionOnlyKeys = 1 + EKVersionOnlyKeys = 1 // EKVersionDescriptors is the first PDF including the descriptors - EKVersionDescriptors = 2 + EKVersionDescriptors = 2 // EKVersionMusig add the musig descriptors - EKVersionMusig = 3 - ekVersionCurrent = EKVersionMusig + EKVersionMusig = 3 + ekVersionCurrent = EKVersionMusig ) // EKInput input struct to fill the PDF diff --git a/libwallet/emergencykit/descriptors.go b/libwallet/emergencykit/descriptors.go index 39d16d40..dc5cc7d8 100644 --- a/libwallet/emergencykit/descriptors.go +++ b/libwallet/emergencykit/descriptors.go @@ -17,8 +17,8 @@ var descriptorFormats = []string{ "sh(wsh(multi(2, %s/1'/1'/1/*, %s/1'/1'/1/*)))", // V3 external "wsh(multi(2, %s/1'/1'/0/*, %s/1'/1'/0/*))", // V4 change "wsh(multi(2, %s/1'/1'/1/*, %s/1'/1'/1/*))", // V4 external - "tr(musig(%s/1'/1'/0/*, %s/1'/1'/0/*))", // V5 change - "tr(musig(%s/1'/1'/1/*, %s/1'/1'/1/*))", // V5 external + "tr(musig(%s/1'/1'/0/*, %s/1'/1'/0/*))", // V5 change + "tr(musig(%s/1'/1'/1/*, %s/1'/1'/1/*))", // V5 external } // GetDescriptors returns an array of raw output descriptors. diff --git a/libwallet/emergencykit/metadata.go b/libwallet/emergencykit/metadata.go index 520b36a8..0321ff5a 100644 --- a/libwallet/emergencykit/metadata.go +++ b/libwallet/emergencykit/metadata.go @@ -3,7 +3,6 @@ package emergencykit import ( "encoding/json" "fmt" - "io/ioutil" "os" "path/filepath" @@ -80,7 +79,7 @@ func (mr *MetadataReader) ReadMetadata() (*Metadata, error) { } // Create the temporary directory, with a deferred call to clean up: - tmpDir, err := ioutil.TempDir("", "ek-metadata-*") + tmpDir, err := os.MkdirTemp("", "ek-metadata-*") if err != nil { return nil, fmt.Errorf("ReadMetadata failed to create a temporary directory") } @@ -94,7 +93,7 @@ func (mr *MetadataReader) ReadMetadata() (*Metadata, error) { } // Read the contents of the file: - metadataBytes, err := ioutil.ReadFile(filepath.Join(tmpDir, metadataName)) + metadataBytes, err := os.ReadFile(filepath.Join(tmpDir, metadataName)) if err != nil { return nil, fmt.Errorf("ReadMetadata failed to read the extracted file: %w", err) } @@ -128,7 +127,7 @@ func (mw *MetadataWriter) WriteMetadata(metadata *Metadata) error { } // Write to the temporary file, with a deferred call to clean up: - err = ioutil.WriteFile(tmpFile, metadataBytes, os.FileMode(0600)) + err = os.WriteFile(tmpFile, metadataBytes, os.FileMode(0600)) if err != nil { return fmt.Errorf("WriteMetadata failed to write a temporary file: %w", err) } diff --git a/libwallet/emergencykit/metadata_test.go b/libwallet/emergencykit/metadata_test.go index 92e24e4b..24c072ca 100644 --- a/libwallet/emergencykit/metadata_test.go +++ b/libwallet/emergencykit/metadata_test.go @@ -2,7 +2,6 @@ package emergencykit import ( "encoding/hex" - "io/ioutil" "os" "path/filepath" "reflect" @@ -63,7 +62,7 @@ func TestReadWriteMetadata(t *testing.T) { } func createTmpDir(t *testing.T) string { - tmpDir, err := ioutil.TempDir("", "pdf") + tmpDir, err := os.MkdirTemp("", "pdf") if err != nil { t.Fatalf("Failed to create temporary directory %s: %v", tmpDir, err) } @@ -77,7 +76,7 @@ func createPdfFile(t *testing.T, path string) { t.Fatalf("Failed to hex-decode the sample PDF data: %v", err) } - err = ioutil.WriteFile(path, content, os.FileMode(0600)) + err = os.WriteFile(path, content, os.FileMode(0600)) if err != nil { t.Fatalf("Failed to write PDF to %s: %v", path, err) } diff --git a/libwallet/encrypt.go b/libwallet/encrypt.go index 10fcb995..e466acd2 100644 --- a/libwallet/encrypt.go +++ b/libwallet/encrypt.go @@ -15,8 +15,9 @@ import ( "github.com/muun/libwallet/aescbc" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcutil/base58" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/btcsuite/btcd/btcutil/base58" ) const serializedPublicKeyLength = btcec.PubKeyBytesLenCompressed @@ -102,7 +103,7 @@ func (e *hdPubKeyEncrypter) Encrypt(payload []byte) (string, error) { signaturePayload = append(signaturePayload, payload...) signaturePayload = append(signaturePayload, encryptionKey.SerializeCompressed()...) hash := sha256.Sum256(signaturePayload) - senderSignature, err := btcec.SignCompact(btcec.S256(), signingKey, hash[:], false) + senderSignature, err := ecdsa.SignCompact(signingKey, hash[:], false) if err != nil { return "", fmt.Errorf("Encrypt: failed to sign payload: %w", err) } @@ -302,7 +303,7 @@ func (d *hdPrivKeyDecrypter) Decrypt(payload string) ([]byte, error) { signatureData = append(signatureData, data...) signatureData = append(signatureData, encryptionKey.PubKey().SerializeCompressed()...) hash := sha256.Sum256(signatureData) - signatureKey, _, err := btcec.RecoverCompact(btcec.S256(), sig, hash[:]) + signatureKey, _, err := ecdsa.RecoverCompact(sig, hash[:]) if err != nil { return nil, fmt.Errorf("Decrypt: failed to verify signature: %w", err) } @@ -341,12 +342,12 @@ func encryptWithPubKey(pubKey *btcec.PublicKey, plaintext []byte) (*btcec.Public // generateSharedEncryptionSecret performs a ECDH with pubKey // Deprecated: this function is unsafe and generateSharedEncryptionSecretForAES should be used func generateSharedEncryptionSecret(pubKey *btcec.PublicKey) (*btcec.PublicKey, *big.Int, error) { - privEph, err := btcec.NewPrivateKey(btcec.S256()) + privEph, err := btcec.NewPrivateKey() if err != nil { return nil, nil, fmt.Errorf("generateSharedEncryptionSecretForAES: failed to generate key: %w", err) } - sharedSecret, _ := pubKey.ScalarMult(pubKey.X, pubKey.Y, privEph.D.Bytes()) + sharedSecret, _ := btcec.S256().ScalarMult(pubKey.X(), pubKey.Y(), privEph.ToECDSA().D.Bytes()) return privEph.PubKey(), sharedSecret, nil } @@ -384,12 +385,12 @@ func decryptWithPrivKey(privKey *btcec.PrivateKey, rawPubEph []byte, ciphertext // recoverSharedEncryptionSecret performs an ECDH to recover the encryption secret meant for privKey from rawPubEph // Deprecated: this function is unsafe and recoverSharedEncryptionSecretForAES should be used func recoverSharedEncryptionSecret(privKey *btcec.PrivateKey, rawPubEph []byte) (*big.Int, error) { - pubEph, err := btcec.ParsePubKey(rawPubEph, btcec.S256()) + pubEph, err := btcec.ParsePubKey(rawPubEph) if err != nil { return nil, fmt.Errorf("recoverSharedEncryptionSecretForAES: failed to parse pub eph: %w", err) } - sharedSecret, _ := pubEph.ScalarMult(pubEph.X, pubEph.Y, privKey.D.Bytes()) + sharedSecret, _ := btcec.S256().ScalarMult(pubEph.X(), pubEph.Y(), privKey.ToECDSA().D.Bytes()) return sharedSecret, nil } @@ -416,7 +417,7 @@ func randomBytes(count int) []byte { // What follows are work arounds for https://github.com/golang/go/issues/46893 type DecryptOperation struct { - d Decrypter + d Decrypter payload string } @@ -432,7 +433,7 @@ func (o *DecryptOperation) Decrypt() ([]byte, error) { } type EncryptOperation struct { - e Encrypter + e Encrypter payload []byte } diff --git a/libwallet/encrypt_test.go b/libwallet/encrypt_test.go index 298b1de4..56f437d3 100644 --- a/libwallet/encrypt_test.go +++ b/libwallet/encrypt_test.go @@ -9,8 +9,8 @@ import ( "strings" "testing" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcutil/base58" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/btcsuite/btcd/btcutil/base58" ) func TestPublicKeyEncryption(t *testing.T) { @@ -201,7 +201,7 @@ func TestPublicKeyDecryptV1(t *testing.T) { fakePrivKey, _ := fakeHdPrivKey.key.ECPrivKey() hash := sha256.Sum256(fakePayload) - fakeSig, _ := btcec.SignCompact(btcec.S256(), fakePrivKey, hash[:], false) + fakeSig, _ := ecdsa.SignCompact(fakePrivKey, hash[:], false) plaintext := bytes.NewBuffer(nil) addVariableBytes(plaintext, fakeSig) @@ -249,4 +249,4 @@ func TestEncDecOps(t *testing.T) { if !bytes.Equal(payload, decrypted) { t.Fatal("decrypt is bad") } -} \ No newline at end of file +} diff --git a/libwallet/features.go b/libwallet/features.go index cb4bcf3f..c12e0954 100644 --- a/libwallet/features.go +++ b/libwallet/features.go @@ -5,11 +5,12 @@ import ( ) const ( - BackendFeatureTaproot = "TAPROOT" - BackendFeatureTaprootPreactivation = "TAPROOT_PREACTIVATION" - BackendFeatureApolloBiometrics = "APOLLO_BIOMETRICS" - BackendFeatureHighFeesHomeBanner = "HIGH_FEES_HOME_BANNER" - BackendFeatureHighFeesReceiveFlow = "HIGH_FEES_RECEIVE_FLOW" + BackendFeatureTaproot = "TAPROOT" + BackendFeatureTaprootPreactivation = "TAPROOT_PREACTIVATION" + BackendFeatureApolloBiometrics = "APOLLO_BIOMETRICS" + BackendFeatureHighFeesHomeBanner = "HIGH_FEES_HOME_BANNER" + BackendFeatureHighFeesReceiveFlow = "HIGH_FEES_RECEIVE_FLOW" + BackendFeatureEffectiveFeesCalculation = "EFFECTIVE_FEES_CALCULATION" UserActivatedFeatureStatusOff = "off" UserActivatedFeatureStatusCanPreactivate = "can_preactivate" @@ -28,7 +29,7 @@ type UserActivatedFeature interface { BackendPreactivationFeature() string } -type taprootUserActivatedFeature struct {} +type taprootUserActivatedFeature struct{} func (t *taprootUserActivatedFeature) Blockheight(network *Network) int { @@ -138,5 +139,6 @@ func DetermineUserActivatedFeatureStatus( } - - +func DetermineBackendActivatedFeatureStatus(flag string) bool { + return Cfg.FeatureStatusProvider.IsBackendFlagEnabled(flag) +} diff --git a/libwallet/features_test.go b/libwallet/features_test.go index a5585854..0283f8fc 100644 --- a/libwallet/features_test.go +++ b/libwallet/features_test.go @@ -137,66 +137,66 @@ func Test_DetermineUserActivatedFeatureStatus(t *testing.T) { { "backend only preactivation for pre-activated user", args{ - feature: UserActivatedFeatureTaproot, - height: postTaprootActivationHeight, - kitVersion: exportedBoth, + feature: UserActivatedFeatureTaproot, + height: postTaprootActivationHeight, + kitVersion: exportedBoth, backendFeatures: backendFeaturesWithTaprootPreactivation, - network: Mainnet(), + network: Mainnet(), }, UserActivatedFeatureStatusPreactivated, }, { "backend only preactivation for scheduled activated user with no kit", args{ - feature: UserActivatedFeatureTaproot, - height: postTaprootActivationHeight, - kitVersion: neverExportedKit, + feature: UserActivatedFeatureTaproot, + height: postTaprootActivationHeight, + kitVersion: neverExportedKit, backendFeatures: backendFeaturesWithTaprootPreactivation, - network: Mainnet(), + network: Mainnet(), }, UserActivatedFeatureStatusOff, }, { "backend only preactivation for scheduled activated user with latest kit", args{ - feature: UserActivatedFeatureTaproot, - height: postTaprootActivationHeight, - kitVersion: exportedOnlyLatest, + feature: UserActivatedFeatureTaproot, + height: postTaprootActivationHeight, + kitVersion: exportedOnlyLatest, backendFeatures: backendFeaturesWithTaprootPreactivation, - network: Mainnet(), + network: Mainnet(), }, UserActivatedFeatureStatusScheduledActivation, }, { "backend turned off for pre-activated user", args{ - feature: UserActivatedFeatureTaproot, - height: postTaprootActivationHeight, - kitVersion: exportedBoth, + feature: UserActivatedFeatureTaproot, + height: postTaprootActivationHeight, + kitVersion: exportedBoth, backendFeatures: backendFeaturesWithoutTaproot, - network: Mainnet(), + network: Mainnet(), }, UserActivatedFeatureStatusOff, }, { "backend turned off for scheduled activated user with no kit", args{ - feature: UserActivatedFeatureTaproot, - height: postTaprootActivationHeight, - kitVersion: neverExportedKit, + feature: UserActivatedFeatureTaproot, + height: postTaprootActivationHeight, + kitVersion: neverExportedKit, backendFeatures: backendFeaturesWithoutTaproot, - network: Mainnet(), + network: Mainnet(), }, UserActivatedFeatureStatusOff, }, { "backend turned off for scheduled activated user with latest kit", args{ - feature: UserActivatedFeatureTaproot, - height: postTaprootActivationHeight, - kitVersion: exportedOnlyLatest, + feature: UserActivatedFeatureTaproot, + height: postTaprootActivationHeight, + kitVersion: exportedOnlyLatest, backendFeatures: backendFeaturesWithoutTaproot, - network: Mainnet(), + network: Mainnet(), }, UserActivatedFeatureStatusOff, }, @@ -209,3 +209,31 @@ func Test_DetermineUserActivatedFeatureStatus(t *testing.T) { }) } } + +type TestBackendActivatedFeatureStatusProvider struct{} + +func (t TestBackendActivatedFeatureStatusProvider) IsBackendFlagEnabled(flag string) bool { + return flag == BackendFeatureEffectiveFeesCalculation +} + +func Test_DetermineBackendActivatedFeatureStatus(t *testing.T) { + Cfg = &Config{ + DataDir: "", + FeatureStatusProvider: TestBackendActivatedFeatureStatusProvider{}, + } + + var status = DetermineBackendActivatedFeatureStatus(BackendFeatureEffectiveFeesCalculation) + if !status { + t.Errorf("DetermineBackendActivatedFeatureStatus(BackendFeatureEffectiveFeesCalculation) = %v, want %v", status, true) + } + + status = DetermineBackendActivatedFeatureStatus(BackendFeatureHighFeesHomeBanner) + if status { + t.Errorf("DetermineBackendActivatedFeatureStatus(BackendFeatureHighFeesHomeBanner) = %v, want %v", status, false) + } + + status = DetermineBackendActivatedFeatureStatus("UnknownFlag") + if status { + t.Errorf("DetermineBackendActivatedFeatureStatus(\"UnknownFlag\") = %v, want %v", status, false) + } +} diff --git a/libwallet/fees/fees.go b/libwallet/fees/fees.go index 6b54fd4d..2fa2b653 100644 --- a/libwallet/fees/fees.go +++ b/libwallet/fees/fees.go @@ -1,6 +1,6 @@ package fees -import "github.com/btcsuite/btcutil" +import "github.com/btcsuite/btcd/btcutil" const dustThreshold = 546 diff --git a/libwallet/fees/fees_test.go b/libwallet/fees/fees_test.go index 9244f4ed..4507236e 100644 --- a/libwallet/fees/fees_test.go +++ b/libwallet/fees/fees_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/btcutil" ) func TestComputeSwapFees(t *testing.T) { diff --git a/libwallet/go.mod b/libwallet/go.mod index 0bd19427..2def56d8 100644 --- a/libwallet/go.mod +++ b/libwallet/go.mod @@ -1,25 +1,191 @@ module github.com/muun/libwallet -go 1.14 +go 1.22.1 require ( - 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/btcsuite/btcd v0.24.2-beta.rc1.0.20240403021926-ae5533602c46 + github.com/btcsuite/btcd/btcec/v2 v2.3.3 + github.com/btcsuite/btcd/btcutil v1.1.5 + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 + github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 + github.com/fiatjaf/go-lnurl v1.13.1 github.com/jinzhu/gorm v1.9.16 - github.com/lightningnetwork/lightning-onion v1.0.1 - github.com/lightningnetwork/lnd v0.10.4-beta - github.com/miekg/dns v1.1.29 // indirect + github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f + github.com/lightningnetwork/lnd v0.18.0-beta + github.com/lightningnetwork/lnd/tlv v1.2.3 github.com/pdfcpu/pdfcpu v0.3.11 github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.2.0 + github.com/stretchr/testify v1.9.0 + github.com/test-go/testify v1.1.4 golang.org/x/crypto v0.25.0 + google.golang.org/protobuf v1.33.0 + gopkg.in/gormigrate.v1 v1.6.0 +) + +require ( + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect + github.com/aead/siphash v1.0.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/btcsuite/btcd/btcutil/psbt v1.1.8 // indirect + github.com/btcsuite/btcwallet v0.16.10-0.20240404104514-b2f31f9045fb // indirect + github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 // indirect + github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 // indirect + github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4 // indirect + github.com/btcsuite/btcwallet/walletdb v1.4.2 // indirect + github.com/btcsuite/btcwallet/wtxmgr v1.5.3 // indirect + github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect + github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect + github.com/btcsuite/winsvc v1.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/containerd/continuity v0.3.0 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/decred/dcrd/lru v1.1.2 // indirect + github.com/docker/cli v20.10.17+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/fergusstrange/embedded-postgres v1.25.0 // indirect + github.com/go-errors/errors v1.0.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.4.2 // indirect + github.com/golang-migrate/migrate/v4 v4.17.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650 // indirect + github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.14.3 // indirect + github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.3 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/pgx/v4 v4.18.2 // indirect + github.com/jessevdk/go-flags v1.4.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.1 // indirect + github.com/jonboulle/clockwork v0.2.2 // indirect + github.com/jrick/logrotate v1.0.0 // indirect + github.com/json-iterator/go v1.1.11 // indirect + github.com/kkdai/bstream v1.0.0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect + github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd // indirect + github.com/lightninglabs/neutrino/cache v1.1.2 // indirect + github.com/lightningnetwork/lnd/clock v1.1.1 // indirect + github.com/lightningnetwork/lnd/fn v1.0.5 // indirect + github.com/lightningnetwork/lnd/healthcheck v1.2.4 // indirect + github.com/lightningnetwork/lnd/kvdb v1.4.8 // indirect + github.com/lightningnetwork/lnd/queue v1.1.1 // indirect + github.com/lightningnetwork/lnd/sqldb v1.0.2 // indirect + github.com/lightningnetwork/lnd/ticker v1.1.1 // indirect + github.com/lightningnetwork/lnd/tor v1.1.2 // indirect + github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/miekg/dns v1.1.43 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/nbd-wtf/ln-decodepay v1.6.0 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/opencontainers/runc v1.1.12 // indirect + github.com/ory/dockertest/v3 v3.10.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.11.1 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.26.0 // indirect + github.com/prometheus/procfs v0.6.0 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/sirupsen/logrus v1.9.2 // indirect + github.com/soheilhy/cmux v0.1.5 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + github.com/tidwall/gjson v1.9.3 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect + github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect + go.etcd.io/bbolt v1.3.7 // indirect + go.etcd.io/etcd/api/v3 v3.5.7 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect + go.etcd.io/etcd/client/v2 v2.305.7 // indirect + go.etcd.io/etcd/client/v3 v3.5.7 // indirect + go.etcd.io/etcd/pkg/v3 v3.5.7 // indirect + go.etcd.io/etcd/raft/v3 v3.5.7 // indirect + go.etcd.io/etcd/server/v3 v3.5.7 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0 // indirect + go.opentelemetry.io/otel v1.0.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1 // indirect + go.opentelemetry.io/otel/sdk v1.0.1 // indirect + go.opentelemetry.io/otel/trace v1.0.1 // indirect + go.opentelemetry.io/proto/otlp v0.19.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.17.0 // indirect + golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect golang.org/x/image v0.18.0 // indirect golang.org/x/mobile v0.0.0-20220414153400-ce6a79cf6a13 // indirect + golang.org/x/mod v0.19.0 // indirect golang.org/x/net v0.27.0 // indirect - google.golang.org/protobuf v1.25.0 - gopkg.in/gormigrate.v1 v1.6.0 + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.23.0 // indirect + google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect + google.golang.org/grpc v1.59.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.49.3 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/sqlite v1.29.8 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect + sigs.k8s.io/yaml v1.2.0 // indirect ) // Fork that includes the -cache flag for quicker builds replace golang.org/x/mobile => github.com/muun/mobile v0.0.0-20240709203120-049ae58602a0 + +// Use newer version to prevent compilation errors https://github.com/hashicorp/go-dbw/issues/56 +replace github.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.14.16 diff --git a/libwallet/go.sum b/libwallet/go.sum index f7420546..e0d136e8 100644 --- a/libwallet/go.sum +++ b/libwallet/go.sum @@ -1,139 +1,390 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 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/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -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/go.mod h1:GbuBk21JqF+driLX3XtJYNZjGa45YDoa9IqCTzNSfEc= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -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/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 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= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.20.1-beta.0.20200513120220-b470eee47728/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.20.1-beta.0.20200515232429-9f0179fd2c46 h1:QyTpiR5nQe94vza2qkvf7Ns8XX2Rjh/vdIhO3RzGj4o= -github.com/btcsuite/btcd v0.20.1-beta.0.20200515232429-9f0179fd2c46/go.mod h1:Yktc19YNjh/Iz2//CX0vfRTS4IJKM/RKO5YZ9Fn+Pgo= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240403021926-ae5533602c46 h1:tjpNTdZNQqE14menwDGAxWfzN0DFHVTXFEyEL8yvA/4= +github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240403021926-ae5533602c46/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= +github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/btcutil/psbt v1.1.8 h1:4voqtT8UppT7nmKQkXV+T9K8UyQjKOn2z/ycpmJK8wg= +github.com/btcsuite/btcd/btcutil/psbt v1.1.8/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8XGBQuuTmuKYUf6q7/U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= -github.com/btcsuite/btcutil/psbt v1.0.2 h1:gCVY3KxdoEVU7Q6TjusPO+GANIwVgr9yTLqM+a6CZr8= -github.com/btcsuite/btcutil/psbt v1.0.2/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ= -github.com/btcsuite/btcwallet v0.11.1-0.20200612012534-48addcd5591a h1:AZ1Mf0gd9mgJqrTTIFUc17ep9EKUbQusVAIzJ6X+x3Q= -github.com/btcsuite/btcwallet v0.11.1-0.20200612012534-48addcd5591a/go.mod h1:9+AH3V5mcTtNXTKe+fe63fDLKGOwQbZqmvOVUef+JFE= -github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0 h1:KGHMW5sd7yDdDMkCZ/JpP0KltolFsQcB973brBnfj4c= -github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU= -github.com/btcsuite/btcwallet/wallet/txrules v1.0.0 h1:2VsfS0sBedcM5KmDzRMT3+b6xobqWveZGvjb+jFez5w= -github.com/btcsuite/btcwallet/wallet/txrules v1.0.0/go.mod h1:UwQE78yCerZ313EXZwEiu3jNAtfXj2n2+c8RWiE/WNA= -github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0 h1:6DxkcoMnCPY4E9cUDPB5tbuuf40SmmMkSQkoE8vCT+s= -github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs= -github.com/btcsuite/btcwallet/walletdb v1.0.0/go.mod h1:bZTy9RyYZh9fLnSua+/CD48TJtYJSHjjYcSaszuxCCk= -github.com/btcsuite/btcwallet/walletdb v1.2.0/go.mod h1:9cwc1Yyg4uvd4ZdfdoMnALji+V9gfWSMfxEdLdR5Vwc= -github.com/btcsuite/btcwallet/walletdb v1.3.1/go.mod h1:9cwc1Yyg4uvd4ZdfdoMnALji+V9gfWSMfxEdLdR5Vwc= -github.com/btcsuite/btcwallet/walletdb v1.3.2/go.mod h1:GZCMPNpUu5KE3ASoVd+k06p/1OW8OwNGCCaNWRto2cQ= -github.com/btcsuite/btcwallet/walletdb v1.3.3 h1:u6e7vRIKBF++cJy+hOHaMGg+88ZTwvpaY27AFvtB668= -github.com/btcsuite/btcwallet/walletdb v1.3.3/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= -github.com/btcsuite/btcwallet/wtxmgr v1.0.0/go.mod h1:vc4gBprll6BP0UJ+AIGDaySoc7MdAmZf8kelfNb8CFY= -github.com/btcsuite/btcwallet/wtxmgr v1.2.0 h1:ZUYPsSv8GjF9KK7lboB2OVHF0uYEcHxgrCfFWqPd9NA= -github.com/btcsuite/btcwallet/wtxmgr v1.2.0/go.mod h1:h8hkcKUE3X7lMPzTUoGnNiw5g7VhGrKEW3KpR2r0VnY= +github.com/btcsuite/btcwallet v0.16.10-0.20240404104514-b2f31f9045fb h1:qoIOlBPRZWtfpcbQlNFf67Wz8ZlXo+mxQc9Pnbm/iqU= +github.com/btcsuite/btcwallet v0.16.10-0.20240404104514-b2f31f9045fb/go.mod h1:2C3Q/MhYAKmk7F+Tey6LfKtKRTdQsrCf8AAAzzDPmH4= +github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 h1:poyHFf7+5+RdxNp5r2T6IBRD7RyraUsYARYbp/7t4D8= +github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4/go.mod h1:GETGDQuyq+VFfH1S/+/7slLM/9aNa4l7P4ejX6dJfb0= +github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 h1:UZo7YRzdHbwhK7Rhv3PO9bXgTxiOH45edK5qdsdiatk= +github.com/btcsuite/btcwallet/wallet/txrules v1.2.1/go.mod h1:MVSqRkju/IGxImXYPfBkG65FgEZYA4fXchheILMVl8g= +github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4 h1:nmcKAVTv/cmYrs0A4hbiC6Qw+WTLYy/14SmTt3mLnCo= +github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4/go.mod h1:YqJR8WAAHiKIPesZTr9Cx9Az4fRhRLcJ6GcxzRUZCAc= +github.com/btcsuite/btcwallet/walletdb v1.4.2 h1:zwZZ+zaHo4mK+FAN6KeK85S3oOm+92x2avsHvFAhVBE= +github.com/btcsuite/btcwallet/walletdb v1.4.2/go.mod h1:7ZQ+BvOEre90YT7eSq8bLoxTsgXidUzA/mqbRS114CQ= +github.com/btcsuite/btcwallet/wtxmgr v1.5.3 h1:QrWCio9Leh3DwkWfp+A1SURj8pYn3JuTLv3waP5uEro= +github.com/btcsuite/btcwallet/wtxmgr v1.5.3/go.mod h1:M4nQpxGTXiDlSOODKXboXX7NFthmiBNjzAKKNS7Fhjg= 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/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= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJGQE= 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/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI= +github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E= +github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= +github.com/cockroachdb/errors v1.2.4 h1:Lap807SXTH5tri2TivECb/4abUkMZC9zRoLarvcKDqs= +github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 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= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/decred/dcrd/lru v1.1.2 h1:KdCzlkxppuoIDGEvCGah1fZRicrDH36IipvlB1ROkFY= +github.com/decred/dcrd/lru v1.1.2/go.mod h1:gEdCVgXs1/YoBvFWt7Scgknbhwik3FgVSzlnCcXL2N8= 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/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dhui/dktest v0.4.0 h1:z05UmuXZHO/bgj/ds2bGMBu8FI4WA+Ag/m3ghL+om7M= +github.com/dhui/dktest v0.4.0/go.mod h1:v/Dbz1LgCBOi2Uki2nUqLBGa83hWBGFMu5MrgMDCc78= +github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= +github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= 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/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fergusstrange/embedded-postgres v1.25.0 h1:sa+k2Ycrtz40eCRPOzI7Ry7TtkWXXJ+YRsxpKMDhxK0= +github.com/fergusstrange/embedded-postgres v1.25.0/go.mod h1:t/MLs0h9ukYM6FSt99R7InCHs1nW0ordoVCcnzmpTYw= +github.com/fiatjaf/go-lnurl v1.13.1 h1:7AeseawVNsl7JkZgAkTLf1mxAPbQuNrCPn3v7Qod0VQ= +github.com/fiatjaf/go-lnurl v1.13.1/go.mod h1:GaZb1TFGKiMlmG7yphWg6pifLL2vJaRiDHAn3ZV9L9s= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= +github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= 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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 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-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 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-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= +github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU= +github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM= 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/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 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= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= 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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +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/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 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= github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk= github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7 h1:o1wMw7uTNyA58IlEdDpxIrtFHTgnvYzA8sCQz8luv94= 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/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= -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/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= +github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0= +github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 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= @@ -143,127 +394,303 @@ github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLl github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v0.0.0-20181116074157-8ec929ed50c3/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc= -github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 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/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 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/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= -github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= -github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= -github.com/juju/retry v0.0.0-20180821225755-9058e192b216/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4= -github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= -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/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 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/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8= +github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 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/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= -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/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/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= -github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms= -github.com/lightningnetwork/lnd/queue v1.0.4 h1:8Dq3vxAFSACPy+pKN88oPFhuCpCoAAChPBwa4BJxH4k= -github.com/lightningnetwork/lnd/queue v1.0.4/go.mod h1:YTkTVZCxz8tAYreH27EO3s8572ODumWrNdYW2E/YKxg= -github.com/lightningnetwork/lnd/ticker v1.0.0 h1:S1b60TEGoTtCe2A0yeB+ecoj/kkS4qpwh6l+AkQEZwU= -github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0= +github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd h1:D8aRocHpoCv43hL8egXEMYyPmyOiefFHZ66338KQB2s= +github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd/go.mod h1:x3OmY2wsA18+Kc3TSV2QpSUewOCiscw2mKpXgZv2kZk= +github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3shmlu5hIQ798g= +github.com/lightninglabs/neutrino/cache v1.1.2/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo= +github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f h1:Pua7+5TcFEJXIIZ1I2YAUapmbcttmLj4TTi786bIi3s= +github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI= +github.com/lightningnetwork/lnd v0.18.0-beta h1:3cH7npkUh156FI5kb6bZbiO+Fl3YD+Bu2UbFKoLZ4lo= +github.com/lightningnetwork/lnd v0.18.0-beta/go.mod h1:1SA9iv9rZddNAcfP38SN9lNSVT1zf5aqmukLUoomjDU= +github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= +github.com/lightningnetwork/lnd/clock v1.1.1/go.mod h1:mGnAhPyjYZQJmebS7aevElXKTFDuO+uNFFfMXK1W8xQ= +github.com/lightningnetwork/lnd/fn v1.0.5 h1:ffDgMSn83avw6rNzxhbt6w5/2oIrwQKTPGfyaLupZtE= +github.com/lightningnetwork/lnd/fn v1.0.5/go.mod h1:P027+0CyELd92H9gnReUkGGAqbFA1HwjHWdfaDFD51U= +github.com/lightningnetwork/lnd/healthcheck v1.2.4 h1:lLPLac+p/TllByxGSlkCwkJlkddqMP5UCoawCj3mgFQ= +github.com/lightningnetwork/lnd/healthcheck v1.2.4/go.mod h1:G7Tst2tVvWo7cx6mSBEToQC5L1XOGxzZTPB29g9Rv2I= +github.com/lightningnetwork/lnd/kvdb v1.4.8 h1:xH0a5Vi1yrcZ5BEeF2ba3vlKBRxrL9uYXlWTjOjbNTY= +github.com/lightningnetwork/lnd/kvdb v1.4.8/go.mod h1:J2diNABOoII9UrMnxXS5w7vZwP7CA1CStrl8MnIrb3A= +github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI= +github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4= +github.com/lightningnetwork/lnd/sqldb v1.0.2 h1:PfuYzScYMD9/QonKo/QvgsbXfTnH5DfldIimkfdW4Bk= +github.com/lightningnetwork/lnd/sqldb v1.0.2/go.mod h1:V2Xl6JNWLTKE97WJnwfs0d0TYJdIQTqK8/3aAwkd3qI= +github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM= +github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA= +github.com/lightningnetwork/lnd/tlv v1.2.3 h1:If5ibokA/UoCBGuCKaY6Vn2SJU0l9uAbehCnhTZjEP8= +github.com/lightningnetwork/lnd/tlv v1.2.3/go.mod h1:zDkmqxOczP6LaLTvSFDQ1SJUfHcQRCMKFj93dn3eMB8= +github.com/lightningnetwork/lnd/tor v1.1.2 h1:3zv9z/EivNFaMF89v3ciBjCS7kvCj4ZFG7XvD2Qq0/k= +github.com/lightningnetwork/lnd/tor v1.1.2/go.mod h1:j7T9uJ2NLMaHwE7GiBGnpYLn4f7NRoTM6qj+ul6/ycA= 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/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +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/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 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/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nbd-wtf/ln-decodepay v1.6.0 h1:SwitboTc319Ezh7lYfT/JiLclLIP2PhRCEVXgbWSwo0= +github.com/nbd-wtf/ln-decodepay v1.6.0/go.mod h1:ZY0ZeLImteHHe9Uub75c+V23L0EMNYnBX0lnEAI0KWM= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 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= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= +github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= +github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/pdfcpu/pdfcpu v0.3.11 h1:T5XLD5blrB61tBjkSrQnwikrQO4gmwQm61fsyGZa04w= github.com/pdfcpu/pdfcpu v0.3.11/go.mod h1:SZ51teSs9l709Xim2VEuOYGf+uf7RdH2eY0LrXvz7n8= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -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/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 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/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= -github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= -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/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY= -github.com/urfave/cli v1.18.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= +github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= +github.com/tidwall/gjson v1.9.3 h1:hqzS9wAHMO+KVBBkLxYdkEeeFHuqr95GfClRLKlgK0E= +github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= +go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY= +go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= +go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg= +go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= +go.etcd.io/etcd/client/v2 v2.305.7 h1:AELPkjNR3/igjbO7CjyF1fPuVPjrblliiKj+Y6xSGOU= +go.etcd.io/etcd/client/v2 v2.305.7/go.mod h1:GQGT5Z3TBuAQGvgPfhR7VPySu/SudxmEkRq9BgzFU6s= +go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4= +go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw= +go.etcd.io/etcd/pkg/v3 v3.5.7 h1:obOzeVwerFwZ9trMWapU/VjDcYUJb5OfgC1zqEGWO/0= +go.etcd.io/etcd/pkg/v3 v3.5.7/go.mod h1:kcOfWt3Ov9zgYdOiJ/o1Y9zFfLhQjylTgL4Lru8opRo= +go.etcd.io/etcd/raft/v3 v3.5.7 h1:aN79qxLmV3SvIq84aNTliYGmjwsW6NqJSnqmI1HLJKc= +go.etcd.io/etcd/raft/v3 v3.5.7/go.mod h1:TflkAb/8Uy6JFBxcRaH2Fr6Slm9mCPVdI2efzxY96yU= +go.etcd.io/etcd/server/v3 v3.5.7 h1:BTBD8IJUV7YFgsczZMHhMTS67XuA4KpRquL0MFOJGRk= +go.etcd.io/etcd/server/v3 v3.5.7/go.mod h1:gxBgT84issUVBRpZ3XkW1T55NjOb4vZZRI4wVvNhf4A= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0 h1:Wx7nFnvCaissIUZxPkBqDz2963Z+Cl+PkYbDKzTxDqQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0/go.mod h1:E5NNboN0UqSAki0Atn9kVwaN7I+l25gGxDqBueo/74E= +go.opentelemetry.io/otel v1.0.1 h1:4XKyXmfqJLOQ7feyV5DB6gsBFZ0ltB8vLtp6pj4JIcc= +go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1 h1:ofMbch7i29qIUf7VtF+r0HRF6ac0SBaPSziSsKp7wkk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1/go.mod h1:Kv8liBeVNFkkkbilbgWRpV+wWuu+H5xdOT6HAgd30iw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1 h1:CFMFNoz+CGprjFAFy+RJFrfEe4GBia3RRm2a4fREvCA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1/go.mod h1:xOvWoTOrQjxjW61xtOmD/WKGRYb/P4NzRo3bs65U6Rk= +go.opentelemetry.io/otel/sdk v1.0.1 h1:wXxFEWGo7XfXupPwVJvTBOaPBC9FEg0wB8hMNrKk+cA= +go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI= +go.opentelemetry.io/otel/trace v1.0.1 h1:StTeIH6Q3G4r0Fiw34LTokUFESZgIDUr0qIJ7mKmAfw= +go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= +go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 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= golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -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-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/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/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 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= @@ -271,17 +698,39 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v 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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 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/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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +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.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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= @@ -294,18 +743,39 @@ golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/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-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/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/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 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= @@ -315,11 +785,24 @@ 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/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= +golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= 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-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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= @@ -329,21 +812,61 @@ 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= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.6.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= @@ -352,6 +875,7 @@ 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-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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= @@ -361,9 +885,13 @@ 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.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -373,65 +901,233 @@ 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/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 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/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 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/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= +google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= 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= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 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.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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/go.mod h1:3NjfXwocQRYAPTq4/fzX+CwUhPRcR/azYRhj8G+LqMo= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 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/go.mod h1:B4/T17l+ZWGwxFSZQmlBwp25x+og7OkhETfr3S9MbIA= -gopkg.in/macaroon.v2 v2.0.0/go.mod h1:+I6LnTMkm/uV5ew/0nsulNjL16SK4+C8yDmRUzHR17I= -gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 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= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk= +modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA= +modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg= +modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.29.8 h1:nGKglNx9K5v0As+zF0/Gcl1kMkmaU1XynYyq92PbsC8= +modernc.org/sqlite v1.29.8/go.mod h1:lQPm27iqa4UNZpmr4Aor0MH0HkCLbt1huYDfWylLZFk= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/libwallet/hdpath/hdpath.go b/libwallet/hdpath/hdpath.go index edf90d4a..c7c66e02 100644 --- a/libwallet/hdpath/hdpath.go +++ b/libwallet/hdpath/hdpath.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/btcsuite/btcutil/hdkeychain" + "github.com/btcsuite/btcd/btcutil/hdkeychain" ) type Path string diff --git a/libwallet/hdprivatekey.go b/libwallet/hdprivatekey.go index b438096b..9f89d716 100644 --- a/libwallet/hdprivatekey.go +++ b/libwallet/hdprivatekey.go @@ -8,7 +8,8 @@ import ( "github.com/muun/libwallet/hdpath" - "github.com/btcsuite/btcutil/hdkeychain" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/btcsuite/btcd/btcutil/hdkeychain" ) // HDPrivateKey is an HD capable priv key @@ -79,7 +80,7 @@ func (p *HDPrivateKey) DerivedAt(index int64, hardened bool) (*HDPrivateKey, err modifier = hdkeychain.HardenedKeyStart } - child, err := p.key.Child(uint32(index) | modifier) + child, err := p.key.Derive(uint32(index) | modifier) if err != nil { return nil, err } @@ -132,10 +133,7 @@ func (p *HDPrivateKey) Sign(data []byte) ([]byte, error) { } hash := sha256.Sum256(data) - sig, err := signingKey.Sign(hash[:]) - if err != nil { - return nil, err - } + sig := ecdsa.Sign(signingKey, hash[:]) return sig.Serialize(), nil } diff --git a/libwallet/hdprivatekey_test.go b/libwallet/hdprivatekey_test.go index 255658a2..836350f1 100644 --- a/libwallet/hdprivatekey_test.go +++ b/libwallet/hdprivatekey_test.go @@ -4,7 +4,7 @@ import ( "crypto/sha256" "testing" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/chaincfg" ) @@ -279,7 +279,7 @@ func TestHDPrivateKeySign(t *testing.T) { if err != nil { t.Fatal(err) } - sig, err := btcec.ParseSignature(sigBytes, btcec.S256()) + sig, err := ecdsa.ParseSignature(sigBytes) if err != nil { t.Fatal(err) } @@ -288,7 +288,7 @@ func TestHDPrivateKeySign(t *testing.T) { t.Fatal(err) } hash := sha256.Sum256(data) - if ok := sig.Verify(hash[:], pubKey); !ok { - t.Fatal(err) + if !sig.Verify(hash[:], pubKey) { + t.Fatal("sig.Verify failed") } } diff --git a/libwallet/hdpublickey.go b/libwallet/hdpublickey.go index 01c6edcd..a2994255 100644 --- a/libwallet/hdpublickey.go +++ b/libwallet/hdpublickey.go @@ -7,8 +7,8 @@ import ( "github.com/muun/libwallet/hdpath" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/hdkeychain" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/hdkeychain" ) // HDPublicKey is an HD capable pub key @@ -47,7 +47,7 @@ func (p *HDPublicKey) DerivedAt(index int64) (*HDPublicKey, error) { return nil, fmt.Errorf("can't derive a hardened pub key (index %v)", index) } - child, err := p.key.Child(uint32(index)) + child, err := p.key.Derive(uint32(index)) if err != nil { return nil, err } diff --git a/libwallet/hdpublickey_test.go b/libwallet/hdpublickey_test.go index 8916a4da..c7fa237d 100644 --- a/libwallet/hdpublickey_test.go +++ b/libwallet/hdpublickey_test.go @@ -5,7 +5,7 @@ import ( "math" "testing" - "github.com/btcsuite/btcutil/hdkeychain" + "github.com/btcsuite/btcd/btcutil/hdkeychain" ) func TestHDPublicKey_DerivedAt(t *testing.T) { diff --git a/libwallet/incoming_swap.go b/libwallet/incoming_swap.go index 071eb107..1e63102e 100644 --- a/libwallet/incoming_swap.go +++ b/libwallet/incoming_swap.go @@ -7,10 +7,11 @@ import ( "fmt" "strings" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" + lndinput "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwire" "github.com/muun/libwallet/btcsuitew/txscriptw" "github.com/muun/libwallet/hdpath" @@ -214,6 +215,7 @@ type coinIncomingSwap struct { HtlcOutputKeyPath string } +// NOTE: this method only works on segwit v0 txs func (c *coinIncomingSwap) SignInput(index int, tx *wire.MsgTx, userKey *HDPrivateKey, muunKey *HDPublicKey) error { // Deserialize the HTLC transaction htlcTx := wire.MsgTx{} @@ -311,7 +313,7 @@ func (c *coinIncomingSwap) SignInput(index int, tx *wire.MsgTx, userKey *HDPriva return fmt.Errorf("expected fulfillment tx input to point to correct htlc output") } - sigHashes := txscript.NewTxSigHashes(tx) + sigHashes := lndinput.NewTxSigHashesV0Only(tx) muunSigKey, err := muunPublicKey.key.ECPubKey() if err != nil { @@ -360,7 +362,7 @@ func (c *coinIncomingSwap) SignInput(index int, tx *wire.MsgTx, userKey *HDPriva } // Sign the fulfillment tx - sig, err := signNativeSegwitInput( + sig, err := signNativeSegwitInputV0( index, tx, userPrivateKey, @@ -438,7 +440,7 @@ func (c *coinIncomingSwap) signature(index int, tx *wire.MsgTx, userKey *HDPubli prevOutAmount := htlcTx.TxOut[htlcOutputIndex].Value - sig, err := signNativeSegwitInput( + sig, err := signNativeSegwitInputV0( index, tx, signingKey, diff --git a/libwallet/incoming_swap_test.go b/libwallet/incoming_swap_test.go index c33671ef..7fa3124d 100644 --- a/libwallet/incoming_swap_test.go +++ b/libwallet/incoming_swap_test.go @@ -8,11 +8,11 @@ import ( "strings" "testing" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/record" @@ -122,7 +122,11 @@ func TestFulfillHtlc(t *testing.T) { panic(err) } - sigHashes := txscript.NewTxSigHashes(fulfillmentTx) + // prevFetcher is the structure used to select a virtual outpoint + prevFetcher := txscript.NewCannedPrevOutputFetcher( + pkScript, amt) + + sigHashes := txscript.NewTxSigHashes(fulfillmentTx, prevFetcher) muunSignature, err := txscript.RawTxInWitnessSignature( fulfillmentTx, sigHashes, @@ -257,18 +261,21 @@ func TestFulfillHtlcWithCollect(t *testing.T) { outputPath := "m/schema:1'/recovery:1'/34/56" addr := newAddressAt(userKey, muunKey, outputPath, network) - - fulfillmentTx.AddTxOut(&wire.TxOut{ + txOut := wire.TxOut{ PkScript: addr.ScriptAddress(), Value: outputAmount, - }) + } + fulfillmentTx.AddTxOut(&txOut) muunSignKey, err := muunHtlcKey.key.ECPrivKey() if err != nil { panic(err) } - sigHashes := txscript.NewTxSigHashes(fulfillmentTx) + prevFetcher := txscript.NewCannedPrevOutputFetcher( + txOut.PkScript, txOut.Value) + + sigHashes := txscript.NewTxSigHashes(fulfillmentTx, prevFetcher) muunSignature, err := txscript.RawTxInWitnessSignature( fulfillmentTx, sigHashes, @@ -788,13 +795,13 @@ func createSphinxPacket(nodePublicKey *btcec.PublicKey, paymentHash, paymentSecr b := &bytes.Buffer{} tlv.MustNewStream(tlvRecords...).Encode(b) - hopPayload, err := sphinx.NewHopPayload(nil, b.Bytes()) + hopPayload, err := sphinx.NewTLVHopPayload(b.Bytes()) if err != nil { panic(err) } paymentPath[0].HopPayload = hopPayload - ephemeralKey, err := btcec.NewPrivateKey(btcec.S256()) + ephemeralKey, err := btcec.NewPrivateKey() if err != nil { panic(err) } @@ -836,13 +843,13 @@ func createMppSphinxPacket( b := &bytes.Buffer{} tlv.MustNewStream(tlvRecords...).Encode(b) - hopPayload, err := sphinx.NewHopPayload(nil, b.Bytes()) + hopPayload, err := sphinx.NewTLVHopPayload(b.Bytes()) if err != nil { panic(err) } paymentPath[0].HopPayload = hopPayload - ephemeralKey, err := btcec.NewPrivateKey(btcec.S256()) + ephemeralKey, err := btcec.NewPrivateKey() if err != nil { panic(err) } diff --git a/libwallet/init.go b/libwallet/init.go index 6d2a0d43..68379c4c 100644 --- a/libwallet/init.go +++ b/libwallet/init.go @@ -1,26 +1,33 @@ package libwallet import ( + "log/slog" "runtime/debug" ) -// Listener is an interface implemented by the apps to receive notifications -// of data changes from the libwallet code. Each change is reported with a -// string tag identifying the type of change. -type Listener interface { - OnDataChanged(tag string) +// BackendActivatedFeatureStatusProvider is an interface implemented by the +// apps to provide us with information about the state of some backend side +// feature flags until we can implement a libwallet-side solution for this. +type BackendActivatedFeatureStatusProvider interface { + IsBackendFlagEnabled(flag string) bool } // Config defines the global libwallet configuration. type Config struct { - DataDir string - Listener Listener + DataDir string + FeatureStatusProvider BackendActivatedFeatureStatusProvider + AppLogSink AppLogSink } -var cfg *Config +var Cfg *Config // Init configures the libwallet func Init(c *Config) { debug.SetTraceback("crash") - cfg = c + Cfg = c + + if Cfg.AppLogSink != nil { + logger := slog.New(NewBridgeLogHandler(Cfg.AppLogSink, slog.LevelWarn)) + slog.SetDefault(logger) + } } diff --git a/libwallet/init_test.go b/libwallet/init_test.go index a3201162..54b4fee7 100644 --- a/libwallet/init_test.go +++ b/libwallet/init_test.go @@ -1,9 +1,9 @@ package libwallet -import "io/ioutil" +import "os" func setup() { - dir, err := ioutil.TempDir("", "libwallet") + dir, err := os.MkdirTemp("", "libwallet") if err != nil { panic(err) } diff --git a/libwallet/invoices.go b/libwallet/invoices.go index 25cd811b..149d3e13 100644 --- a/libwallet/invoices.go +++ b/libwallet/invoices.go @@ -12,10 +12,12 @@ import ( "path" "time" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/zpay32" @@ -324,9 +326,17 @@ func (i *InvoiceBuilder) Build() (string, error) { } // sign the invoice with the identity pubkey - signer := netann.NewNodeSigner(identityKey) + nodeKeyLocator := keychain.KeyLocator{ + Family: keychain.KeyFamilyNodeKey, + } + identityKeySigner := keychain.NewPrivKeyMessageSigner( + identityKey, nodeKeyLocator, + ) + signer := netann.NewNodeSigner(identityKeySigner) bech32, err := invoice.Encode(zpay32.MessageSigner{ - SignCompact: signer.SignDigestCompact, + SignCompact: func(msg []byte) ([]byte, error) { + return signer.SignMessageCompact(msg, false) + }, }) if err != nil { return "", err @@ -405,7 +415,7 @@ func GetInvoiceMetadata(paymentHash []byte) (string, error) { } func openDB() (*walletdb.DB, error) { - return walletdb.Open(path.Join(cfg.DataDir, "wallet.db")) + return walletdb.Open(path.Join(Cfg.DataDir, "wallet.db")) } func parsePubKey(s string) (*btcec.PublicKey, error) { @@ -413,7 +423,7 @@ func parsePubKey(s string) (*btcec.PublicKey, error) { if err != nil { return nil, err } - return btcec.ParsePubKey(bytes, btcec.S256()) + return btcec.ParsePubKey(bytes) } func verifyTxWitnessSignature(tx *wire.MsgTx, sigHashes *txscript.TxSigHashes, outputIndex int, amount int64, script []byte, sig []byte, signKey *btcec.PublicKey) error { @@ -421,7 +431,7 @@ func verifyTxWitnessSignature(tx *wire.MsgTx, sigHashes *txscript.TxSigHashes, o if err != nil { return err } - signature, err := btcec.ParseDERSignature(sig, btcec.S256()) + signature, err := ecdsa.ParseDERSignature(sig) if err != nil { return err } diff --git a/libwallet/invoices_test.go b/libwallet/invoices_test.go index 401ec58b..a980bbd2 100644 --- a/libwallet/invoices_test.go +++ b/libwallet/invoices_test.go @@ -6,7 +6,7 @@ import ( "encoding/json" "testing" - "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/zpay32" ) diff --git a/libwallet/keycrypt/keycrypt.go b/libwallet/keycrypt/keycrypt.go index d383ac27..1b4579c6 100755 --- a/libwallet/keycrypt/keycrypt.go +++ b/libwallet/keycrypt/keycrypt.go @@ -11,7 +11,7 @@ import ( "strings" "unicode/utf16" - "github.com/btcsuite/btcutil/hdkeychain" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/muun/libwallet/aescbc" "golang.org/x/crypto/scrypt" diff --git a/libwallet/keycrypt/keycrypt_test.go b/libwallet/keycrypt/keycrypt_test.go index a3cf185e..b2f91f3e 100755 --- a/libwallet/keycrypt/keycrypt_test.go +++ b/libwallet/keycrypt/keycrypt_test.go @@ -7,7 +7,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil/hdkeychain" + "github.com/btcsuite/btcd/btcutil/hdkeychain" ) var ( diff --git a/libwallet/lnurl_test.go b/libwallet/lnurl_test.go index 20cfd4e7..833541ed 100644 --- a/libwallet/lnurl_test.go +++ b/libwallet/lnurl_test.go @@ -106,7 +106,7 @@ func TestLNURLWithdrawAllowUnsafe(t *testing.T) { result := <-listener.ch if result != "DONE" { - t.Fatalf(result) + t.Fatalf("%s", result) } invoiceBuilder.Network(Mainnet()) @@ -119,6 +119,6 @@ func TestLNURLWithdrawAllowUnsafe(t *testing.T) { result = <-listener.ch if result != "DONE" { - t.Fatalf(result) + t.Fatalf("%s", result) } } diff --git a/libwallet/musig/README.md b/libwallet/musig/README.md deleted file mode 100644 index 24081406..00000000 --- a/libwallet/musig/README.md +++ /dev/null @@ -1,7 +0,0 @@ -CGo likes having all C files in the same folder as the go package that will use it. This can be avoided, but it requires building the lib ourselves. In the context of libwallet, that means cross compiling to iOS and Android targets, then selectively linking the proper one. Not exactly easy. - -The alternative is then to flatten libsecp256k1 to a single folder. We can now use golangs include directives to use the headers we need. So far so good, right? - -Wrong. The lib has a peculiar pattern of writing a lot of it's logic in .h files instead of .c files. CGo naturally only compiles .c files. To get around this, a new .c file is added: umbrella.c, which includes every source header we need. It's counterpart, umbrella.h includes every definition header we need to make things a bit easier to handle on Go's side. - -Some things to keep in mind if you want to update libsecp. The script does it best job to make everything work, but it might fail if any details change in the lib. After executing it, review added files to see if they are relevant and remove them if not. diff --git a/libwallet/musig/adaptor_impl.h b/libwallet/musig/adaptor_impl.h deleted file mode 100644 index dc75a479..00000000 --- a/libwallet/musig/adaptor_impl.h +++ /dev/null @@ -1,84 +0,0 @@ -/********************************************************************** - * Copyright (c) 2021 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef _SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_ -#define _SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_ - -#include "session.h" - -int secp256k1_musig_nonce_parity(const secp256k1_context* ctx, int *nonce_parity, secp256k1_musig_session *session) { - secp256k1_musig_session_internal session_i; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(nonce_parity != NULL); - ARG_CHECK(session != NULL); - - if (!secp256k1_musig_session_load(ctx, &session_i, session)) { - return 0; - } - *nonce_parity = session_i.fin_nonce_parity; - return 1; -} - -int secp256k1_musig_adapt(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *sec_adaptor32, int nonce_parity) { - secp256k1_scalar s; - secp256k1_scalar t; - int overflow; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig64 != NULL); - ARG_CHECK(sec_adaptor32 != NULL); - - secp256k1_scalar_set_b32(&s, &sig64[32], &overflow); - if (overflow) { - return 0; - } - secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow); - if (overflow) { - secp256k1_scalar_clear(&t); - return 0; - } - - if (nonce_parity) { - secp256k1_scalar_negate(&t, &t); - } - - secp256k1_scalar_add(&s, &s, &t); - secp256k1_scalar_get_b32(&sig64[32], &s); - secp256k1_scalar_clear(&t); - return 1; -} - -int secp256k1_musig_extract_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const unsigned char *pre_sig64, int nonce_parity) { - secp256k1_scalar t; - secp256k1_scalar s; - int overflow; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sec_adaptor32 != NULL); - ARG_CHECK(sig64 != NULL); - ARG_CHECK(pre_sig64 != NULL); - - secp256k1_scalar_set_b32(&t, &sig64[32], &overflow); - if (overflow) { - return 0; - } - secp256k1_scalar_negate(&t, &t); - - secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); - if (overflow) { - return 0; - } - secp256k1_scalar_add(&t, &t, &s); - - if (!nonce_parity) { - secp256k1_scalar_negate(&t, &t); - } - secp256k1_scalar_get_b32(sec_adaptor32, &t); - secp256k1_scalar_clear(&t); - return 1; -} - -#endif diff --git a/libwallet/musig/assumptions.h b/libwallet/musig/assumptions.h deleted file mode 100644 index 6dc527b2..00000000 --- a/libwallet/musig/assumptions.h +++ /dev/null @@ -1,80 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2020 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_ASSUMPTIONS_H -#define SECP256K1_ASSUMPTIONS_H - -#include - -#include "util.h" - -/* This library, like most software, relies on a number of compiler implementation defined (but not undefined) - behaviours. Although the behaviours we require are essentially universal we test them specifically here to - reduce the odds of experiencing an unwelcome surprise. -*/ - -struct secp256k1_assumption_checker { - /* This uses a trick to implement a static assertion in C89: a type with an array of negative size is not - allowed. */ - int dummy_array[( - /* Bytes are 8 bits. */ - (CHAR_BIT == 8) && - - /* No integer promotion for uint32_t. This ensures that we can multiply uintXX_t values where XX >= 32 - without signed overflow, which would be undefined behaviour. */ - (UINT_MAX <= UINT32_MAX) && - - /* Conversions from unsigned to signed outside of the bounds of the signed type are - implementation-defined. Verify that they function as reinterpreting the lower - bits of the input in two's complement notation. Do this for conversions: - - from uint(N)_t to int(N)_t with negative result - - from uint(2N)_t to int(N)_t with negative result - - from int(2N)_t to int(N)_t with negative result - - from int(2N)_t to int(N)_t with positive result */ - - /* To int8_t. */ - ((int8_t)(uint8_t)0xAB == (int8_t)-(int8_t)0x55) && - ((int8_t)(uint16_t)0xABCD == (int8_t)-(int8_t)0x33) && - ((int8_t)(int16_t)(uint16_t)0xCDEF == (int8_t)(uint8_t)0xEF) && - ((int8_t)(int16_t)(uint16_t)0x9234 == (int8_t)(uint8_t)0x34) && - - /* To int16_t. */ - ((int16_t)(uint16_t)0xBCDE == (int16_t)-(int16_t)0x4322) && - ((int16_t)(uint32_t)0xA1B2C3D4 == (int16_t)-(int16_t)0x3C2C) && - ((int16_t)(int32_t)(uint32_t)0xC1D2E3F4 == (int16_t)(uint16_t)0xE3F4) && - ((int16_t)(int32_t)(uint32_t)0x92345678 == (int16_t)(uint16_t)0x5678) && - - /* To int32_t. */ - ((int32_t)(uint32_t)0xB2C3D4E5 == (int32_t)-(int32_t)0x4D3C2B1B) && - ((int32_t)(uint64_t)0xA123B456C789D012ULL == (int32_t)-(int32_t)0x38762FEE) && - ((int32_t)(int64_t)(uint64_t)0xC1D2E3F4A5B6C7D8ULL == (int32_t)(uint32_t)0xA5B6C7D8) && - ((int32_t)(int64_t)(uint64_t)0xABCDEF0123456789ULL == (int32_t)(uint32_t)0x23456789) && - - /* To int64_t. */ - ((int64_t)(uint64_t)0xB123C456D789E012ULL == (int64_t)-(int64_t)0x4EDC3BA928761FEEULL) && -#if defined(SECP256K1_WIDEMUL_INT128) - ((int64_t)(((uint128_t)0xA1234567B8901234ULL << 64) + 0xC5678901D2345678ULL) == (int64_t)-(int64_t)0x3A9876FE2DCBA988ULL) && - (((int64_t)(int128_t)(((uint128_t)0xB1C2D3E4F5A6B7C8ULL << 64) + 0xD9E0F1A2B3C4D5E6ULL)) == (int64_t)(uint64_t)0xD9E0F1A2B3C4D5E6ULL) && - (((int64_t)(int128_t)(((uint128_t)0xABCDEF0123456789ULL << 64) + 0x0123456789ABCDEFULL)) == (int64_t)(uint64_t)0x0123456789ABCDEFULL) && - - /* To int128_t. */ - ((int128_t)(((uint128_t)0xB1234567C8901234ULL << 64) + 0xD5678901E2345678ULL) == (int128_t)(-(int128_t)0x8E1648B3F50E80DCULL * 0x8E1648B3F50E80DDULL + 0x5EA688D5482F9464ULL)) && -#endif - - /* Right shift on negative signed values is implementation defined. Verify that it - acts as a right shift in two's complement with sign extension (i.e duplicating - the top bit into newly added bits). */ - ((((int8_t)0xE8) >> 2) == (int8_t)(uint8_t)0xFA) && - ((((int16_t)0xE9AC) >> 4) == (int16_t)(uint16_t)0xFE9A) && - ((((int32_t)0x937C918A) >> 9) == (int32_t)(uint32_t)0xFFC9BE48) && - ((((int64_t)0xA8B72231DF9CF4B9ULL) >> 19) == (int64_t)(uint64_t)0xFFFFF516E4463BF3ULL) && -#if defined(SECP256K1_WIDEMUL_INT128) - ((((int128_t)(((uint128_t)0xCD833A65684A0DBCULL << 64) + 0xB349312F71EA7637ULL)) >> 39) == (int128_t)(((uint128_t)0xFFFFFFFFFF9B0674ULL << 64) + 0xCAD0941B79669262ULL)) && -#endif - 1) * 2 - 1]; -}; - -#endif /* SECP256K1_ASSUMPTIONS_H */ diff --git a/libwallet/musig/basic-config.h b/libwallet/musig/basic-config.h deleted file mode 100644 index 6f7693cb..00000000 --- a/libwallet/musig/basic-config.h +++ /dev/null @@ -1,17 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_BASIC_CONFIG_H -#define SECP256K1_BASIC_CONFIG_H - -#ifdef USE_BASIC_CONFIG - -#define ECMULT_WINDOW_SIZE 15 -#define ECMULT_GEN_PREC_BITS 4 - -#endif /* USE_BASIC_CONFIG */ - -#endif /* SECP256K1_BASIC_CONFIG_H */ diff --git a/libwallet/musig/eccommit.h b/libwallet/musig/eccommit.h deleted file mode 100644 index 6bb11039..00000000 --- a/libwallet/musig/eccommit.h +++ /dev/null @@ -1,28 +0,0 @@ -/********************************************************************** - * Copyright (c) 2020 The libsecp256k1-zkp Developers * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef SECP256K1_ECCOMMIT_H -#define SECP256K1_ECCOMMIT_H - -/** Helper function to add a 32-byte value to a scalar */ -static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak); -/** Helper function to add a 32-byte value, times G, to an EC point */ -static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak); - -/** Serializes elem as a 33 byte array. This is non-constant time with respect to - * whether pubp is the point at infinity. Thus, you may need to declassify - * pubp->infinity before calling this function. */ -static int secp256k1_ec_commit_pubkey_serialize_const(secp256k1_ge *pubp, unsigned char *buf33); -/** Compute an ec commitment tweak as hash(pubkey, data). */ -static int secp256k1_ec_commit_tweak(unsigned char *tweak32, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); -/** Compute an ec commitment as pubkey + hash(pubkey, data)*G. */ -static int secp256k1_ec_commit(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); -/** Compute a secret key commitment as seckey + hash(pubkey, data). */ -static int secp256k1_ec_commit_seckey(const secp256k1_ecmult_gen_context* ecmult_gen_ctx, secp256k1_scalar* seckey, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); -/** Verify an ec commitment as pubkey + hash(pubkey, data)*G ?= commitment. */ -static int secp256k1_ec_commit_verify(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); - -#endif /* SECP256K1_ECCOMMIT_H */ diff --git a/libwallet/musig/eccommit_impl.h b/libwallet/musig/eccommit_impl.h deleted file mode 100644 index 641c07d2..00000000 --- a/libwallet/musig/eccommit_impl.h +++ /dev/null @@ -1,73 +0,0 @@ -/********************************************************************** - * Copyright (c) 2020 The libsecp256k1 Developers * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#include - -#include "eckey.h" -#include "hash.h" - -/* from secp256k1.c */ -static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak); -static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *pubp, const unsigned char *tweak); - -static int secp256k1_ec_commit_pubkey_serialize_const(secp256k1_ge *pubp, unsigned char *buf33) { - if (secp256k1_ge_is_infinity(pubp)) { - return 0; - } - secp256k1_fe_normalize(&pubp->x); - secp256k1_fe_normalize(&pubp->y); - secp256k1_fe_get_b32(&buf33[1], &pubp->x); - buf33[0] = secp256k1_fe_is_odd(&pubp->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN; - return 1; -} - -/* Compute an ec commitment tweak as hash(pubp, data). */ -static int secp256k1_ec_commit_tweak(unsigned char *tweak32, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) -{ - unsigned char rbuf[33]; - - if (!secp256k1_ec_commit_pubkey_serialize_const(pubp, rbuf)) { - return 0; - } - secp256k1_sha256_write(sha, rbuf, sizeof(rbuf)); - secp256k1_sha256_write(sha, data, data_size); - secp256k1_sha256_finalize(sha, tweak32); - return 1; -} - -/* Compute an ec commitment as pubp + hash(pubp, data)*G. */ -static int secp256k1_ec_commit(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) { - unsigned char tweak[32]; - - *commitp = *pubp; - return secp256k1_ec_commit_tweak(tweak, commitp, sha, data, data_size) - && secp256k1_ec_pubkey_tweak_add_helper(ecmult_ctx, commitp, tweak); -} - -/* Compute the seckey of an ec commitment from the original secret key of the pubkey as seckey + - * hash(pubp, data). */ -static int secp256k1_ec_commit_seckey(secp256k1_scalar* seckey, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) { - unsigned char tweak[32]; - return secp256k1_ec_commit_tweak(tweak, pubp, sha, data, data_size) - && secp256k1_ec_seckey_tweak_add_helper(seckey, tweak); -} - -/* Verify an ec commitment as pubp + hash(pubp, data)*G ?= commitment. */ -static int secp256k1_ec_commit_verify(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) { - secp256k1_gej pj; - secp256k1_ge p; - - if (!secp256k1_ec_commit(ecmult_ctx, &p, pubp, sha, data, data_size)) { - return 0; - } - - /* Return p == commitp */ - secp256k1_ge_neg(&p, &p); - secp256k1_gej_set_ge(&pj, &p); - secp256k1_gej_add_ge_var(&pj, &pj, commitp, NULL); - return secp256k1_gej_is_infinity(&pj); -} - diff --git a/libwallet/musig/ecdsa.h b/libwallet/musig/ecdsa.h deleted file mode 100644 index d5e54d8c..00000000 --- a/libwallet/musig/ecdsa.h +++ /dev/null @@ -1,21 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_ECDSA_H -#define SECP256K1_ECDSA_H - -#include - -#include "scalar.h" -#include "group.h" -#include "ecmult.h" - -static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *r, secp256k1_scalar *s, const unsigned char *sig, size_t size); -static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar *r, const secp256k1_scalar *s); -static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message); -static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid); - -#endif /* SECP256K1_ECDSA_H */ diff --git a/libwallet/musig/ecdsa_impl.h b/libwallet/musig/ecdsa_impl.h deleted file mode 100644 index c32141e8..00000000 --- a/libwallet/musig/ecdsa_impl.h +++ /dev/null @@ -1,315 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013-2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - - -#ifndef SECP256K1_ECDSA_IMPL_H -#define SECP256K1_ECDSA_IMPL_H - -#include "scalar.h" -#include "field.h" -#include "group.h" -#include "ecmult.h" -#include "ecmult_gen.h" -#include "ecdsa.h" - -/** Group order for secp256k1 defined as 'n' in "Standards for Efficient Cryptography" (SEC2) 2.7.1 - * sage: for t in xrange(1023, -1, -1): - * .. p = 2**256 - 2**32 - t - * .. if p.is_prime(): - * .. print '%x'%p - * .. break - * 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' - * sage: a = 0 - * sage: b = 7 - * sage: F = FiniteField (p) - * sage: '%x' % (EllipticCurve ([F (a), F (b)]).order()) - * 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' - */ -static const secp256k1_fe secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST( - 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, - 0xBAAEDCE6UL, 0xAF48A03BUL, 0xBFD25E8CUL, 0xD0364141UL -); - -/** Difference between field and order, values 'p' and 'n' values defined in - * "Standards for Efficient Cryptography" (SEC2) 2.7.1. - * sage: p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F - * sage: a = 0 - * sage: b = 7 - * sage: F = FiniteField (p) - * sage: '%x' % (p - EllipticCurve ([F (a), F (b)]).order()) - * '14551231950b75fc4402da1722fc9baee' - */ -static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST( - 0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL -); - -static int secp256k1_der_read_len(size_t *len, const unsigned char **sigp, const unsigned char *sigend) { - size_t lenleft; - unsigned char b1; - VERIFY_CHECK(len != NULL); - *len = 0; - if (*sigp >= sigend) { - return 0; - } - b1 = *((*sigp)++); - if (b1 == 0xFF) { - /* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */ - return 0; - } - if ((b1 & 0x80) == 0) { - /* X.690-0207 8.1.3.4 short form length octets */ - *len = b1; - return 1; - } - if (b1 == 0x80) { - /* Indefinite length is not allowed in DER. */ - return 0; - } - /* X.690-207 8.1.3.5 long form length octets */ - lenleft = b1 & 0x7F; /* lenleft is at least 1 */ - if (lenleft > (size_t)(sigend - *sigp)) { - return 0; - } - if (**sigp == 0) { - /* Not the shortest possible length encoding. */ - return 0; - } - if (lenleft > sizeof(size_t)) { - /* The resulting length would exceed the range of a size_t, so - * certainly longer than the passed array size. - */ - return 0; - } - while (lenleft > 0) { - *len = (*len << 8) | **sigp; - (*sigp)++; - lenleft--; - } - if (*len > (size_t)(sigend - *sigp)) { - /* Result exceeds the length of the passed array. */ - return 0; - } - if (*len < 128) { - /* Not the shortest possible length encoding. */ - return 0; - } - return 1; -} - -static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) { - int overflow = 0; - unsigned char ra[32] = {0}; - size_t rlen; - - if (*sig == sigend || **sig != 0x02) { - /* Not a primitive integer (X.690-0207 8.3.1). */ - return 0; - } - (*sig)++; - if (secp256k1_der_read_len(&rlen, sig, sigend) == 0) { - return 0; - } - if (rlen == 0 || *sig + rlen > sigend) { - /* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */ - return 0; - } - if (**sig == 0x00 && rlen > 1 && (((*sig)[1]) & 0x80) == 0x00) { - /* Excessive 0x00 padding. */ - return 0; - } - if (**sig == 0xFF && rlen > 1 && (((*sig)[1]) & 0x80) == 0x80) { - /* Excessive 0xFF padding. */ - return 0; - } - if ((**sig & 0x80) == 0x80) { - /* Negative. */ - overflow = 1; - } - /* There is at most one leading zero byte: - * if there were two leading zero bytes, we would have failed and returned 0 - * because of excessive 0x00 padding already. */ - if (rlen > 0 && **sig == 0) { - /* Skip leading zero byte */ - rlen--; - (*sig)++; - } - if (rlen > 32) { - overflow = 1; - } - if (!overflow) { - if (rlen) memcpy(ra + 32 - rlen, *sig, rlen); - secp256k1_scalar_set_b32(r, ra, &overflow); - } - if (overflow) { - secp256k1_scalar_set_int(r, 0); - } - (*sig) += rlen; - return 1; -} - -static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) { - const unsigned char *sigend = sig + size; - size_t rlen; - if (sig == sigend || *(sig++) != 0x30) { - /* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */ - return 0; - } - if (secp256k1_der_read_len(&rlen, &sig, sigend) == 0) { - return 0; - } - if (rlen != (size_t)(sigend - sig)) { - /* Tuple exceeds bounds or garage after tuple. */ - return 0; - } - - if (!secp256k1_der_parse_integer(rr, &sig, sigend)) { - return 0; - } - if (!secp256k1_der_parse_integer(rs, &sig, sigend)) { - return 0; - } - - if (sig != sigend) { - /* Trailing garbage inside tuple. */ - return 0; - } - - return 1; -} - -static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar* ar, const secp256k1_scalar* as) { - unsigned char r[33] = {0}, s[33] = {0}; - unsigned char *rp = r, *sp = s; - size_t lenR = 33, lenS = 33; - secp256k1_scalar_get_b32(&r[1], ar); - secp256k1_scalar_get_b32(&s[1], as); - while (lenR > 1 && rp[0] == 0 && rp[1] < 0x80) { lenR--; rp++; } - while (lenS > 1 && sp[0] == 0 && sp[1] < 0x80) { lenS--; sp++; } - if (*size < 6+lenS+lenR) { - *size = 6 + lenS + lenR; - return 0; - } - *size = 6 + lenS + lenR; - sig[0] = 0x30; - sig[1] = 4 + lenS + lenR; - sig[2] = 0x02; - sig[3] = lenR; - memcpy(sig+4, rp, lenR); - sig[4+lenR] = 0x02; - sig[5+lenR] = lenS; - memcpy(sig+lenR+6, sp, lenS); - return 1; -} - -static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) { - unsigned char c[32]; - secp256k1_scalar sn, u1, u2; -#if !defined(EXHAUSTIVE_TEST_ORDER) - secp256k1_fe xr; -#endif - secp256k1_gej pubkeyj; - secp256k1_gej pr; - - if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { - return 0; - } - - secp256k1_scalar_inverse_var(&sn, sigs); - secp256k1_scalar_mul(&u1, &sn, message); - secp256k1_scalar_mul(&u2, &sn, sigr); - secp256k1_gej_set_ge(&pubkeyj, pubkey); - secp256k1_ecmult(ctx, &pr, &pubkeyj, &u2, &u1); - if (secp256k1_gej_is_infinity(&pr)) { - return 0; - } - -#if defined(EXHAUSTIVE_TEST_ORDER) -{ - secp256k1_scalar computed_r; - secp256k1_ge pr_ge; - secp256k1_ge_set_gej(&pr_ge, &pr); - secp256k1_fe_normalize(&pr_ge.x); - - secp256k1_fe_get_b32(c, &pr_ge.x); - secp256k1_scalar_set_b32(&computed_r, c, NULL); - return secp256k1_scalar_eq(sigr, &computed_r); -} -#else - secp256k1_scalar_get_b32(c, sigr); - secp256k1_fe_set_b32(&xr, c); - - /** We now have the recomputed R point in pr, and its claimed x coordinate (modulo n) - * in xr. Naively, we would extract the x coordinate from pr (requiring a inversion modulo p), - * compute the remainder modulo n, and compare it to xr. However: - * - * xr == X(pr) mod n - * <=> exists h. (xr + h * n < p && xr + h * n == X(pr)) - * [Since 2 * n > p, h can only be 0 or 1] - * <=> (xr == X(pr)) || (xr + n < p && xr + n == X(pr)) - * [In Jacobian coordinates, X(pr) is pr.x / pr.z^2 mod p] - * <=> (xr == pr.x / pr.z^2 mod p) || (xr + n < p && xr + n == pr.x / pr.z^2 mod p) - * [Multiplying both sides of the equations by pr.z^2 mod p] - * <=> (xr * pr.z^2 mod p == pr.x) || (xr + n < p && (xr + n) * pr.z^2 mod p == pr.x) - * - * Thus, we can avoid the inversion, but we have to check both cases separately. - * secp256k1_gej_eq_x implements the (xr * pr.z^2 mod p == pr.x) test. - */ - if (secp256k1_gej_eq_x_var(&xr, &pr)) { - /* xr * pr.z^2 mod p == pr.x, so the signature is valid. */ - return 1; - } - if (secp256k1_fe_cmp_var(&xr, &secp256k1_ecdsa_const_p_minus_order) >= 0) { - /* xr + n >= p, so we can skip testing the second case. */ - return 0; - } - secp256k1_fe_add(&xr, &secp256k1_ecdsa_const_order_as_fe); - if (secp256k1_gej_eq_x_var(&xr, &pr)) { - /* (xr + n) * pr.z^2 mod p == pr.x, so the signature is valid. */ - return 1; - } - return 0; -#endif -} - -static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) { - unsigned char b[32]; - secp256k1_gej rp; - secp256k1_ge r; - secp256k1_scalar n; - int overflow = 0; - int high; - - secp256k1_ecmult_gen(ctx, &rp, nonce); - secp256k1_ge_set_gej(&r, &rp); - secp256k1_fe_normalize(&r.x); - secp256k1_fe_normalize(&r.y); - secp256k1_fe_get_b32(b, &r.x); - secp256k1_scalar_set_b32(sigr, b, &overflow); - if (recid) { - /* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log - * of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria. - */ - *recid = (overflow << 1) | secp256k1_fe_is_odd(&r.y); - } - secp256k1_scalar_mul(&n, sigr, seckey); - secp256k1_scalar_add(&n, &n, message); - secp256k1_scalar_inverse(sigs, nonce); - secp256k1_scalar_mul(sigs, sigs, &n); - secp256k1_scalar_clear(&n); - secp256k1_gej_clear(&rp); - secp256k1_ge_clear(&r); - high = secp256k1_scalar_is_high(sigs); - secp256k1_scalar_cond_negate(sigs, high); - if (recid) { - *recid ^= high; - } - /* P.x = order is on the curve, so technically sig->r could end up being zero, which would be an invalid signature. - * This is cryptographically unreachable as hitting it requires finding the discrete log of P.x = N. - */ - return !secp256k1_scalar_is_zero(sigr) & !secp256k1_scalar_is_zero(sigs); -} - -#endif /* SECP256K1_ECDSA_IMPL_H */ diff --git a/libwallet/musig/eckey.h b/libwallet/musig/eckey.h deleted file mode 100644 index 5be3a64b..00000000 --- a/libwallet/musig/eckey.h +++ /dev/null @@ -1,25 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_ECKEY_H -#define SECP256K1_ECKEY_H - -#include - -#include "group.h" -#include "scalar.h" -#include "ecmult.h" -#include "ecmult_gen.h" - -static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size); -static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed); - -static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak); -static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); -static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak); -static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); - -#endif /* SECP256K1_ECKEY_H */ diff --git a/libwallet/musig/eckey_impl.h b/libwallet/musig/eckey_impl.h deleted file mode 100644 index a39cb796..00000000 --- a/libwallet/musig/eckey_impl.h +++ /dev/null @@ -1,96 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_ECKEY_IMPL_H -#define SECP256K1_ECKEY_IMPL_H - -#include "eckey.h" - -#include "scalar.h" -#include "field.h" -#include "group.h" -#include "ecmult_gen.h" - -static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size) { - if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) { - secp256k1_fe x; - return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD); - } else if (size == 65 && (pub[0] == SECP256K1_TAG_PUBKEY_UNCOMPRESSED || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) { - secp256k1_fe x, y; - if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { - return 0; - } - secp256k1_ge_set_xy(elem, &x, &y); - if ((pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD) && - secp256k1_fe_is_odd(&y) != (pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) { - return 0; - } - return secp256k1_ge_is_valid_var(elem); - } else { - return 0; - } -} - -static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed) { - if (secp256k1_ge_is_infinity(elem)) { - return 0; - } - secp256k1_fe_normalize_var(&elem->x); - secp256k1_fe_normalize_var(&elem->y); - secp256k1_fe_get_b32(&pub[1], &elem->x); - if (compressed) { - *size = 33; - pub[0] = secp256k1_fe_is_odd(&elem->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN; - } else { - *size = 65; - pub[0] = SECP256K1_TAG_PUBKEY_UNCOMPRESSED; - secp256k1_fe_get_b32(&pub[33], &elem->y); - } - return 1; -} - -static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) { - secp256k1_scalar_add(key, key, tweak); - return !secp256k1_scalar_is_zero(key); -} - -static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { - secp256k1_gej pt; - secp256k1_scalar one; - secp256k1_gej_set_ge(&pt, key); - secp256k1_scalar_set_int(&one, 1); - secp256k1_ecmult(ctx, &pt, &pt, &one, tweak); - - if (secp256k1_gej_is_infinity(&pt)) { - return 0; - } - secp256k1_ge_set_gej(key, &pt); - return 1; -} - -static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) { - int ret; - ret = !secp256k1_scalar_is_zero(tweak); - - secp256k1_scalar_mul(key, key, tweak); - return ret; -} - -static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { - secp256k1_scalar zero; - secp256k1_gej pt; - if (secp256k1_scalar_is_zero(tweak)) { - return 0; - } - - secp256k1_scalar_set_int(&zero, 0); - secp256k1_gej_set_ge(&pt, key); - secp256k1_ecmult(ctx, &pt, &pt, tweak, &zero); - secp256k1_ge_set_gej(key, &pt); - return 1; -} - -#endif /* SECP256K1_ECKEY_IMPL_H */ diff --git a/libwallet/musig/ecmult.h b/libwallet/musig/ecmult.h deleted file mode 100644 index 84537bbf..00000000 --- a/libwallet/musig/ecmult.h +++ /dev/null @@ -1,44 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_ECMULT_H -#define SECP256K1_ECMULT_H - -#include "group.h" -#include "scalar.h" -#include "scratch.h" - -typedef struct { - /* For accelerating the computation of a*P + b*G: */ - secp256k1_ge_storage (*pre_g)[]; /* odd multiples of the generator */ - secp256k1_ge_storage (*pre_g_128)[]; /* odd multiples of 2^128*generator */ -} secp256k1_ecmult_context; - -static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx); -static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc); -static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src); -static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx); -static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx); - -/** Double multiply: R = na*A + ng*G */ -static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); - -typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data); - -/** - * Multi-multiply: R = inp_g_sc * G + sum_i ni * Ai. - * Chooses the right algorithm for a given number of points and scratch space - * size. Resets and overwrites the given scratch space. If the points do not - * fit in the scratch space the algorithm is repeatedly run with batches of - * points. If no scratch space is given then a simple algorithm is used that - * simply multiplies the points with the corresponding scalars and adds them up. - * Returns: 1 on success (including when inp_g_sc is NULL and n is 0) - * 0 if there is not enough scratch space for a single point or - * callback returns 0 - */ -static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n); - -#endif /* SECP256K1_ECMULT_H */ diff --git a/libwallet/musig/ecmult_const.h b/libwallet/musig/ecmult_const.h deleted file mode 100644 index d6f0ea22..00000000 --- a/libwallet/musig/ecmult_const.h +++ /dev/null @@ -1,20 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2015 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_ECMULT_CONST_H -#define SECP256K1_ECMULT_CONST_H - -#include "scalar.h" -#include "group.h" - -/** - * Multiply: R = q*A (in constant-time) - * Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus - * one because we internally sometimes add 2 to the number during the WNAF conversion. - */ -static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits); - -#endif /* SECP256K1_ECMULT_CONST_H */ diff --git a/libwallet/musig/ecmult_const_impl.h b/libwallet/musig/ecmult_const_impl.h deleted file mode 100644 index 0e1fb965..00000000 --- a/libwallet/musig/ecmult_const_impl.h +++ /dev/null @@ -1,254 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_ECMULT_CONST_IMPL_H -#define SECP256K1_ECMULT_CONST_IMPL_H - -#include "scalar.h" -#include "group.h" -#include "ecmult_const.h" -#include "ecmult_impl.h" - -/* This is like `ECMULT_TABLE_GET_GE` but is constant time */ -#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \ - int m = 0; \ - /* Extract the sign-bit for a constant time absolute-value. */ \ - int mask = (n) >> (sizeof(n) * CHAR_BIT - 1); \ - int abs_n = ((n) + mask) ^ mask; \ - int idx_n = abs_n >> 1; \ - secp256k1_fe neg_y; \ - VERIFY_CHECK(((n) & 1) == 1); \ - VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ - VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ - VERIFY_SETUP(secp256k1_fe_clear(&(r)->x)); \ - VERIFY_SETUP(secp256k1_fe_clear(&(r)->y)); \ - /* Unconditionally set r->x = (pre)[m].x. r->y = (pre)[m].y. because it's either the correct one \ - * or will get replaced in the later iterations, this is needed to make sure `r` is initialized. */ \ - (r)->x = (pre)[m].x; \ - (r)->y = (pre)[m].y; \ - for (m = 1; m < ECMULT_TABLE_SIZE(w); m++) { \ - /* This loop is used to avoid secret data in array indices. See - * the comment in ecmult_gen_impl.h for rationale. */ \ - secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == idx_n); \ - secp256k1_fe_cmov(&(r)->y, &(pre)[m].y, m == idx_n); \ - } \ - (r)->infinity = 0; \ - secp256k1_fe_negate(&neg_y, &(r)->y, 1); \ - secp256k1_fe_cmov(&(r)->y, &neg_y, (n) != abs_n); \ -} while(0) - - -/** Convert a number to WNAF notation. - * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. - * It has the following guarantees: - * - each wnaf[i] an odd integer between -(1 << w) and (1 << w) - * - each wnaf[i] is nonzero - * - the number of words set is always WNAF_SIZE(w) + 1 - * - * Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar - * Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.) - * CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlag Berlin Heidelberg 2003 - * - * Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335 - */ -static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w, int size) { - int global_sign; - int skew = 0; - int word = 0; - - /* 1 2 3 */ - int u_last; - int u; - - int flip; - int bit; - secp256k1_scalar s; - int not_neg_one; - - VERIFY_CHECK(w > 0); - VERIFY_CHECK(size > 0); - - /* Note that we cannot handle even numbers by negating them to be odd, as is - * done in other implementations, since if our scalars were specified to have - * width < 256 for performance reasons, their negations would have width 256 - * and we'd lose any performance benefit. Instead, we use a technique from - * Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even) - * or 2 (for odd) to the number we are encoding, returning a skew value indicating - * this, and having the caller compensate after doing the multiplication. - * - * In fact, we _do_ want to negate numbers to minimize their bit-lengths (and in - * particular, to ensure that the outputs from the endomorphism-split fit into - * 128 bits). If we negate, the parity of our number flips, inverting which of - * {1, 2} we want to add to the scalar when ensuring that it's odd. Further - * complicating things, -1 interacts badly with `secp256k1_scalar_cadd_bit` and - * we need to special-case it in this logic. */ - flip = secp256k1_scalar_is_high(scalar); - /* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */ - bit = flip ^ !secp256k1_scalar_is_even(scalar); - /* We check for negative one, since adding 2 to it will cause an overflow */ - secp256k1_scalar_negate(&s, scalar); - not_neg_one = !secp256k1_scalar_is_one(&s); - s = *scalar; - secp256k1_scalar_cadd_bit(&s, bit, not_neg_one); - /* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects - * that we added two to it and flipped it. In fact for -1 these operations are - * identical. We only flipped, but since skewing is required (in the sense that - * the skew must be 1 or 2, never zero) and flipping is not, we need to change - * our flags to claim that we only skewed. */ - global_sign = secp256k1_scalar_cond_negate(&s, flip); - global_sign *= not_neg_one * 2 - 1; - skew = 1 << bit; - - /* 4 */ - u_last = secp256k1_scalar_shr_int(&s, w); - do { - int even; - - /* 4.1 4.4 */ - u = secp256k1_scalar_shr_int(&s, w); - /* 4.2 */ - even = ((u & 1) == 0); - /* In contrast to the original algorithm, u_last is always > 0 and - * therefore we do not need to check its sign. In particular, it's easy - * to see that u_last is never < 0 because u is never < 0. Moreover, - * u_last is never = 0 because u is never even after a loop - * iteration. The same holds analogously for the initial value of - * u_last (in the first loop iteration). */ - VERIFY_CHECK(u_last > 0); - VERIFY_CHECK((u_last & 1) == 1); - u += even; - u_last -= even * (1 << w); - - /* 4.3, adapted for global sign change */ - wnaf[word++] = u_last * global_sign; - - u_last = u; - } while (word * w < size); - wnaf[word] = u * global_sign; - - VERIFY_CHECK(secp256k1_scalar_is_zero(&s)); - VERIFY_CHECK(word == WNAF_SIZE_BITS(size, w)); - return skew; -} - -static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar, int size) { - secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; - secp256k1_ge tmpa; - secp256k1_fe Z; - - int skew_1; - secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; - int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)]; - int skew_lam; - secp256k1_scalar q_1, q_lam; - int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)]; - - int i; - - /* build wnaf representation for q. */ - int rsize = size; - if (size > 128) { - rsize = 128; - /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */ - secp256k1_scalar_split_lambda(&q_1, &q_lam, scalar); - skew_1 = secp256k1_wnaf_const(wnaf_1, &q_1, WINDOW_A - 1, 128); - skew_lam = secp256k1_wnaf_const(wnaf_lam, &q_lam, WINDOW_A - 1, 128); - } else - { - skew_1 = secp256k1_wnaf_const(wnaf_1, scalar, WINDOW_A - 1, size); - skew_lam = 0; - } - - /* Calculate odd multiples of a. - * All multiples are brought to the same Z 'denominator', which is stored - * in Z. Due to secp256k1' isomorphism we can do all operations pretending - * that the Z coordinate was 1, use affine addition formulae, and correct - * the Z coordinate of the result once at the end. - */ - secp256k1_gej_set_ge(r, a); - secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, r); - for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { - secp256k1_fe_normalize_weak(&pre_a[i].y); - } - if (size > 128) { - for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { - secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); - } - - } - - /* first loop iteration (separated out so we can directly set r, rather - * than having it start at infinity, get doubled several times, then have - * its new value added to it) */ - i = wnaf_1[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)]; - VERIFY_CHECK(i != 0); - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A); - secp256k1_gej_set_ge(r, &tmpa); - if (size > 128) { - i = wnaf_lam[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)]; - VERIFY_CHECK(i != 0); - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A); - secp256k1_gej_add_ge(r, r, &tmpa); - } - /* remaining loop iterations */ - for (i = WNAF_SIZE_BITS(rsize, WINDOW_A - 1) - 1; i >= 0; i--) { - int n; - int j; - for (j = 0; j < WINDOW_A - 1; ++j) { - secp256k1_gej_double(r, r); - } - - n = wnaf_1[i]; - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); - VERIFY_CHECK(n != 0); - secp256k1_gej_add_ge(r, r, &tmpa); - if (size > 128) { - n = wnaf_lam[i]; - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); - VERIFY_CHECK(n != 0); - secp256k1_gej_add_ge(r, r, &tmpa); - } - } - - secp256k1_fe_mul(&r->z, &r->z, &Z); - - { - /* Correct for wNAF skew */ - secp256k1_ge correction = *a; - secp256k1_ge_storage correction_1_stor; - secp256k1_ge_storage correction_lam_stor; - secp256k1_ge_storage a2_stor; - secp256k1_gej tmpj; - secp256k1_gej_set_ge(&tmpj, &correction); - secp256k1_gej_double_var(&tmpj, &tmpj, NULL); - secp256k1_ge_set_gej(&correction, &tmpj); - secp256k1_ge_to_storage(&correction_1_stor, a); - if (size > 128) { - secp256k1_ge_to_storage(&correction_lam_stor, a); - } - secp256k1_ge_to_storage(&a2_stor, &correction); - - /* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */ - secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2); - if (size > 128) { - secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2); - } - - /* Apply the correction */ - secp256k1_ge_from_storage(&correction, &correction_1_stor); - secp256k1_ge_neg(&correction, &correction); - secp256k1_gej_add_ge(r, r, &correction); - - if (size > 128) { - secp256k1_ge_from_storage(&correction, &correction_lam_stor); - secp256k1_ge_neg(&correction, &correction); - secp256k1_ge_mul_lambda(&correction, &correction); - secp256k1_gej_add_ge(r, r, &correction); - } - } -} - -#endif /* SECP256K1_ECMULT_CONST_IMPL_H */ diff --git a/libwallet/musig/ecmult_gen.h b/libwallet/musig/ecmult_gen.h deleted file mode 100644 index 05cf4d52..00000000 --- a/libwallet/musig/ecmult_gen.h +++ /dev/null @@ -1,49 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_ECMULT_GEN_H -#define SECP256K1_ECMULT_GEN_H - -#include "scalar.h" -#include "group.h" - -#if ECMULT_GEN_PREC_BITS != 2 && ECMULT_GEN_PREC_BITS != 4 && ECMULT_GEN_PREC_BITS != 8 -# error "Set ECMULT_GEN_PREC_BITS to 2, 4 or 8." -#endif -#define ECMULT_GEN_PREC_B ECMULT_GEN_PREC_BITS -#define ECMULT_GEN_PREC_G (1 << ECMULT_GEN_PREC_B) -#define ECMULT_GEN_PREC_N (256 / ECMULT_GEN_PREC_B) - -typedef struct { - /* For accelerating the computation of a*G: - * To harden against timing attacks, use the following mechanism: - * * Break up the multiplicand into groups of PREC_B bits, called n_0, n_1, n_2, ..., n_(PREC_N-1). - * * Compute sum(n_i * (PREC_G)^i * G + U_i, i=0 ... PREC_N-1), where: - * * U_i = U * 2^i, for i=0 ... PREC_N-2 - * * U_i = U * (1-2^(PREC_N-1)), for i=PREC_N-1 - * where U is a point with no known corresponding scalar. Note that sum(U_i, i=0 ... PREC_N-1) = 0. - * For each i, and each of the PREC_G possible values of n_i, (n_i * (PREC_G)^i * G + U_i) is - * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0 ... PREC_N-1). - * None of the resulting prec group elements have a known scalar, and neither do any of - * the intermediate sums while computing a*G. - */ - secp256k1_ge_storage (*prec)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G]; /* prec[j][i] = (PREC_G)^j * i * G + U_i */ - secp256k1_scalar blind; - secp256k1_gej initial; -} secp256k1_ecmult_gen_context; - -static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx); -static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, void **prealloc); -static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context* src); -static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx); -static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx); - -/** Multiply with the generator: R = a*G */ -static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp256k1_gej *r, const secp256k1_scalar *a); - -static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32); - -#endif /* SECP256K1_ECMULT_GEN_H */ diff --git a/libwallet/musig/ecmult_gen_impl.h b/libwallet/musig/ecmult_gen_impl.h deleted file mode 100644 index 384a67fa..00000000 --- a/libwallet/musig/ecmult_gen_impl.h +++ /dev/null @@ -1,208 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_ECMULT_GEN_IMPL_H -#define SECP256K1_ECMULT_GEN_IMPL_H - -#include "util.h" -#include "scalar.h" -#include "group.h" -#include "ecmult_gen.h" -#include "hash_impl.h" -#ifdef USE_ECMULT_STATIC_PRECOMPUTATION -#include "ecmult_static_context.h" -#endif - -#ifndef USE_ECMULT_STATIC_PRECOMPUTATION - static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = ROUND_TO_ALIGN(sizeof(*((secp256k1_ecmult_gen_context*) NULL)->prec)); -#else - static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = 0; -#endif - -static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) { - ctx->prec = NULL; -} - -static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, void **prealloc) { -#ifndef USE_ECMULT_STATIC_PRECOMPUTATION - secp256k1_ge prec[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G]; - secp256k1_gej gj; - secp256k1_gej nums_gej; - int i, j; - size_t const prealloc_size = SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE; - void* const base = *prealloc; -#endif - - if (ctx->prec != NULL) { - return; - } -#ifndef USE_ECMULT_STATIC_PRECOMPUTATION - ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])manual_alloc(prealloc, prealloc_size, base, prealloc_size); - - /* get the generator */ - secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); - - /* Construct a group element with no known corresponding scalar (nothing up my sleeve). */ - { - static const unsigned char nums_b32[33] = "The scalar for this x is unknown"; - secp256k1_fe nums_x; - secp256k1_ge nums_ge; - int r; - r = secp256k1_fe_set_b32(&nums_x, nums_b32); - (void)r; - VERIFY_CHECK(r); - r = secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0); - (void)r; - VERIFY_CHECK(r); - secp256k1_gej_set_ge(&nums_gej, &nums_ge); - /* Add G to make the bits in x uniformly distributed. */ - secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g, NULL); - } - - /* compute prec. */ - { - secp256k1_gej precj[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G]; /* Jacobian versions of prec. */ - secp256k1_gej gbase; - secp256k1_gej numsbase; - gbase = gj; /* PREC_G^j * G */ - numsbase = nums_gej; /* 2^j * nums. */ - for (j = 0; j < ECMULT_GEN_PREC_N; j++) { - /* Set precj[j*PREC_G .. j*PREC_G+(PREC_G-1)] to (numsbase, numsbase + gbase, ..., numsbase + (PREC_G-1)*gbase). */ - precj[j*ECMULT_GEN_PREC_G] = numsbase; - for (i = 1; i < ECMULT_GEN_PREC_G; i++) { - secp256k1_gej_add_var(&precj[j*ECMULT_GEN_PREC_G + i], &precj[j*ECMULT_GEN_PREC_G + i - 1], &gbase, NULL); - } - /* Multiply gbase by PREC_G. */ - for (i = 0; i < ECMULT_GEN_PREC_B; i++) { - secp256k1_gej_double_var(&gbase, &gbase, NULL); - } - /* Multiply numbase by 2. */ - secp256k1_gej_double_var(&numsbase, &numsbase, NULL); - if (j == ECMULT_GEN_PREC_N - 2) { - /* In the last iteration, numsbase is (1 - 2^j) * nums instead. */ - secp256k1_gej_neg(&numsbase, &numsbase); - secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); - } - } - secp256k1_ge_set_all_gej_var(prec, precj, ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G); - } - for (j = 0; j < ECMULT_GEN_PREC_N; j++) { - for (i = 0; i < ECMULT_GEN_PREC_G; i++) { - secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*ECMULT_GEN_PREC_G + i]); - } - } -#else - (void)prealloc; - ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])secp256k1_ecmult_static_context; -#endif - secp256k1_ecmult_gen_blind(ctx, NULL); -} - -static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx) { - return ctx->prec != NULL; -} - -static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context *src) { -#ifndef USE_ECMULT_STATIC_PRECOMPUTATION - if (src->prec != NULL) { - /* We cast to void* first to suppress a -Wcast-align warning. */ - dst->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])(void*)((unsigned char*)dst + ((unsigned char*)src->prec - (unsigned char*)src)); - } -#else - (void)dst, (void)src; -#endif -} - -static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) { - secp256k1_scalar_clear(&ctx->blind); - secp256k1_gej_clear(&ctx->initial); - ctx->prec = NULL; -} - -static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp256k1_gej *r, const secp256k1_scalar *gn) { - secp256k1_ge add; - secp256k1_ge_storage adds; - secp256k1_scalar gnb; - int bits; - int i, j; - memset(&adds, 0, sizeof(adds)); - *r = ctx->initial; - /* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */ - secp256k1_scalar_add(&gnb, gn, &ctx->blind); - add.infinity = 0; - for (j = 0; j < ECMULT_GEN_PREC_N; j++) { - bits = secp256k1_scalar_get_bits(&gnb, j * ECMULT_GEN_PREC_B, ECMULT_GEN_PREC_B); - for (i = 0; i < ECMULT_GEN_PREC_G; i++) { - /** This uses a conditional move to avoid any secret data in array indexes. - * _Any_ use of secret indexes has been demonstrated to result in timing - * sidechannels, even when the cache-line access patterns are uniform. - * See also: - * "A word of warning", CHES 2013 Rump Session, by Daniel J. Bernstein and Peter Schwabe - * (https://cryptojedi.org/peter/data/chesrump-20130822.pdf) and - * "Cache Attacks and Countermeasures: the Case of AES", RSA 2006, - * by Dag Arne Osvik, Adi Shamir, and Eran Tromer - * (https://www.tau.ac.il/~tromer/papers/cache.pdf) - */ - secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits); - } - secp256k1_ge_from_storage(&add, &adds); - secp256k1_gej_add_ge(r, r, &add); - } - bits = 0; - secp256k1_ge_clear(&add); - secp256k1_scalar_clear(&gnb); -} - -/* Setup blinding values for secp256k1_ecmult_gen. */ -static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32) { - secp256k1_scalar b; - secp256k1_gej gb; - secp256k1_fe s; - unsigned char nonce32[32]; - secp256k1_rfc6979_hmac_sha256 rng; - int overflow; - unsigned char keydata[64] = {0}; - if (seed32 == NULL) { - /* When seed is NULL, reset the initial point and blinding value. */ - secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g); - secp256k1_gej_neg(&ctx->initial, &ctx->initial); - secp256k1_scalar_set_int(&ctx->blind, 1); - } - /* The prior blinding value (if not reset) is chained forward by including it in the hash. */ - secp256k1_scalar_get_b32(nonce32, &ctx->blind); - /** Using a CSPRNG allows a failure free interface, avoids needing large amounts of random data, - * and guards against weak or adversarial seeds. This is a simpler and safer interface than - * asking the caller for blinding values directly and expecting them to retry on failure. - */ - memcpy(keydata, nonce32, 32); - if (seed32 != NULL) { - memcpy(keydata + 32, seed32, 32); - } - secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32); - memset(keydata, 0, sizeof(keydata)); - /* Accept unobservably small non-uniformity. */ - secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); - overflow = !secp256k1_fe_set_b32(&s, nonce32); - overflow |= secp256k1_fe_is_zero(&s); - secp256k1_fe_cmov(&s, &secp256k1_fe_one, overflow); - /* Randomize the projection to defend against multiplier sidechannels. */ - secp256k1_gej_rescale(&ctx->initial, &s); - secp256k1_fe_clear(&s); - secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); - secp256k1_scalar_set_b32(&b, nonce32, NULL); - /* A blinding value of 0 works, but would undermine the projection hardening. */ - secp256k1_scalar_cmov(&b, &secp256k1_scalar_one, secp256k1_scalar_is_zero(&b)); - secp256k1_rfc6979_hmac_sha256_finalize(&rng); - memset(nonce32, 0, 32); - secp256k1_ecmult_gen(ctx, &gb, &b); - secp256k1_scalar_negate(&b, &b); - ctx->blind = b; - ctx->initial = gb; - secp256k1_scalar_clear(&b); - secp256k1_gej_clear(&gb); -} - -#endif /* SECP256K1_ECMULT_GEN_IMPL_H */ diff --git a/libwallet/musig/ecmult_impl.h b/libwallet/musig/ecmult_impl.h deleted file mode 100644 index 5c2edac6..00000000 --- a/libwallet/musig/ecmult_impl.h +++ /dev/null @@ -1,1083 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php. * - ******************************************************************************/ - -#ifndef SECP256K1_ECMULT_IMPL_H -#define SECP256K1_ECMULT_IMPL_H - -#include -#include - -#include "util.h" -#include "group.h" -#include "scalar.h" -#include "ecmult.h" - -#if defined(EXHAUSTIVE_TEST_ORDER) -/* We need to lower these values for exhaustive tests because - * the tables cannot have infinities in them (this breaks the - * affine-isomorphism stuff which tracks z-ratios) */ -# if EXHAUSTIVE_TEST_ORDER > 128 -# define WINDOW_A 5 -# define WINDOW_G 8 -# elif EXHAUSTIVE_TEST_ORDER > 8 -# define WINDOW_A 4 -# define WINDOW_G 4 -# else -# define WINDOW_A 2 -# define WINDOW_G 2 -# endif -#else -/* optimal for 128-bit and 256-bit exponents. */ -# define WINDOW_A 5 -/** Larger values for ECMULT_WINDOW_SIZE result in possibly better - * performance at the cost of an exponentially larger precomputed - * table. The exact table size is - * (1 << (WINDOW_G - 2)) * sizeof(secp256k1_ge_storage) bytes, - * where sizeof(secp256k1_ge_storage) is typically 64 bytes but can - * be larger due to platform-specific padding and alignment. - * Two tables of this size are used (due to the endomorphism - * optimization). - */ -# define WINDOW_G ECMULT_WINDOW_SIZE -#endif - -/* Noone will ever need more than a window size of 24. The code might - * be correct for larger values of ECMULT_WINDOW_SIZE but this is not - * not tested. - * - * The following limitations are known, and there are probably more: - * If WINDOW_G > 27 and size_t has 32 bits, then the code is incorrect - * because the size of the memory object that we allocate (in bytes) - * will not fit in a size_t. - * If WINDOW_G > 31 and int has 32 bits, then the code is incorrect - * because certain expressions will overflow. - */ -#if ECMULT_WINDOW_SIZE < 2 || ECMULT_WINDOW_SIZE > 24 -# error Set ECMULT_WINDOW_SIZE to an integer in range [2..24]. -#endif - -#define WNAF_BITS 128 -#define WNAF_SIZE_BITS(bits, w) (((bits) + (w) - 1) / (w)) -#define WNAF_SIZE(w) WNAF_SIZE_BITS(WNAF_BITS, w) - -/** The number of entries a table with precomputed multiples needs to have. */ -#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2)) - -/* The number of objects allocated on the scratch space for ecmult_multi algorithms */ -#define PIPPENGER_SCRATCH_OBJECTS 6 -#define STRAUSS_SCRATCH_OBJECTS 6 - -#define PIPPENGER_MAX_BUCKET_WINDOW 12 - -/* Minimum number of points for which pippenger_wnaf is faster than strauss wnaf */ -#define ECMULT_PIPPENGER_THRESHOLD 88 - -#define ECMULT_MAX_POINTS_PER_BATCH 5000000 - -/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain - * the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will - * contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z. - * Prej's Z values are undefined, except for the last value. - */ -static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, secp256k1_fe *zr, const secp256k1_gej *a) { - secp256k1_gej d; - secp256k1_ge a_ge, d_ge; - int i; - - VERIFY_CHECK(!a->infinity); - - secp256k1_gej_double_var(&d, a, NULL); - - /* - * Perform the additions on an isomorphism where 'd' is affine: drop the z coordinate - * of 'd', and scale the 1P starting value's x/y coordinates without changing its z. - */ - d_ge.x = d.x; - d_ge.y = d.y; - d_ge.infinity = 0; - - secp256k1_ge_set_gej_zinv(&a_ge, a, &d.z); - prej[0].x = a_ge.x; - prej[0].y = a_ge.y; - prej[0].z = a->z; - prej[0].infinity = 0; - - zr[0] = d.z; - for (i = 1; i < n; i++) { - secp256k1_gej_add_ge_var(&prej[i], &prej[i-1], &d_ge, &zr[i]); - } - - /* - * Each point in 'prej' has a z coordinate too small by a factor of 'd.z'. Only - * the final point's z coordinate is actually used though, so just update that. - */ - secp256k1_fe_mul(&prej[n-1].z, &prej[n-1].z, &d.z); -} - -/** Fill a table 'pre' with precomputed odd multiples of a. - * - * There are two versions of this function: - * - secp256k1_ecmult_odd_multiples_table_globalz_windowa which brings its - * resulting point set to a single constant Z denominator, stores the X and Y - * coordinates as ge_storage points in pre, and stores the global Z in rz. - * It only operates on tables sized for WINDOW_A wnaf multiples. - * - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its - * resulting point set to actually affine points, and stores those in pre. - * It operates on tables of any size. - * - * To compute a*P + b*G, we compute a table for P using the first function, - * and for G using the second (which requires an inverse, but it only needs to - * happen once). - */ -static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *pre, secp256k1_fe *globalz, const secp256k1_gej *a) { - secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)]; - secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; - - /* Compute the odd multiples in Jacobian form. */ - secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a); - /* Bring them to the same Z denominator. */ - secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr); -} - -static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp256k1_ge_storage *pre, const secp256k1_gej *a) { - secp256k1_gej d; - secp256k1_ge d_ge, p_ge; - secp256k1_gej pj; - secp256k1_fe zi; - secp256k1_fe zr; - secp256k1_fe dx_over_dz_squared; - int i; - - VERIFY_CHECK(!a->infinity); - - secp256k1_gej_double_var(&d, a, NULL); - - /* First, we perform all the additions in an isomorphic curve obtained by multiplying - * all `z` coordinates by 1/`d.z`. In these coordinates `d` is affine so we can use - * `secp256k1_gej_add_ge_var` to perform the additions. For each addition, we store - * the resulting y-coordinate and the z-ratio, since we only have enough memory to - * store two field elements. These are sufficient to efficiently undo the isomorphism - * and recompute all the `x`s. - */ - d_ge.x = d.x; - d_ge.y = d.y; - d_ge.infinity = 0; - - secp256k1_ge_set_gej_zinv(&p_ge, a, &d.z); - pj.x = p_ge.x; - pj.y = p_ge.y; - pj.z = a->z; - pj.infinity = 0; - - for (i = 0; i < (n - 1); i++) { - secp256k1_fe_normalize_var(&pj.y); - secp256k1_fe_to_storage(&pre[i].y, &pj.y); - secp256k1_gej_add_ge_var(&pj, &pj, &d_ge, &zr); - secp256k1_fe_normalize_var(&zr); - secp256k1_fe_to_storage(&pre[i].x, &zr); - } - - /* Invert d.z in the same batch, preserving pj.z so we can extract 1/d.z */ - secp256k1_fe_mul(&zi, &pj.z, &d.z); - secp256k1_fe_inv_var(&zi, &zi); - - /* Directly set `pre[n - 1]` to `pj`, saving the inverted z-coordinate so - * that we can combine it with the saved z-ratios to compute the other zs - * without any more inversions. */ - secp256k1_ge_set_gej_zinv(&p_ge, &pj, &zi); - secp256k1_ge_to_storage(&pre[n - 1], &p_ge); - - /* Compute the actual x-coordinate of D, which will be needed below. */ - secp256k1_fe_mul(&d.z, &zi, &pj.z); /* d.z = 1/d.z */ - secp256k1_fe_sqr(&dx_over_dz_squared, &d.z); - secp256k1_fe_mul(&dx_over_dz_squared, &dx_over_dz_squared, &d.x); - - /* Going into the second loop, we have set `pre[n-1]` to its final affine - * form, but still need to set `pre[i]` for `i` in 0 through `n-2`. We - * have `zi = (p.z * d.z)^-1`, where - * - * `p.z` is the z-coordinate of the point on the isomorphic curve - * which was ultimately assigned to `pre[n-1]`. - * `d.z` is the multiplier that must be applied to all z-coordinates - * to move from our isomorphic curve back to secp256k1; so the - * product `p.z * d.z` is the z-coordinate of the secp256k1 - * point assigned to `pre[n-1]`. - * - * All subsequent inverse-z-coordinates can be obtained by multiplying this - * factor by successive z-ratios, which is much more efficient than directly - * computing each one. - * - * Importantly, these inverse-zs will be coordinates of points on secp256k1, - * while our other stored values come from computations on the isomorphic - * curve. So in the below loop, we will take care not to actually use `zi` - * or any derived values until we're back on secp256k1. - */ - i = n - 1; - while (i > 0) { - secp256k1_fe zi2, zi3; - const secp256k1_fe *rzr; - i--; - - secp256k1_ge_from_storage(&p_ge, &pre[i]); - - /* For each remaining point, we extract the z-ratio from the stored - * x-coordinate, compute its z^-1 from that, and compute the full - * point from that. */ - rzr = &p_ge.x; - secp256k1_fe_mul(&zi, &zi, rzr); - secp256k1_fe_sqr(&zi2, &zi); - secp256k1_fe_mul(&zi3, &zi2, &zi); - /* To compute the actual x-coordinate, we use the stored z ratio and - * y-coordinate, which we obtained from `secp256k1_gej_add_ge_var` - * in the loop above, as well as the inverse of the square of its - * z-coordinate. We store the latter in the `zi2` variable, which is - * computed iteratively starting from the overall Z inverse then - * multiplying by each z-ratio in turn. - * - * Denoting the z-ratio as `rzr`, we observe that it is equal to `h` - * from the inside of the above `gej_add_ge_var` call. This satisfies - * - * rzr = d_x * z^2 - x * d_z^2 - * - * where (`d_x`, `d_z`) are Jacobian coordinates of `D` and `(x, z)` - * are Jacobian coordinates of our desired point -- except both are on - * the isomorphic curve that we were using when we called `gej_add_ge_var`. - * To get back to secp256k1, we must multiply both `z`s by `d_z`, or - * equivalently divide both `x`s by `d_z^2`. Our equation then becomes - * - * rzr = d_x * z^2 / d_z^2 - x - * - * (The left-hand-side, being a ratio of z-coordinates, is unaffected - * by the isomorphism.) - * - * Rearranging to solve for `x`, we have - * - * x = d_x * z^2 / d_z^2 - rzr - * - * But what we actually want is the affine coordinate `X = x/z^2`, - * which will satisfy - * - * X = d_x / d_z^2 - rzr / z^2 - * = dx_over_dz_squared - rzr * zi2 - */ - secp256k1_fe_mul(&p_ge.x, rzr, &zi2); - secp256k1_fe_negate(&p_ge.x, &p_ge.x, 1); - secp256k1_fe_add(&p_ge.x, &dx_over_dz_squared); - /* y is stored_y/z^3, as we expect */ - secp256k1_fe_mul(&p_ge.y, &p_ge.y, &zi3); - /* Store */ - secp256k1_ge_to_storage(&pre[i], &p_ge); - } -} - -/** The following two macro retrieves a particular odd multiple from a table - * of precomputed multiples. */ -#define ECMULT_TABLE_GET_GE(r,pre,n,w) do { \ - VERIFY_CHECK(((n) & 1) == 1); \ - VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ - VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ - if ((n) > 0) { \ - *(r) = (pre)[((n)-1)/2]; \ - } else { \ - *(r) = (pre)[(-(n)-1)/2]; \ - secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \ - } \ -} while(0) - -#define ECMULT_TABLE_GET_GE_STORAGE(r,pre,n,w) do { \ - VERIFY_CHECK(((n) & 1) == 1); \ - VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ - VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ - if ((n) > 0) { \ - secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \ - } else { \ - secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \ - secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \ - } \ -} while(0) - -static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE = - ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)) - + ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)) - ; - -static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) { - ctx->pre_g = NULL; - ctx->pre_g_128 = NULL; -} - -static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc) { - secp256k1_gej gj; - void* const base = *prealloc; - size_t const prealloc_size = SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE; - - if (ctx->pre_g != NULL) { - return; - } - - /* get the generator */ - secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); - - { - size_t size = sizeof((*ctx->pre_g)[0]) * ((size_t)ECMULT_TABLE_SIZE(WINDOW_G)); - /* check for overflow */ - VERIFY_CHECK(size / sizeof((*ctx->pre_g)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G))); - ctx->pre_g = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size); - } - - /* precompute the tables with odd multiples */ - secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj); - - { - secp256k1_gej g_128j; - int i; - - size_t size = sizeof((*ctx->pre_g_128)[0]) * ((size_t) ECMULT_TABLE_SIZE(WINDOW_G)); - /* check for overflow */ - VERIFY_CHECK(size / sizeof((*ctx->pre_g_128)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G))); - ctx->pre_g_128 = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size); - - /* calculate 2^128*generator */ - g_128j = gj; - for (i = 0; i < 128; i++) { - secp256k1_gej_double_var(&g_128j, &g_128j, NULL); - } - secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j); - } -} - -static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src) { - if (src->pre_g != NULL) { - /* We cast to void* first to suppress a -Wcast-align warning. */ - dst->pre_g = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g) - (unsigned char*)src)); - } - if (src->pre_g_128 != NULL) { - dst->pre_g_128 = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g_128) - (unsigned char*)src)); - } -} - -static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx) { - return ctx->pre_g != NULL; -} - -static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) { - secp256k1_ecmult_context_init(ctx); -} - -/** Convert a number to WNAF notation. The number becomes represented by sum(2^i * wnaf[i], i=0..bits), - * with the following guarantees: - * - each wnaf[i] is either 0, or an odd integer between -(1<<(w-1) - 1) and (1<<(w-1) - 1) - * - two non-zero entries in wnaf are separated by at least w-1 zeroes. - * - the number of set values in wnaf is returned. This number is at most 256, and at most one more - * than the number of bits in the (absolute value) of the input. - */ -static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) { - secp256k1_scalar s; - int last_set_bit = -1; - int bit = 0; - int sign = 1; - int carry = 0; - - VERIFY_CHECK(wnaf != NULL); - VERIFY_CHECK(0 <= len && len <= 256); - VERIFY_CHECK(a != NULL); - VERIFY_CHECK(2 <= w && w <= 31); - - memset(wnaf, 0, len * sizeof(wnaf[0])); - - s = *a; - if (secp256k1_scalar_get_bits(&s, 255, 1)) { - secp256k1_scalar_negate(&s, &s); - sign = -1; - } - - while (bit < len) { - int now; - int word; - if (secp256k1_scalar_get_bits(&s, bit, 1) == (unsigned int)carry) { - bit++; - continue; - } - - now = w; - if (now > len - bit) { - now = len - bit; - } - - word = secp256k1_scalar_get_bits_var(&s, bit, now) + carry; - - carry = (word >> (w-1)) & 1; - word -= carry << w; - - wnaf[bit] = sign * word; - last_set_bit = bit; - - bit += now; - } -#ifdef VERIFY - CHECK(carry == 0); - while (bit < 256) { - CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0); - } -#endif - return last_set_bit + 1; -} - -struct secp256k1_strauss_point_state { - secp256k1_scalar na_1, na_lam; - int wnaf_na_1[129]; - int wnaf_na_lam[129]; - int bits_na_1; - int bits_na_lam; - size_t input_pos; -}; - -struct secp256k1_strauss_state { - secp256k1_gej* prej; - secp256k1_fe* zr; - secp256k1_ge* pre_a; - secp256k1_ge* pre_a_lam; - struct secp256k1_strauss_point_state* ps; -}; - -static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, const struct secp256k1_strauss_state *state, secp256k1_gej *r, size_t num, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { - secp256k1_ge tmpa; - secp256k1_fe Z; - /* Splitted G factors. */ - secp256k1_scalar ng_1, ng_128; - int wnaf_ng_1[129]; - int bits_ng_1 = 0; - int wnaf_ng_128[129]; - int bits_ng_128 = 0; - int i; - int bits = 0; - size_t np; - size_t no = 0; - - for (np = 0; np < num; ++np) { - if (secp256k1_scalar_is_zero(&na[np]) || secp256k1_gej_is_infinity(&a[np])) { - continue; - } - state->ps[no].input_pos = np; - /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ - secp256k1_scalar_split_lambda(&state->ps[no].na_1, &state->ps[no].na_lam, &na[np]); - - /* build wnaf representation for na_1 and na_lam. */ - state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 129, &state->ps[no].na_1, WINDOW_A); - state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 129, &state->ps[no].na_lam, WINDOW_A); - VERIFY_CHECK(state->ps[no].bits_na_1 <= 129); - VERIFY_CHECK(state->ps[no].bits_na_lam <= 129); - if (state->ps[no].bits_na_1 > bits) { - bits = state->ps[no].bits_na_1; - } - if (state->ps[no].bits_na_lam > bits) { - bits = state->ps[no].bits_na_lam; - } - ++no; - } - - /* Calculate odd multiples of a. - * All multiples are brought to the same Z 'denominator', which is stored - * in Z. Due to secp256k1' isomorphism we can do all operations pretending - * that the Z coordinate was 1, use affine addition formulae, and correct - * the Z coordinate of the result once at the end. - * The exception is the precomputed G table points, which are actually - * affine. Compared to the base used for other points, they have a Z ratio - * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same - * isomorphism to efficiently add with a known Z inverse. - */ - if (no > 0) { - /* Compute the odd multiples in Jacobian form. */ - secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej, state->zr, &a[state->ps[0].input_pos]); - for (np = 1; np < no; ++np) { - secp256k1_gej tmp = a[state->ps[np].input_pos]; -#ifdef VERIFY - secp256k1_fe_normalize_var(&(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z)); -#endif - secp256k1_gej_rescale(&tmp, &(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z)); - secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &tmp); - secp256k1_fe_mul(state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &(a[state->ps[np].input_pos].z)); - } - /* Bring them to the same Z denominator. */ - secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, &Z, state->prej, state->zr); - } else { - secp256k1_fe_set_int(&Z, 1); - } - - for (np = 0; np < no; ++np) { - for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { - secp256k1_ge_mul_lambda(&state->pre_a_lam[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i]); - } - } - - if (ng) { - /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */ - secp256k1_scalar_split_128(&ng_1, &ng_128, ng); - - /* Build wnaf representation for ng_1 and ng_128 */ - bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G); - bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G); - if (bits_ng_1 > bits) { - bits = bits_ng_1; - } - if (bits_ng_128 > bits) { - bits = bits_ng_128; - } - } - - secp256k1_gej_set_infinity(r); - - for (i = bits - 1; i >= 0; i--) { - int n; - secp256k1_gej_double_var(r, r, NULL); - for (np = 0; np < no; ++np) { - if (i < state->ps[np].bits_na_1 && (n = state->ps[np].wnaf_na_1[i])) { - ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); - secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); - } - if (i < state->ps[np].bits_na_lam && (n = state->ps[np].wnaf_na_lam[i])) { - ECMULT_TABLE_GET_GE(&tmpa, state->pre_a_lam + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); - secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); - } - } - if (i < bits_ng_1 && (n = wnaf_ng_1[i])) { - ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); - secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); - } - if (i < bits_ng_128 && (n = wnaf_ng_128[i])) { - ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g_128, n, WINDOW_G); - secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); - } - } - - if (!r->infinity) { - secp256k1_fe_mul(&r->z, &r->z, &Z); - } -} - -static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { - secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)]; - secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; - secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; - struct secp256k1_strauss_point_state ps[1]; - secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; - struct secp256k1_strauss_state state; - - state.prej = prej; - state.zr = zr; - state.pre_a = pre_a; - state.pre_a_lam = pre_a_lam; - state.ps = ps; - secp256k1_ecmult_strauss_wnaf(ctx, &state, r, 1, a, na, ng); -} - -static size_t secp256k1_strauss_scratch_size(size_t n_points) { - static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar); - return n_points*point_size; -} - -static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { - secp256k1_gej* points; - secp256k1_scalar* scalars; - struct secp256k1_strauss_state state; - size_t i; - const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch); - - secp256k1_gej_set_infinity(r); - if (inp_g_sc == NULL && n_points == 0) { - return 1; - } - - points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej)); - scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar)); - state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej)); - state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); - state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); - state.pre_a_lam = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); - state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); - - if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL || state.pre_a_lam == NULL || state.ps == NULL) { - secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); - return 0; - } - - for (i = 0; i < n_points; i++) { - secp256k1_ge point; - if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) { - secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); - return 0; - } - secp256k1_gej_set_ge(&points[i], &point); - } - secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc); - secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); - return 1; -} - -/* Wrapper for secp256k1_ecmult_multi_func interface */ -static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { - return secp256k1_ecmult_strauss_batch(error_callback, actx, scratch, r, inp_g_sc, cb, cbdata, n, 0); -} - -static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) { - return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1); -} - -/** Convert a number to WNAF notation. - * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. - * It has the following guarantees: - * - each wnaf[i] is either 0 or an odd integer between -(1 << w) and (1 << w) - * - the number of words set is always WNAF_SIZE(w) - * - the returned skew is 0 or 1 - */ -static int secp256k1_wnaf_fixed(int *wnaf, const secp256k1_scalar *s, int w) { - int skew = 0; - int pos; - int max_pos; - int last_w; - const secp256k1_scalar *work = s; - - if (secp256k1_scalar_is_zero(s)) { - for (pos = 0; pos < WNAF_SIZE(w); pos++) { - wnaf[pos] = 0; - } - return 0; - } - - if (secp256k1_scalar_is_even(s)) { - skew = 1; - } - - wnaf[0] = secp256k1_scalar_get_bits_var(work, 0, w) + skew; - /* Compute last window size. Relevant when window size doesn't divide the - * number of bits in the scalar */ - last_w = WNAF_BITS - (WNAF_SIZE(w) - 1) * w; - - /* Store the position of the first nonzero word in max_pos to allow - * skipping leading zeros when calculating the wnaf. */ - for (pos = WNAF_SIZE(w) - 1; pos > 0; pos--) { - int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w); - if(val != 0) { - break; - } - wnaf[pos] = 0; - } - max_pos = pos; - pos = 1; - - while (pos <= max_pos) { - int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w); - if ((val & 1) == 0) { - wnaf[pos - 1] -= (1 << w); - wnaf[pos] = (val + 1); - } else { - wnaf[pos] = val; - } - /* Set a coefficient to zero if it is 1 or -1 and the proceeding digit - * is strictly negative or strictly positive respectively. Only change - * coefficients at previous positions because above code assumes that - * wnaf[pos - 1] is odd. - */ - if (pos >= 2 && ((wnaf[pos - 1] == 1 && wnaf[pos - 2] < 0) || (wnaf[pos - 1] == -1 && wnaf[pos - 2] > 0))) { - if (wnaf[pos - 1] == 1) { - wnaf[pos - 2] += 1 << w; - } else { - wnaf[pos - 2] -= 1 << w; - } - wnaf[pos - 1] = 0; - } - ++pos; - } - - return skew; -} - -struct secp256k1_pippenger_point_state { - int skew_na; - size_t input_pos; -}; - -struct secp256k1_pippenger_state { - int *wnaf_na; - struct secp256k1_pippenger_point_state* ps; -}; - -/* - * pippenger_wnaf computes the result of a multi-point multiplication as - * follows: The scalars are brought into wnaf with n_wnaf elements each. Then - * for every i < n_wnaf, first each point is added to a "bucket" corresponding - * to the point's wnaf[i]. Second, the buckets are added together such that - * r += 1*bucket[0] + 3*bucket[1] + 5*bucket[2] + ... - */ -static int secp256k1_ecmult_pippenger_wnaf(secp256k1_gej *buckets, int bucket_window, struct secp256k1_pippenger_state *state, secp256k1_gej *r, const secp256k1_scalar *sc, const secp256k1_ge *pt, size_t num) { - size_t n_wnaf = WNAF_SIZE(bucket_window+1); - size_t np; - size_t no = 0; - int i; - int j; - - for (np = 0; np < num; ++np) { - if (secp256k1_scalar_is_zero(&sc[np]) || secp256k1_ge_is_infinity(&pt[np])) { - continue; - } - state->ps[no].input_pos = np; - state->ps[no].skew_na = secp256k1_wnaf_fixed(&state->wnaf_na[no*n_wnaf], &sc[np], bucket_window+1); - no++; - } - secp256k1_gej_set_infinity(r); - - if (no == 0) { - return 1; - } - - for (i = n_wnaf - 1; i >= 0; i--) { - secp256k1_gej running_sum; - - for(j = 0; j < ECMULT_TABLE_SIZE(bucket_window+2); j++) { - secp256k1_gej_set_infinity(&buckets[j]); - } - - for (np = 0; np < no; ++np) { - int n = state->wnaf_na[np*n_wnaf + i]; - struct secp256k1_pippenger_point_state point_state = state->ps[np]; - secp256k1_ge tmp; - int idx; - - if (i == 0) { - /* correct for wnaf skew */ - int skew = point_state.skew_na; - if (skew) { - secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]); - secp256k1_gej_add_ge_var(&buckets[0], &buckets[0], &tmp, NULL); - } - } - if (n > 0) { - idx = (n - 1)/2; - secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &pt[point_state.input_pos], NULL); - } else if (n < 0) { - idx = -(n + 1)/2; - secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]); - secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &tmp, NULL); - } - } - - for(j = 0; j < bucket_window; j++) { - secp256k1_gej_double_var(r, r, NULL); - } - - secp256k1_gej_set_infinity(&running_sum); - /* Accumulate the sum: bucket[0] + 3*bucket[1] + 5*bucket[2] + 7*bucket[3] + ... - * = bucket[0] + bucket[1] + bucket[2] + bucket[3] + ... - * + 2 * (bucket[1] + 2*bucket[2] + 3*bucket[3] + ...) - * using an intermediate running sum: - * running_sum = bucket[0] + bucket[1] + bucket[2] + ... - * - * The doubling is done implicitly by deferring the final window doubling (of 'r'). - */ - for(j = ECMULT_TABLE_SIZE(bucket_window+2) - 1; j > 0; j--) { - secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[j], NULL); - secp256k1_gej_add_var(r, r, &running_sum, NULL); - } - - secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[0], NULL); - secp256k1_gej_double_var(r, r, NULL); - secp256k1_gej_add_var(r, r, &running_sum, NULL); - } - return 1; -} - -/** - * Returns optimal bucket_window (number of bits of a scalar represented by a - * set of buckets) for a given number of points. - */ -static int secp256k1_pippenger_bucket_window(size_t n) { - if (n <= 1) { - return 1; - } else if (n <= 4) { - return 2; - } else if (n <= 20) { - return 3; - } else if (n <= 57) { - return 4; - } else if (n <= 136) { - return 5; - } else if (n <= 235) { - return 6; - } else if (n <= 1260) { - return 7; - } else if (n <= 4420) { - return 9; - } else if (n <= 7880) { - return 10; - } else if (n <= 16050) { - return 11; - } else { - return PIPPENGER_MAX_BUCKET_WINDOW; - } -} - -/** - * Returns the maximum optimal number of points for a bucket_window. - */ -static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) { - switch(bucket_window) { - case 1: return 1; - case 2: return 4; - case 3: return 20; - case 4: return 57; - case 5: return 136; - case 6: return 235; - case 7: return 1260; - case 8: return 1260; - case 9: return 4420; - case 10: return 7880; - case 11: return 16050; - case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX; - } - return 0; -} - - -SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, secp256k1_scalar *s2, secp256k1_ge *p1, secp256k1_ge *p2) { - secp256k1_scalar tmp = *s1; - secp256k1_scalar_split_lambda(s1, s2, &tmp); - secp256k1_ge_mul_lambda(p2, p1); - - if (secp256k1_scalar_is_high(s1)) { - secp256k1_scalar_negate(s1, s1); - secp256k1_ge_neg(p1, p1); - } - if (secp256k1_scalar_is_high(s2)) { - secp256k1_scalar_negate(s2, s2); - secp256k1_ge_neg(p2, p2); - } -} - -/** - * Returns the scratch size required for a given number of points (excluding - * base point G) without considering alignment. - */ -static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_window) { - size_t entries = 2*n_points + 2; - size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int); - return (sizeof(secp256k1_gej) << bucket_window) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size; -} - -static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { - const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch); - /* Use 2(n+1) with the endomorphism, when calculating batch - * sizes. The reason for +1 is that we add the G scalar to the list of - * other scalars. */ - size_t entries = 2*n_points + 2; - secp256k1_ge *points; - secp256k1_scalar *scalars; - secp256k1_gej *buckets; - struct secp256k1_pippenger_state *state_space; - size_t idx = 0; - size_t point_idx = 0; - int i, j; - int bucket_window; - - (void)ctx; - secp256k1_gej_set_infinity(r); - if (inp_g_sc == NULL && n_points == 0) { - return 1; - } - - bucket_window = secp256k1_pippenger_bucket_window(n_points); - points = (secp256k1_ge *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*points)); - scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*scalars)); - state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(error_callback, scratch, sizeof(*state_space)); - if (points == NULL || scalars == NULL || state_space == NULL) { - secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); - return 0; - } - - state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*state_space->ps)); - state_space->wnaf_na = (int *) secp256k1_scratch_alloc(error_callback, scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int)); - buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, (1<ps == NULL || state_space->wnaf_na == NULL || buckets == NULL) { - secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); - return 0; - } - - if (inp_g_sc != NULL) { - scalars[0] = *inp_g_sc; - points[0] = secp256k1_ge_const_g; - idx++; - secp256k1_ecmult_endo_split(&scalars[0], &scalars[1], &points[0], &points[1]); - idx++; - } - - while (point_idx < n_points) { - if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) { - secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); - return 0; - } - idx++; - secp256k1_ecmult_endo_split(&scalars[idx - 1], &scalars[idx], &points[idx - 1], &points[idx]); - idx++; - point_idx++; - } - - secp256k1_ecmult_pippenger_wnaf(buckets, bucket_window, state_space, r, scalars, points, idx); - - /* Clear data */ - for(i = 0; (size_t)i < idx; i++) { - secp256k1_scalar_clear(&scalars[i]); - state_space->ps[i].skew_na = 0; - for(j = 0; j < WNAF_SIZE(bucket_window+1); j++) { - state_space->wnaf_na[i * WNAF_SIZE(bucket_window+1) + j] = 0; - } - } - for(i = 0; i < 1< max_alloc) { - break; - } - space_for_points = max_alloc - space_overhead; - - n_points = space_for_points/entry_size; - n_points = n_points > max_points ? max_points : n_points; - if (n_points > res) { - res = n_points; - } - if (n_points < max_points) { - /* A larger bucket_window may support even more points. But if we - * would choose that then the caller couldn't safely use any number - * smaller than what this function returns */ - break; - } - } - return res; -} - -/* Computes ecmult_multi by simply multiplying and adding each point. Does not - * require a scratch space */ -static int secp256k1_ecmult_multi_simple_var(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points) { - size_t point_idx; - secp256k1_scalar szero; - secp256k1_gej tmpj; - - secp256k1_scalar_set_int(&szero, 0); - secp256k1_gej_set_infinity(r); - secp256k1_gej_set_infinity(&tmpj); - /* r = inp_g_sc*G */ - secp256k1_ecmult(ctx, r, &tmpj, &szero, inp_g_sc); - for (point_idx = 0; point_idx < n_points; point_idx++) { - secp256k1_ge point; - secp256k1_gej pointj; - secp256k1_scalar scalar; - if (!cb(&scalar, &point, point_idx, cbdata)) { - return 0; - } - /* r += scalar*point */ - secp256k1_gej_set_ge(&pointj, &point); - secp256k1_ecmult(ctx, &tmpj, &pointj, &scalar, NULL); - secp256k1_gej_add_var(r, r, &tmpj, NULL); - } - return 1; -} - -/* Compute the number of batches and the batch size given the maximum batch size and the - * total number of points */ -static int secp256k1_ecmult_multi_batch_size_helper(size_t *n_batches, size_t *n_batch_points, size_t max_n_batch_points, size_t n) { - if (max_n_batch_points == 0) { - return 0; - } - if (max_n_batch_points > ECMULT_MAX_POINTS_PER_BATCH) { - max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH; - } - if (n == 0) { - *n_batches = 0; - *n_batch_points = 0; - return 1; - } - /* Compute ceil(n/max_n_batch_points) and ceil(n/n_batches) */ - *n_batches = 1 + (n - 1) / max_n_batch_points; - *n_batch_points = 1 + (n - 1) / *n_batches; - return 1; -} - -typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_callback* error_callback, const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t); -static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { - size_t i; - - int (*f)(const secp256k1_callback* error_callback, const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t); - size_t n_batches; - size_t n_batch_points; - - secp256k1_gej_set_infinity(r); - if (inp_g_sc == NULL && n == 0) { - return 1; - } else if (n == 0) { - secp256k1_scalar szero; - secp256k1_scalar_set_int(&szero, 0); - secp256k1_ecmult(ctx, r, r, &szero, inp_g_sc); - return 1; - } - if (scratch == NULL) { - return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n); - } - - /* Compute the batch sizes for Pippenger's algorithm given a scratch space. If it's greater than - * a threshold use Pippenger's algorithm. Otherwise use Strauss' algorithm. - * As a first step check if there's enough space for Pippenger's algo (which requires less space - * than Strauss' algo) and if not, use the simple algorithm. */ - if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_pippenger_max_points(error_callback, scratch), n)) { - return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n); - } - if (n_batch_points >= ECMULT_PIPPENGER_THRESHOLD) { - f = secp256k1_ecmult_pippenger_batch; - } else { - if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(error_callback, scratch), n)) { - return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n); - } - f = secp256k1_ecmult_strauss_batch; - } - for(i = 0; i < n_batches; i++) { - size_t nbp = n < n_batch_points ? n : n_batch_points; - size_t offset = n_batch_points*i; - secp256k1_gej tmp; - if (!f(error_callback, ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) { - return 0; - } - secp256k1_gej_add_var(r, r, &tmp, NULL); - n -= nbp; - } - return 1; -} - -#endif /* SECP256K1_ECMULT_IMPL_H */ diff --git a/libwallet/musig/ecmult_static_context.h b/libwallet/musig/ecmult_static_context.h deleted file mode 100644 index 8c0577b8..00000000 --- a/libwallet/musig/ecmult_static_context.h +++ /dev/null @@ -1,1163 +0,0 @@ -#ifndef SECP256K1_ECMULT_STATIC_CONTEXT_H -#define SECP256K1_ECMULT_STATIC_CONTEXT_H -#include "group.h" -#define SC SECP256K1_GE_STORAGE_CONST -#if ECMULT_GEN_PREC_N != 64 || ECMULT_GEN_PREC_G != 16 - #error configuration mismatch, invalid ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G. Try deleting ecmult_static_context.h before the build. -#endif -static const secp256k1_ge_storage secp256k1_ecmult_static_context[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G] = { -{ - SC(983487347u, 1861041900u, 2599115456u, 565528146u, 1451326239u, 148794576u, 4224640328u, 3120843701u, 2076989736u, 3184115747u, 3754320824u, 2656004457u, 2876577688u, 2388659905u, 3527541004u, 1170708298u), - SC(3830281845u, 3284871255u, 1309883393u, 2806991612u, 1558611192u, 1249416977u, 1614773327u, 1353445208u, 633124399u, 4264439010u, 426432620u, 167800352u, 2355417627u, 2991792291u, 3042397084u, 505150283u), - SC(1792710820u, 2165839471u, 3876070801u, 3603801374u, 2437636273u, 1231643248u, 860890267u, 4002236272u, 3258245037u, 4085545079u, 2695347418u, 288209541u, 484302592u, 139267079u, 14621978u, 2750167787u), - SC(11094760u, 1663454715u, 3104893589u, 1290390142u, 1334245677u, 2671416785u, 3982578986u, 2050971459u, 2136209393u, 1792200847u, 367473428u, 114820199u, 1096121039u, 425028623u, 3983611854u, 923011107u), - SC(3063072592u, 3527226996u, 3276923831u, 3785926779u, 346414977u, 2234429237u, 547163845u, 1847763663u, 2978762519u, 623753375u, 2207114031u, 3006533282u, 3147176505u, 1421052999u, 4188545436u, 1210097867u), - SC(1763690305u, 2162645845u, 1202943473u, 469109438u, 1159538654u, 390308918u, 1603161004u, 2906790921u, 2394291613u, 4089459264u, 1827402608u, 2166723935u, 3207526147u, 1197293526u, 436375990u, 1773481373u), - SC(1882939156u, 2815960179u, 295089455u, 2929502411u, 2990911492u, 2056857815u, 3502518067u, 957616604u, 1591168682u, 1240626880u, 1298264859u, 1839469436u, 3185927997u, 2386526557u, 4025121105u, 260593756u), - SC(699984967u, 3527536033u, 3843799838u, 958940236u, 927446270u, 2095887205u, 733758855u, 793790581u, 2288595512u, 2237855935u, 4158071588u, 103726164u, 1804839263u, 2006149890u, 3944535719u, 3558448075u), - SC(1145702317u, 3893958345u, 851308226u, 566580932u, 1803510929u, 244954233u, 754894895u, 1321302288u, 772295727u, 4004336128u, 2009158070u, 4026087258u, 1899732245u, 1392930957u, 3019192545u, 149625039u), - SC(3772604811u, 577564124u, 4116730494u, 548732504u, 241159976u, 965811878u, 3286803623u, 3781136673u, 2690883927u, 863484863u, 463101630u, 2948469162u, 1712070245u, 3742601912u, 2535479384u, 1015456764u), - SC(2610513434u, 780361970u, 4072278968u, 3165566617u, 362677842u, 1775830058u, 4195110448u, 2813784845u, 1072168452u, 1018450691u, 1028609376u, 2101464438u, 2419500187u, 2190549840u, 1837865365u, 625038589u), - SC(1347265449u, 3654928411u, 3255194520u, 1322421425u, 3049188507u, 1827004342u, 3467202132u, 4261348427u, 3419671838u, 2239837129u, 3441474020u, 268041876u, 4157379961u, 971431753u, 2053887746u, 2038404815u), - SC(3723233964u, 515696298u, 2908946645u, 1626400003u, 2191461318u, 1201029625u, 186243574u, 1212380923u, 858781105u, 4236445790u, 1936144063u, 1009147468u, 2407567966u, 1865959325u, 1701035060u, 241151649u), - SC(3696430315u, 3089900654u, 1103438577u, 3528924465u, 1259662835u, 2438429227u, 1692672370u, 2989843137u, 1446894995u, 2239587625u, 2340544036u, 434491102u, 128239031u, 2734594294u, 2667284742u, 1865591178u), - SC(1980028431u, 1099813170u, 2013628738u, 4214038867u, 3231891435u, 3896266769u, 2756820145u, 1490749299u, 951981230u, 3655451652u, 1676645053u, 3593230746u, 3010864552u, 405419875u, 1336073872u, 1398624425u), - SC(3414779716u, 2008156201u, 4125277506u, 2287126283u, 2446053551u, 212726297u, 2794923956u, 3421277562u, 1460719994u, 552209919u, 2551004934u, 953727248u, 3096710400u, 3712627263u, 3614955842u, 557715603u) -}, -{ - SC(461660907u, 483260338u, 3090624303u, 3468817529u, 2869411999u, 3408320195u, 157674611u, 1298485121u, 103769941u, 3030878493u, 1440637991u, 4223892787u, 3840844824u, 2730509202u, 2748389383u, 214732837u), - SC(4143598594u, 459898515u, 2922648667u, 1209678535u, 1176716252u, 1612841999u, 2202917330u, 13015901u, 1575346251u, 891263272u, 3091905261u, 3543244385u, 3935435865u, 2372913859u, 1075649255u, 201888830u), - SC(3481295448u, 3640243220u, 2859715852u, 3846556079u, 1065182531u, 2330293896u, 2178091110u, 3893510868u, 4099261975u, 2577582684u, 4207143791u, 589834100u, 2090766670u, 4242818989u, 2413240316u, 1338191979u), - SC(1222367653u, 2295459885u, 1856816550u, 918616911u, 3733449540u, 288527426u, 308654335u, 175301747u, 2585816357u, 1572985110u, 3820086017u, 3400646033u, 3928615806u, 2543586180u, 1619974000u, 1257448912u), - SC(3467862907u, 681146163u, 2909728989u, 83906098u, 2626131995u, 3872919971u, 2290108548u, 1697087700u, 1793941143u, 3236443826u, 1940064764u, 1563989881u, 527371209u, 610869743u, 1604941439u, 3670721525u), - SC(2302729378u, 1391194029u, 1641771531u, 3876177737u, 1929557473u, 2752989331u, 2519109900u, 1131448856u, 3786463166u, 506905989u, 2345013855u, 2144715811u, 1583628159u, 291930150u, 3243470493u, 4130365181u), - SC(2855519179u, 3147287790u, 1536116015u, 1784764672u, 959281397u, 3099717666u, 86403980u, 3409201632u, 3921301684u, 2101228153u, 575924517u, 1382150904u, 641478876u, 3860064926u, 1937521554u, 2358132463u), - SC(972265053u, 3025511526u, 2467192450u, 4011934802u, 4015820825u, 3179306985u, 1744647725u, 423238442u, 2406064939u, 901607195u, 3316491016u, 4128592049u, 1397491632u, 439641584u, 90500461u, 2834580417u), - SC(1730532518u, 2821193463u, 2700804628u, 2416923244u, 3795632308u, 2799866320u, 3434703577u, 3883111373u, 1777933228u, 2963254493u, 3042948878u, 1746288680u, 2832145340u, 544625602u, 3633879343u, 2300858165u), - SC(62331695u, 2228442612u, 3527845246u, 2989876118u, 3995298903u, 3601545798u, 4170931516u, 445717530u, 1981201926u, 94264130u, 2668647577u, 953251412u, 3322279962u, 3837653687u, 3116466555u, 3369796531u), - SC(2739333573u, 3637259489u, 443756582u, 825678124u, 2455706402u, 2994548791u, 3653546249u, 2584145078u, 1245698352u, 89066746u, 1738138166u, 2916153640u, 1850062717u, 3472193431u, 2110631011u, 1214009088u), - SC(2386327178u, 3993497770u, 1051345891u, 4137183237u, 3078790224u, 3598213568u, 3344610192u, 1517270932u, 869515922u, 2057215060u, 2792454282u, 4228826509u, 3425305972u, 2708629086u, 880185559u, 1356729037u), - SC(2989561710u, 3550122639u, 1990591383u, 2036612756u, 3588709655u, 595888062u, 4189293408u, 1955008963u, 987876526u, 542093629u, 1953520395u, 2315684331u, 2929815182u, 3270759899u, 393611756u, 1677885197u), - SC(2331762734u, 371120497u, 1141333410u, 3466824114u, 4113916626u, 3698793791u, 2483365276u, 4265751258u, 3804325409u, 4085909553u, 3531838853u, 2629626707u, 625187055u, 3045263564u, 198131065u, 3993694760u), - SC(27419592u, 3267954699u, 2966738458u, 3143461717u, 3869766944u, 2163162934u, 1886283869u, 2052225367u, 958768216u, 2006727717u, 2069130137u, 1939449196u, 3015752138u, 258766841u, 3290132621u, 4163970366u), - SC(903383785u, 2983456345u, 4269392462u, 3731664159u, 1837248343u, 1888413004u, 652691803u, 897487558u, 3732206419u, 3625013640u, 1917594162u, 967935585u, 1804564817u, 883883125u, 2389854768u, 2347234078u) -}, -{ - SC(1793692126u, 406948681u, 23075151u, 2805328754u, 3264854407u, 427926777u, 2859563730u, 198037267u, 2129133850u, 1089701106u, 3842694445u, 2533380467u, 663211132u, 2312829798u, 807127373u, 38506815u), - SC(571890638u, 3882751380u, 1536180709u, 3437159763u, 3953528399u, 516828575u, 3769463872u, 1449076325u, 4270798907u, 3135758980u, 3520630973u, 1452980147u, 3957715387u, 3054428574u, 2391664223u, 2297670550u), - SC(2724204046u, 2456139736u, 265045669u, 1367810338u, 1722635060u, 1306450931u, 2894913322u, 3094293390u, 3490680992u, 2550020195u, 3028635086u, 4200216295u, 1066664286u, 4170330175u, 777827015u, 183484181u), - SC(947228665u, 1559209921u, 3080864826u, 3123295835u, 2934045313u, 1590990229u, 2766960143u, 3113606941u, 1136432319u, 3758046297u, 2054046144u, 1377389889u, 3244301201u, 127071274u, 1752358610u, 2783507663u), - SC(1460807831u, 3649051054u, 2799484569u, 1231562901u, 3377754600u, 3577118892u, 1234337315u, 380370215u, 3272388869u, 3656237932u, 2653126291u, 786263023u, 1028996455u, 4274234235u, 4225822550u, 10734444u), - SC(2071087047u, 1934036755u, 611830132u, 2015415885u, 1373497691u, 3709112893u, 3810392851u, 1519037663u, 779113716u, 2738053543u, 2754096050u, 2121500804u, 982626833u, 1064427872u, 1627071029u, 1799421889u), - SC(490669665u, 331510235u, 927653097u, 4010558541u, 1341899186u, 2739641489u, 1436050289u, 1379364712u, 441190387u, 3816107121u, 4151493979u, 3530159022u, 2848669857u, 2894763699u, 1938279708u, 3206735972u), - SC(1164630680u, 735028522u, 1426163473u, 1764145219u, 2188722839u, 2599797011u, 2331123230u, 996298865u, 2803113036u, 1732133918u, 4135374745u, 1403496102u, 61305906u, 1982207767u, 35608603u, 680731708u), - SC(3097030574u, 2239944926u, 3004506636u, 3698971324u, 438440050u, 806226289u, 3299217652u, 2137747676u, 2376642592u, 2372355096u, 1444993877u, 4198291752u, 3194432604u, 579432496u, 3143260503u, 58153128u), - SC(3073570790u, 2457870973u, 3254087300u, 132589961u, 3090464363u, 4031655485u, 3397735349u, 3738272915u, 2438408586u, 1610016484u, 3607490511u, 1979839295u, 1993157220u, 1628966973u, 2566520843u, 2415504793u), - SC(2516700697u, 2521039798u, 2777488721u, 3196543385u, 3593950703u, 2445108602u, 4227515375u, 3361503440u, 3741757104u, 1367007706u, 4282009789u, 2127358709u, 2970274265u, 108953332u, 1376097231u, 3612352600u), - SC(2841122028u, 289695603u, 908429972u, 1449591303u, 3496532142u, 430811028u, 1377898285u, 198605765u, 702014643u, 1582973696u, 1654127041u, 4145703462u, 294032334u, 4235431914u, 3438393459u, 865474483u), - SC(3545445168u, 3333415739u, 2928811023u, 1435493501u, 3112072977u, 3466119300u, 61597844u, 839813414u, 3787328278u, 1928915478u, 3046796186u, 549615137u, 3862451403u, 1325262296u, 3520760105u, 1333228419u), - SC(1325790793u, 3907821545u, 4134901119u, 1951705246u, 3223387882u, 561480379u, 1136389443u, 2963679361u, 3722857515u, 626885912u, 3665060294u, 2975869036u, 1378007717u, 1212143055u, 3672021732u, 2520983812u), - SC(436660944u, 1593040065u, 2835874356u, 3054866405u, 1746716106u, 2901130226u, 3275156703u, 889550475u, 1667636846u, 2171317649u, 477876339u, 169193861u, 3301423024u, 2923695575u, 1084572294u, 981889567u), - SC(3803276281u, 4055280968u, 3904809427u, 186227966u, 932166956u, 2399165660u, 3851784532u, 3001852135u, 813014380u, 4116676373u, 2706810629u, 527442580u, 120296772u, 3128162880u, 662936789u, 1729392771u) -}, -{ - SC(1686440452u, 1988561476u, 754604000u, 1313277943u, 3972816537u, 316394247u, 994407191u, 1904170630u, 2086644946u, 2443632379u, 2709748921u, 1003213045u, 3157743406u, 1758245536u, 3227689301u, 1181052876u), - SC(1258105424u, 4154135555u, 2219123623u, 3901620566u, 4152326230u, 2255006844u, 2043811343u, 3401743053u, 1077175625u, 4217078864u, 23446180u, 3296093630u, 2983403379u, 483875022u, 1821322007u, 933769937u), - SC(4094896192u, 2631249021u, 2047644402u, 1580854103u, 3103587285u, 3577080832u, 2726417365u, 309664155u, 1801899606u, 2578001137u, 150353312u, 1950478529u, 895600852u, 3405805048u, 2316670682u, 3067768105u), - SC(443311827u, 441757202u, 1505167796u, 3339695156u, 4080303377u, 2032258566u, 4249816510u, 3524388084u, 3057881006u, 1951550910u, 755229308u, 2331249069u, 1739558582u, 2222328965u, 511821487u, 2764767310u), - SC(989753134u, 2338266356u, 549068233u, 4113024610u, 2746193091u, 2634969710u, 3079940655u, 3384912157u, 143838693u, 4047635856u, 4286586687u, 149695182u, 1777393012u, 52209639u, 2932952119u, 3267437714u), - SC(682610480u, 2717190045u, 3874701500u, 2657184992u, 2055845501u, 1316949440u, 1867841182u, 3514766617u, 3083609836u, 2586162565u, 866399081u, 1085717952u, 3259379257u, 575055971u, 3866877694u, 451222497u), - SC(328731030u, 2942825188u, 1841689481u, 3492191519u, 967390237u, 99172838u, 3036642267u, 3931425637u, 933459735u, 3523655044u, 2662830483u, 2533317360u, 1151283556u, 1285468956u, 15891850u, 3194406721u), - SC(3082245252u, 2305218459u, 2853219703u, 1279555698u, 3695999195u, 2225441691u, 2702374346u, 2002979755u, 3394310641u, 1438568303u, 441738339u, 2319547123u, 745721770u, 3663132780u, 3613740038u, 3163545587u), - SC(3109530474u, 209548946u, 1705898345u, 1227555051u, 1300903197u, 521706788u, 1046889791u, 392785355u, 1195852439u, 1128202903u, 589172095u, 3844020294u, 989062243u, 3765536158u, 3601935109u, 563198009u), - SC(1408383323u, 2941773350u, 4185382573u, 3662857379u, 4172908289u, 4118722458u, 1935569844u, 1296819381u, 439467796u, 917888253u, 1573015538u, 2875181025u, 22626495u, 313409715u, 121133518u, 1579603291u), - SC(838355261u, 2323744266u, 929233883u, 1533162328u, 2939669145u, 1021427197u, 2448693967u, 1568998094u, 455286333u, 2516902543u, 1708158744u, 278073872u, 978123683u, 2512836694u, 3972232382u, 1433020779u), - SC(2010810703u, 4018381427u, 571706262u, 1692351234u, 4256546562u, 1231266051u, 268479287u, 2820752911u, 2261632188u, 845795375u, 3555293251u, 4247559674u, 3383569817u, 4149228066u, 180667610u, 1402241180u), - SC(3525485702u, 3451430050u, 2349871300u, 60510511u, 4165534527u, 3431222792u, 4244473672u, 526926602u, 763199050u, 672899723u, 1978849638u, 489006191u, 1575850086u, 1948428588u, 201110001u, 2038136322u), - SC(3829603224u, 567257667u, 2324557421u, 3080821304u, 1922441927u, 1741539649u, 2023385976u, 3349327437u, 1997432110u, 3734074051u, 1330703636u, 3180299184u, 1913578229u, 141656008u, 2692604045u, 1602929664u), - SC(29051889u, 27392875u, 2013870801u, 1608859787u, 4192290684u, 944038467u, 2706126772u, 4086572363u, 3654238115u, 631287070u, 4277765317u, 2361271762u, 4170133585u, 2022489410u, 2834765713u, 1378478404u), - SC(2835113470u, 3839837803u, 3596950757u, 2129670392u, 1881028173u, 4057879348u, 2459142230u, 3736551989u, 3032996358u, 1333513239u, 3006303259u, 3885122327u, 4228039994u, 134788219u, 3631677646u, 450886807u) -}, -{ - SC(2450731413u, 2768047193u, 2114778718u, 2363611449u, 3811833768u, 1142236074u, 836975073u, 719658637u, 89564040u, 2055034782u, 2279505737u, 2354364196u, 748992674u, 2341838369u, 3471590741u, 3103440079u), - SC(464369172u, 1784969737u, 2303680721u, 1699015058u, 1839678160u, 53342253u, 3929309179u, 3713202491u, 1764215120u, 2190365769u, 3137266333u, 3919018972u, 3446276485u, 1027535494u, 3649392538u, 1979045036u), - SC(3689697965u, 1535268856u, 4095087266u, 1879342666u, 1901613989u, 4062220771u, 1231692686u, 3479254943u, 517178359u, 3704348661u, 3200159500u, 592930780u, 3995209641u, 2367381241u, 1790597847u, 2276745810u), - SC(1563410665u, 2779883331u, 320555798u, 143478861u, 1984047202u, 2486036480u, 1819096593u, 876845470u, 4160262809u, 1685665332u, 1096211683u, 3396846267u, 1079209808u, 1622135728u, 2746449213u, 2258485533u), - SC(1981422448u, 2212169687u, 873443773u, 3576733408u, 3923794933u, 1875069523u, 3053667173u, 4292418240u, 2192702144u, 1027092432u, 278807989u, 2315740043u, 485097259u, 4099751129u, 1350843241u, 1137138810u), - SC(3929635582u, 2647315129u, 1255145681u, 2059161179u, 1939751218u, 2574940312u, 1013734441u, 3958841903u, 615021475u, 1092396560u, 1516857705u, 4167743313u, 744612233u, 1609870616u, 1905505775u, 2106400820u), - SC(1036005687u, 2272703162u, 2208830030u, 2182996589u, 441615709u, 3591433922u, 3586649797u, 164179585u, 3077875769u, 1792522157u, 2657252843u, 657567108u, 656390324u, 1816007391u, 3075467586u, 3873231707u), - SC(1236896749u, 2895887291u, 1978987518u, 822801819u, 516389325u, 1102535042u, 1787993035u, 3557481093u, 3231661433u, 991180576u, 3686912074u, 1297456949u, 3327185778u, 308709174u, 495078044u, 2969592590u), - SC(2019907021u, 744703189u, 2139199843u, 518542186u, 3124680574u, 142934434u, 551498542u, 3021773546u, 4091561632u, 1051317147u, 825719313u, 3707224763u, 335483791u, 4028731434u, 1335000639u, 4102709448u), - SC(1093818871u, 985937516u, 327542691u, 2046117782u, 1264752065u, 697293694u, 1615263505u, 1156460629u, 2812684388u, 1192736815u, 3019618111u, 4209127823u, 2556369187u, 2112950523u, 637809851u, 2176824541u), - SC(1687299893u, 3728297084u, 490922479u, 3634470646u, 250826345u, 3692215527u, 3273717576u, 965983458u, 2226919381u, 1460789800u, 2122435754u, 2519058236u, 1620196106u, 4066817802u, 1130044433u, 3889340415u), - SC(852530522u, 3312783835u, 1596416107u, 1741549460u, 2684468674u, 3424816114u, 2501858342u, 1775689041u, 2140910620u, 3593295971u, 3269455071u, 2386348485u, 3506744308u, 1454965514u, 1429132807u, 1936823584u), - SC(606602909u, 3019871883u, 3512048756u, 3287518999u, 3877975051u, 3914786486u, 3870177904u, 1340649290u, 520571284u, 3028797996u, 2616337132u, 1103844529u, 3133726039u, 1357152000u, 1508799653u, 31330228u), - SC(2817743510u, 2877820134u, 3034826170u, 1694674814u, 3472934533u, 2992700940u, 940570741u, 734740020u, 2101869811u, 3976806699u, 3986671415u, 556491401u, 2336314226u, 3375597171u, 2706276162u, 2068498899u), - SC(2875346415u, 3996130283u, 2530370154u, 2292821435u, 1717542531u, 4166402291u, 2045046397u, 210928306u, 1305773764u, 891667924u, 1720475570u, 2097400197u, 3748242244u, 1645769622u, 3986372109u, 4259524466u), - SC(258680563u, 3407077353u, 3701760456u, 1531445568u, 3746171918u, 2983392727u, 1490964851u, 3947644742u, 2779475335u, 3867487462u, 2573576052u, 3434694262u, 2755711440u, 3366989652u, 566303708u, 3091229946u) -}, -{ - SC(2925506593u, 3911544000u, 1647760999u, 3077282783u, 810174083u, 3532746750u, 1218244633u, 1800164995u, 3882366571u, 1552758454u, 417617232u, 3581187042u, 1107218813u, 308444727u, 2996521844u, 3546298006u), - SC(2766498247u, 1567718891u, 906631353u, 1539374134u, 2937267715u, 3075423755u, 466239025u, 348294756u, 2802746156u, 3822638356u, 2215866298u, 2676073175u, 2327206803u, 3701444736u, 533673746u, 1949565232u), - SC(779912645u, 2120746348u, 3775586707u, 1719694388u, 3225985094u, 1124933767u, 2466028289u, 3688916232u, 2352972448u, 3100332066u, 3699049642u, 105143046u, 3528587757u, 3202351686u, 3275195870u, 2542878955u), - SC(4208701680u, 3032319563u, 1934783333u, 1683344422u, 1898255641u, 1818484420u, 1090856325u, 4203146066u, 3166734039u, 1425051511u, 411614967u, 1272168350u, 905464202u, 2860309946u, 2899721999u, 4016531256u), - SC(1252276677u, 705548877u, 3321309972u, 2587486609u, 1841091772u, 1176108340u, 2483104333u, 1124739854u, 1417860124u, 2145011089u, 1095816787u, 561231448u, 3047186502u, 2188442748u, 782343512u, 2073487869u), - SC(773625401u, 1399854511u, 2112273744u, 3798562401u, 2328245221u, 4053035765u, 884849756u, 2543299151u, 3064173848u, 3322400978u, 2493736578u, 4109781307u, 3356431908u, 2033183790u, 3916558464u, 937192909u), - SC(1676839026u, 1837563838u, 681907940u, 1979087218u, 3861274680u, 1004821519u, 3526269549u, 3587326189u, 4130121750u, 5876755u, 277168273u, 3347389376u, 1295078502u, 3055055655u, 988901279u, 1750695367u), - SC(1466696987u, 793586382u, 3395028606u, 688541023u, 227515247u, 433349930u, 1151320534u, 2638968365u, 2730052118u, 2419949779u, 4184196159u, 3075595332u, 1762597117u, 3208522231u, 3793454426u, 2205574333u), - SC(2271935805u, 2221340650u, 4006866556u, 3892925071u, 3300102857u, 4023132062u, 1966820825u, 193229358u, 3829742367u, 3288127030u, 2999566231u, 1746318860u, 611198282u, 1740582489u, 586692015u, 272371975u), - SC(1512874083u, 1683202061u, 3100471136u, 875884760u, 2252521753u, 3056609126u, 2397470151u, 3238829627u, 398340158u, 1086173909u, 2650682699u, 3851040891u, 267796754u, 1063916466u, 134772391u, 616879617u), - SC(1190901836u, 3498895828u, 121518848u, 4122627266u, 4044339275u, 3929319666u, 3725675569u, 2249645810u, 1648430039u, 805152867u, 604009597u, 428134903u, 3660078748u, 1495738811u, 2912743026u, 3529964664u), - SC(1098872981u, 3803982233u, 1184687675u, 1724685244u, 1166128174u, 3324080552u, 2889006549u, 591614595u, 442372335u, 2188313994u, 392144341u, 559497602u, 2786744839u, 1080958720u, 963196350u, 4153188088u), - SC(2439538370u, 4080798018u, 3371249236u, 2272355420u, 3780648680u, 116755088u, 1743646150u, 3071185844u, 3348389643u, 3506488228u, 3592742183u, 3935997343u, 3470563636u, 4177761627u, 2879753187u, 203653531u), - SC(3278048310u, 2898758456u, 2355004932u, 2165371155u, 909690763u, 4208028121u, 3529336571u, 120122699u, 1468577489u, 2088039937u, 3804192119u, 4005659309u, 496708233u, 114985314u, 4186471387u, 1516837088u), - SC(1694326758u, 3482448156u, 2533790413u, 3535432659u, 1293417127u, 2007819995u, 3512854075u, 2476797465u, 936262398u, 4149678787u, 807292055u, 1683402105u, 3767740082u, 682769936u, 2956180563u, 2800734304u), - SC(804843744u, 1565609957u, 1986774659u, 4163563545u, 1192892219u, 2967653559u, 1407927717u, 134508609u, 2584983666u, 3798685912u, 1759632157u, 1938927553u, 3974685712u, 2763800386u, 3702401831u, 3969543832u) -}, -{ - SC(2016238746u, 3648008750u, 3741265531u, 1468285316u, 3314132186u, 3225615603u, 2260838904u, 650230459u, 666608997u, 1079817106u, 1685466519u, 3417306450u, 465799968u, 1454367507u, 1432699603u, 4060146438u), - SC(1761873323u, 2175765323u, 123320402u, 1086415744u, 3425420197u, 3163463272u, 2096010147u, 892174421u, 3834451486u, 191088153u, 650609666u, 1384830375u, 430440180u, 1275312435u, 936713210u, 3964237847u), - SC(3490530311u, 4154571850u, 1473147571u, 1874492814u, 3394183939u, 690761407u, 1765847887u, 4254640890u, 3957252213u, 852293459u, 403059579u, 1419995731u, 373422976u, 1691953324u, 1513498303u, 3782064719u), - SC(2587537765u, 1727580331u, 2067598687u, 2050934719u, 1018600463u, 825517190u, 281367288u, 396667874u, 2125440864u, 2142555808u, 3739024155u, 471264185u, 2298783646u, 926505635u, 485317745u, 4237064052u), - SC(4177694527u, 1331122857u, 2632274962u, 2272030823u, 2711200568u, 493910969u, 64158788u, 2976239616u, 2805230971u, 1856476899u, 706343172u, 883417303u, 3085501222u, 2167885061u, 2608970459u, 1305891290u), - SC(3887930902u, 1612140391u, 329833229u, 737708613u, 660227298u, 2588285981u, 3429746116u, 4247477263u, 2536670475u, 1091054728u, 1521783433u, 4262529359u, 3261855757u, 453613765u, 484850910u, 3619344637u), - SC(3635973664u, 4002263582u, 683484955u, 1188525929u, 3024525647u, 1588813480u, 3496033065u, 109022234u, 2342061519u, 1416918501u, 2207158673u, 948640868u, 637445219u, 508491813u, 3897434662u, 680054967u), - SC(1039851594u, 403130855u, 3868498597u, 1611578944u, 2942424644u, 2874427101u, 1261647069u, 261871566u, 2520758170u, 2840740989u, 3799279215u, 381717039u, 3582347301u, 2025353438u, 2948438214u, 2918501540u), - SC(81851588u, 3029358979u, 3777821133u, 2109529880u, 3684139703u, 3572137489u, 2624799u, 2076188243u, 53500651u, 2606703535u, 3206313372u, 346558880u, 465806762u, 434266486u, 1902707603u, 4080110534u), - SC(3612241613u, 1917140707u, 4136616607u, 4041104182u, 2193790431u, 801466537u, 3599423303u, 3561003895u, 1189069231u, 8494816u, 4244955339u, 451969883u, 3908494655u, 517115239u, 1812731691u, 777430858u), - SC(3522137911u, 2027939004u, 2210696271u, 3920541975u, 875695915u, 2825269477u, 687289812u, 4252564584u, 1824925315u, 507608234u, 2614820601u, 2462525050u, 3886866857u, 668083682u, 2768243607u, 3293579201u), - SC(1682273922u, 1330912967u, 3636074852u, 840196898u, 1025234484u, 1557176067u, 2837118766u, 3109038869u, 594323342u, 3200796742u, 1959017554u, 1440926582u, 3021668826u, 3738492638u, 446292405u, 2414347832u), - SC(4116164451u, 4091036540u, 474505628u, 1269644927u, 3643568118u, 1673027873u, 1438360759u, 4022285580u, 4024623082u, 1654730750u, 1581385912u, 3853471495u, 335076979u, 2185560806u, 2494598452u, 3520671279u), - SC(4099595861u, 2215053464u, 488918654u, 2772869965u, 2247823716u, 1588093320u, 1138185172u, 732569291u, 247618738u, 1702163570u, 1772683376u, 1056938600u, 1997535786u, 2064838561u, 3705150691u, 1453615480u), - SC(3809909081u, 1962152573u, 3909100601u, 1479514000u, 1615313752u, 3569344372u, 997113509u, 3043376485u, 3480943705u, 4021042580u, 2284195092u, 2749518560u, 3037939132u, 3554704413u, 185068622u, 683070990u), - SC(3163624176u, 326387389u, 438403431u, 1924575191u, 1706136937u, 2432230714u, 4175139676u, 713582699u, 175432919u, 505729353u, 375905517u, 3179239595u, 2233296987u, 472507277u, 1822318909u, 3059447908u) -}, -{ - SC(3955300819u, 2390314746u, 8780989u, 1526705205u, 4147934248u, 1494146575u, 1667625450u, 2277923659u, 406493586u, 957460913u, 3449491434u, 912766689u, 1387230361u, 2368913075u, 3538729245u, 2943257094u), - SC(2095358020u, 3831852940u, 1752942227u, 477088929u, 2503091779u, 898077u, 2215106688u, 1298885808u, 352224250u, 3952364758u, 3669616566u, 664714721u, 1826685582u, 1576488055u, 2121138397u, 1442223205u), - SC(1378268686u, 187975558u, 3210161726u, 870689300u, 1860632239u, 902013623u, 571573600u, 25414363u, 3412397724u, 3841538145u, 215707661u, 324367139u, 2323478150u, 3794355190u, 1128115053u, 2519022352u), - SC(566244395u, 2591175241u, 2926679038u, 2852174582u, 200192886u, 521908517u, 2098042185u, 3563798587u, 1529741033u, 1248315044u, 233787221u, 2706044694u, 2870731528u, 3970719810u, 4167465378u, 525407392u), - SC(2196340159u, 4056996284u, 1702457669u, 2086317410u, 3933566271u, 3751624213u, 4023204768u, 677196918u, 2137509058u, 4037704026u, 2299370032u, 1748548051u, 3326874481u, 1974512389u, 1751264060u, 266112293u), - SC(1812114662u, 524787647u, 285577300u, 3638318945u, 3389691808u, 585441476u, 145370930u, 1149989778u, 1314386440u, 3471672106u, 908522311u, 4171434326u, 329350743u, 2954206373u, 856961382u, 2008618089u), - SC(2318825510u, 3826102862u, 687747522u, 4263777564u, 2387018418u, 1135189382u, 1414060091u, 217356911u, 2998889592u, 698204196u, 801530770u, 3479982231u, 1117806357u, 154519605u, 960816747u, 3149429798u), - SC(3250819610u, 351683992u, 296382659u, 4149667465u, 2183346760u, 1485561783u, 2218034265u, 420633334u, 1869679065u, 1205517989u, 3666184780u, 1975151679u, 371905540u, 367504198u, 1917294142u, 2403996454u), - SC(3958230362u, 3773825115u, 783748416u, 1243337893u, 4032003144u, 3908441244u, 201600922u, 2000451013u, 728826842u, 3533421010u, 3229478766u, 278198864u, 3933272000u, 1331731276u, 3202405750u, 1474627286u), - SC(3181836998u, 2581633616u, 3993055681u, 4020956268u, 2094932060u, 3551878275u, 393027783u, 2154269634u, 2283536956u, 2260289773u, 832949759u, 2403309662u, 3488387345u, 1652392255u, 393935478u, 2309058441u), - SC(4141036972u, 1727820200u, 832481848u, 1055621047u, 1091666560u, 1393833209u, 3406509646u, 2428157250u, 2974564551u, 2286298667u, 3776410458u, 815994971u, 1241023789u, 775596275u, 1035618310u, 3934253771u), - SC(206932164u, 4239023187u, 2046365950u, 2616857124u, 4246776524u, 4059028269u, 129664965u, 907402684u, 3859465657u, 4204192080u, 91453633u, 301171900u, 385561248u, 2689085222u, 1614465584u, 3977451005u), - SC(3683171878u, 3148577689u, 4042394721u, 1085044656u, 682611813u, 2857177748u, 2417075323u, 2983755657u, 3777418770u, 2448398967u, 3909780770u, 4000218621u, 4227580585u, 2425908645u, 1704039191u, 3712924954u), - SC(290465694u, 3921687099u, 2971845338u, 1854613741u, 1583022754u, 371222458u, 1744154613u, 3918664956u, 1960343256u, 1291903121u, 4010470137u, 1525668440u, 4091170130u, 1370665614u, 3468958243u, 1262617601u), - SC(469638518u, 1129475898u, 3766065538u, 1777952666u, 2589258222u, 3182239596u, 2626554219u, 1853296675u, 2912212627u, 2518041806u, 2743002885u, 3765128027u, 851537937u, 2059010589u, 1827964742u, 3630398912u), - SC(2458599023u, 2699477701u, 2305781427u, 2536499567u, 2118412162u, 1356010449u, 1426052710u, 725853717u, 1358092245u, 4196538471u, 66159936u, 4076320019u, 3065284443u, 2664736186u, 1943959552u, 939016920u) -}, -{ - SC(3159079334u, 690659487u, 1550245019u, 1719420482u, 1795694879u, 2846363486u, 15987067u, 569538014u, 1561199762u, 967336684u, 3110376818u, 1863433225u, 3468533545u, 3644881587u, 369296717u, 3652676359u), - SC(3207794512u, 2847938045u, 2415472979u, 1444858769u, 666387488u, 1660608279u, 1038886882u, 10876848u, 2468284561u, 2494495733u, 2622688628u, 2362399325u, 2213804831u, 3448783854u, 3958704532u, 3639349832u), - SC(54374990u, 186360229u, 3420619566u, 1356363720u, 2768151763u, 3862789233u, 4270651882u, 2681019589u, 2332931746u, 928338209u, 3968478928u, 3908570621u, 923281930u, 2285715383u, 3620920276u, 130031468u), - SC(4009596626u, 493238747u, 1786722937u, 653638870u, 1636723425u, 1884625267u, 2113708566u, 1448416211u, 3613674959u, 239497564u, 404863679u, 1521570751u, 2819432609u, 623319225u, 3073321373u, 565867032u), - SC(1220575379u, 4235426741u, 1889734996u, 43054857u, 879216917u, 3299856237u, 2922851906u, 1054251029u, 693641076u, 1704223409u, 961665328u, 2828086835u, 2727513652u, 1580557310u, 4169876178u, 682569510u), - SC(1757813477u, 22814395u, 3549822650u, 2254547303u, 372100012u, 1555116803u, 2587184145u, 3995169383u, 2645743307u, 188252331u, 3723854483u, 2138484090u, 1895504984u, 3538655836u, 1183003060u, 1439034601u), - SC(2578441833u, 3136721732u, 380864696u, 817462912u, 2257087586u, 2256998015u, 93155068u, 930999672u, 2793712092u, 2223512111u, 3157527446u, 1098951014u, 3490358734u, 1362531303u, 2421324125u, 1961922428u), - SC(1049179776u, 2969815936u, 3869567708u, 2883407597u, 1876243265u, 3498929528u, 2248008570u, 1231166427u, 3544374122u, 2839689583u, 1991744998u, 2798946627u, 736844268u, 1293771673u, 153373649u, 1931110485u), - SC(3785289356u, 1913060964u, 169967200u, 3348219956u, 3732729076u, 987877186u, 3063387163u, 3310757163u, 3480818987u, 1991307039u, 2882756981u, 1215305494u, 855630497u, 1471153868u, 1338946323u, 398364632u), - SC(1356154057u, 3013675057u, 3810909135u, 1796458190u, 2691409967u, 3963509663u, 2487357466u, 2764459334u, 2828737787u, 378542508u, 427318427u, 2412936991u, 393927878u, 3384382899u, 1135834101u, 3447900619u), - SC(3813669196u, 1922867812u, 483725924u, 518662823u, 3954558327u, 1908218112u, 2258643690u, 2093138355u, 1162728847u, 205977116u, 821018600u, 1237824238u, 2980682686u, 1821003630u, 3221633606u, 2717269894u), - SC(1353035942u, 2442753208u, 348196860u, 2355246066u, 2218279077u, 2203055542u, 1964199656u, 1329637142u, 1824193111u, 3965017045u, 795175573u, 1029253807u, 3915633667u, 1084707851u, 1682462202u, 2090124205u), - SC(190807548u, 1133131805u, 249542006u, 2858611426u, 304500253u, 2183315108u, 4145782890u, 2998333644u, 962888949u, 974441750u, 1484862994u, 801464190u, 2311388331u, 114769498u, 4260362972u, 1017092877u), - SC(1311406963u, 465174990u, 1760870095u, 883652788u, 1015674641u, 840236175u, 3124632038u, 2756294642u, 178804852u, 3164952754u, 241649187u, 1040890173u, 82588907u, 1771630815u, 1058353446u, 2473824375u), - SC(943051847u, 4107933890u, 535438460u, 2683519853u, 3177219980u, 3711205196u, 3390138615u, 2920849102u, 3747455519u, 4138118615u, 400899690u, 4278329560u, 2602463649u, 808685972u, 136036034u, 1078020636u), - SC(2185570356u, 3896907774u, 3620938057u, 1790823508u, 720763411u, 2404776615u, 3257162972u, 1221107462u, 3223154083u, 2528715719u, 688766234u, 1813423773u, 2324112952u, 83241050u, 4119437520u, 552112812u) -}, -{ - SC(3370489298u, 1718569235u, 523721575u, 2176389434u, 218587365u, 2490878487u, 2288222859u, 812943600u, 2821517993u, 3626217235u, 1545838667u, 3155352961u, 741681736u, 669093936u, 2382929309u, 2620482966u), - SC(40739723u, 469402467u, 1810137291u, 109375068u, 1888845715u, 2140810583u, 1053250454u, 3220064762u, 2539857789u, 4089587896u, 1364971662u, 2996699084u, 3939034030u, 2020251221u, 1606532641u, 3453095239u), - SC(1376139558u, 886121026u, 3003069127u, 3500718919u, 4223467610u, 3212808910u, 126355621u, 2065481301u, 218954382u, 1236555811u, 2283280895u, 4256918831u, 1550185311u, 896721211u, 4286247506u, 2515527710u), - SC(2942433244u, 2364220023u, 3675668782u, 3695614763u, 4041312428u, 2311531471u, 543507321u, 1902188023u, 1380686629u, 2455468346u, 2346421766u, 2211296276u, 3675221499u, 3890242164u, 3592353914u, 323566438u), - SC(971323999u, 4115912859u, 3703400072u, 2662062035u, 2355087034u, 610373016u, 2293984834u, 2456129286u, 2927901115u, 1832014620u, 1168920846u, 552716242u, 3101454502u, 1707155244u, 3450287619u, 2546358284u), - SC(3062358608u, 1394539264u, 4158727824u, 1704721957u, 1117692646u, 4057908715u, 1958466020u, 2309578289u, 271836599u, 2617957229u, 202314495u, 2948978715u, 1423414031u, 4128837100u, 1937488702u, 3301405882u), - SC(1276638700u, 885904232u, 3686149920u, 3283641475u, 619290126u, 2510808612u, 1691008630u, 573145513u, 506979295u, 3062936948u, 2703005699u, 4056634904u, 3460956977u, 3783023797u, 1215556973u, 3726733337u), - SC(3145485089u, 2008513183u, 2407056102u, 633050174u, 2634406893u, 2883313710u, 1233018283u, 3273507959u, 174012667u, 2126243450u, 2258342097u, 2857351925u, 3446764464u, 1187986524u, 3004835628u, 3228122242u), - SC(991481464u, 1720231754u, 1918287975u, 2752686681u, 1174123782u, 4227334584u, 1634945718u, 1074218184u, 3572504705u, 1199611126u, 1378243227u, 2901862427u, 2145083550u, 1055786253u, 3960418624u, 587771424u), - SC(3060872990u, 789280525u, 184089463u, 1784976524u, 344050594u, 2949751745u, 3173202246u, 3813443247u, 1247337895u, 4000924548u, 76989753u, 2093985529u, 265772293u, 3310477933u, 717631968u, 1024610284u), - SC(3399834097u, 2964304651u, 3593395714u, 2850196125u, 2344305533u, 3920139836u, 937580696u, 1116439644u, 4147778799u, 544787491u, 2461636418u, 2647550544u, 1451408824u, 3266700679u, 829974548u, 2625074193u), - SC(645329496u, 2808202504u, 1366740717u, 2841442794u, 1298546911u, 2730798019u, 3834987045u, 3258634143u, 4257492959u, 2976079952u, 1735944512u, 988426767u, 2395072762u, 3103996991u, 730963792u, 4206896923u), - SC(3457675112u, 4140282966u, 1286302693u, 575230857u, 2270112110u, 3056424235u, 1835144711u, 421529065u, 2499621064u, 1907217915u, 1365357672u, 2875249236u, 1193490885u, 644367230u, 2115448516u, 2507997379u), - SC(70240820u, 3745431832u, 1098747160u, 82118642u, 2446590634u, 851446619u, 2715739022u, 2142293045u, 2689000746u, 4219383621u, 3140617705u, 1457579904u, 2541485894u, 3932513084u, 3406615220u, 2746135210u), - SC(2576508439u, 3244150028u, 2516535555u, 3986514000u, 2903382402u, 2225326585u, 1780804949u, 1164188435u, 1682143109u, 2949153515u, 1249412173u, 349674695u, 3467452794u, 1021028584u, 1194554595u, 1296132950u), - SC(1028084134u, 2577983628u, 184499631u, 1037888434u, 1676727662u, 1831883333u, 1276555462u, 4161670547u, 372201005u, 844715673u, 24290758u, 1268964661u, 297554992u, 4061435345u, 719976096u, 1670144314u) -}, -{ - SC(1239892635u, 3772349433u, 1058531752u, 1409211242u, 2847698653u, 2391143499u, 2637108329u, 3000217976u, 4288568828u, 658925470u, 2552628125u, 1468771377u, 3230644908u, 2692030796u, 7587087u, 1951830015u), - SC(488413080u, 1055864530u, 1967623060u, 3973308786u, 2745059783u, 477755698u, 544732176u, 3786002606u, 1569550024u, 2935491988u, 1047898991u, 1749060996u, 1828274710u, 2943223535u, 3716062834u, 1253889972u), - SC(1626917485u, 492893476u, 2371366539u, 3928996000u, 3710769983u, 1237244931u, 1562679102u, 2930576193u, 2085522193u, 2968039733u, 3202113740u, 4250144171u, 3666251088u, 2016963274u, 293320478u, 3775462481u), - SC(3337977767u, 1831658883u, 1096680294u, 2436280860u, 119870062u, 1444445305u, 1467566544u, 2038307180u, 661842797u, 2493843529u, 3851219498u, 3941720925u, 1848373617u, 4051739727u, 1120765529u, 1101800264u), - SC(929493756u, 2211014659u, 3851484027u, 3468182176u, 147674626u, 3850162187u, 1517171722u, 907705770u, 3997080856u, 3666272567u, 659948161u, 2282159142u, 429200635u, 2563204390u, 1422415938u, 1688129608u), - SC(551422730u, 1797390513u, 2828310972u, 97463725u, 131839682u, 3917501017u, 566617767u, 700714239u, 3061891811u, 856175415u, 1072683059u, 1754819408u, 3533865753u, 2568134802u, 4226648923u, 32646755u), - SC(3538971706u, 2916601269u, 2891999350u, 3825811373u, 2355258376u, 2876141009u, 3940019347u, 1309282440u, 2567828520u, 1367177503u, 2910098023u, 1986452448u, 1802584940u, 1360144734u, 2877029236u, 3033303547u), - SC(3313753312u, 261894832u, 3637017242u, 3699232915u, 3508549542u, 3960876382u, 582644479u, 3199091169u, 3644395252u, 2675904765u, 2072396219u, 4071523208u, 3976776729u, 1025403411u, 2178466200u, 1107450603u), - SC(2163612584u, 2845646977u, 4033161886u, 2723908899u, 1902990762u, 3375716497u, 2588626243u, 513179480u, 3101622846u, 1458272618u, 3875706546u, 3028150894u, 3612001457u, 2583302957u, 3385091312u, 3719047138u), - SC(1256280924u, 3685139058u, 1853414115u, 3160743702u, 3455476559u, 2505590918u, 2308735646u, 3742507036u, 4016470170u, 330769483u, 3470077232u, 3383715347u, 1440115354u, 2667395648u, 1883060415u, 3332144245u), - SC(558087170u, 3027059128u, 1986900497u, 1642930671u, 5966195u, 3083778816u, 3199769457u, 1248728791u, 2110460619u, 327014118u, 2524517189u, 1776442925u, 1472982408u, 3459887088u, 1029172283u, 2232815594u), - SC(1544258748u, 3397993939u, 2721410152u, 2948125157u, 3562231734u, 3011402493u, 3266317933u, 527195819u, 369665170u, 3216603774u, 1952585925u, 258420856u, 3339671680u, 3733846143u, 2326118329u, 2310291176u), - SC(4140585488u, 4198875250u, 3415599245u, 3398679011u, 4155727512u, 331520374u, 785987151u, 146809315u, 2929041163u, 1558279570u, 1346822944u, 4167931729u, 2800498595u, 2809390575u, 3295157947u, 4121566122u), - SC(3571413466u, 1596401972u, 140853088u, 3137527478u, 204556611u, 4111255020u, 3835120334u, 3048525996u, 399176328u, 3005771198u, 780994070u, 3747160103u, 3136546207u, 508755537u, 2521091931u, 1715747893u), - SC(1156063870u, 393984449u, 1521183961u, 3649564442u, 183535572u, 3139859119u, 445469714u, 2815871833u, 1268459010u, 355340626u, 2465929503u, 750513297u, 1590602217u, 3983872541u, 97286792u, 110438349u), - SC(2549125874u, 1976691716u, 2532749644u, 279085303u, 633988261u, 3513450026u, 1057503157u, 1110164955u, 317735960u, 3241215082u, 3855084900u, 4137511567u, 3550729054u, 819799870u, 1929320159u, 2825290282u) -}, -{ - SC(2585638847u, 1394876113u, 3750575776u, 4144761638u, 1991524028u, 3165938218u, 158354186u, 812072970u, 3814951634u, 2507408645u, 1163603486u, 3566585210u, 1424854671u, 3326584505u, 3332079056u, 1901915986u), - SC(1520752595u, 1952396314u, 3263601295u, 3458083478u, 3797830135u, 509407552u, 3232598095u, 1205382790u, 2667815610u, 2560349365u, 2472625295u, 2883979179u, 554514567u, 2376619906u, 638138357u, 2568018129u), - SC(2442202610u, 2091297602u, 25025777u, 3622813695u, 3869161931u, 614884494u, 984078136u, 3345125623u, 3918959025u, 227030161u, 3885929851u, 1281751413u, 1612359075u, 2958486463u, 2884267132u, 3619290927u), - SC(3048700207u, 2570072469u, 1076153001u, 3767270422u, 1408579070u, 2076435276u, 2224129615u, 1962182553u, 1823335118u, 1499162388u, 1563913085u, 2068011578u, 1991334162u, 1665201834u, 1756239294u, 648108494u), - SC(2337582449u, 1819429591u, 3833487099u, 3870804287u, 2300831739u, 2232929806u, 1869816966u, 4084965807u, 4220168543u, 1248736546u, 924637940u, 73528534u, 2319796252u, 3657850751u, 2794932350u, 4220430348u), - SC(3028904021u, 2992718647u, 2354594543u, 3084902105u, 3127673085u, 783373559u, 3896264500u, 3984439851u, 820119108u, 4253719169u, 2623678017u, 3039126654u, 2922756242u, 2436956481u, 442364253u, 918876081u), - SC(1539558451u, 2306960255u, 1095386938u, 770368485u, 2906552323u, 3075682102u, 3534951832u, 2083903147u, 1308495764u, 2261904633u, 2112467113u, 1044610889u, 3222649255u, 1736090274u, 1954974285u, 1361850096u), - SC(587984395u, 1588261189u, 4052666242u, 512106258u, 651085942u, 2768947530u, 1250487652u, 1245674804u, 857176247u, 3594046498u, 647658046u, 2882585491u, 259918032u, 3698728358u, 632752990u, 351374374u), - SC(2404749839u, 3296323382u, 805352255u, 3954906457u, 3558496371u, 2470613864u, 2024150378u, 3564550335u, 2499521206u, 2051669779u, 607498894u, 3748811695u, 1128400961u, 3072401950u, 3042994760u, 811721793u), - SC(3595493043u, 1889077187u, 1981480426u, 4189336058u, 2081249554u, 2321560592u, 971543366u, 358725627u, 3595364674u, 3986924883u, 2193763710u, 4189195361u, 3121216309u, 1140981210u, 3226790033u, 353586077u), - SC(2871195936u, 2843651834u, 635723881u, 287569049u, 2067429609u, 2943584978u, 644639896u, 1264563774u, 670309272u, 2690274713u, 246950668u, 933865226u, 4053660195u, 1381269871u, 462688690u, 5420925u), - SC(977313734u, 4104230969u, 3334283655u, 1580178205u, 1578158646u, 1460773045u, 1728595474u, 3957344726u, 553676110u, 966612385u, 1786516334u, 2979157051u, 921122693u, 911238485u, 3922067113u, 1046213221u), - SC(91424183u, 123813459u, 1667146297u, 3387121372u, 438965888u, 4260725592u, 154972710u, 3237027664u, 3006360433u, 2505005588u, 2902337724u, 2660287100u, 1901200613u, 2646189902u, 2780155597u, 49560303u), - SC(3586622617u, 925349590u, 415005474u, 1260234539u, 30249250u, 2179523979u, 3475887768u, 3019952034u, 3517624902u, 4230850494u, 3734868171u, 742624613u, 822822789u, 3974379285u, 3711572581u, 3366701706u), - SC(329275906u, 1905371123u, 4004795330u, 2339811253u, 353091905u, 548998992u, 3687895576u, 356859438u, 2494263562u, 926298666u, 3983230019u, 2882391620u, 2824170047u, 2247742371u, 1881005652u, 1386887463u), - SC(1046492158u, 2680429213u, 1614272999u, 4010933686u, 2479992689u, 595409283u, 765550354u, 2852655093u, 1983575334u, 3910696497u, 2308266592u, 3012641543u, 2582478313u, 14949228u, 60656360u, 1955264759u) -}, -{ - SC(1355623958u, 2575138117u, 2562403739u, 1638722303u, 1523970956u, 2189861089u, 3498071469u, 1919711232u, 231840827u, 3230371223u, 143629793u, 1497495034u, 1677900731u, 1608282251u, 3485501508u, 3944969019u), - SC(1317209868u, 3823870608u, 3335344652u, 3702793515u, 2425890570u, 1442662397u, 4007605978u, 2976935239u, 1444558882u, 3449074340u, 523287240u, 1767769527u, 1776192231u, 1111610095u, 4035220013u, 3434023407u), - SC(1286632782u, 1751340143u, 184421370u, 3989392405u, 1838859918u, 3681550977u, 707040060u, 2695037953u, 1828105102u, 812532736u, 1115387936u, 1381188966u, 1389542552u, 621856846u, 1135930465u, 831833090u), - SC(2741542793u, 3565635943u, 455105161u, 2389444906u, 2966273581u, 4048751601u, 2569017914u, 1796095397u, 1515760827u, 3870103158u, 2737365395u, 818096507u, 2179280538u, 1254083919u, 2114706477u, 1413209953u), - SC(2036431795u, 3313380793u, 2996275588u, 625273343u, 1627738147u, 2163909313u, 2645773664u, 3066825866u, 3862562238u, 3189614065u, 3074707667u, 1611214266u, 689055345u, 1845962762u, 3616153367u, 98214289u), - SC(1783057147u, 1095836105u, 952581152u, 665189523u, 4159236737u, 3621720388u, 2768968806u, 1541462219u, 1550070665u, 2946487171u, 3084327270u, 3528580128u, 3683323170u, 2326350340u, 681502936u, 611874814u), - SC(2075965546u, 3954443814u, 3457426695u, 3100575745u, 795895906u, 2051458923u, 4220432661u, 3191956430u, 2978441632u, 1935083482u, 1260223004u, 1989210512u, 708452144u, 1742032782u, 412060225u, 942058976u), - SC(1554952802u, 1148928548u, 435577880u, 1218016814u, 774531999u, 4171943086u, 2728379380u, 1755428421u, 3096769247u, 551470356u, 663936617u, 2259245103u, 3605128160u, 4254582248u, 2543346251u, 2641240630u), - SC(2834055303u, 3779347324u, 986655417u, 1060344853u, 1961336735u, 3444096071u, 3402507696u, 1296975131u, 4013745799u, 318316127u, 3012349080u, 1543913977u, 3581569730u, 3073345556u, 1048961320u, 3338742347u), - SC(1917475623u, 1573453706u, 3775608035u, 1560651154u, 3305702627u, 840251936u, 2021694407u, 1567223161u, 1217097878u, 4101089784u, 1480235880u, 823763473u, 1860062290u, 3212933927u, 305432786u, 2664137512u), - SC(488290329u, 2159084342u, 1977681447u, 3072933047u, 2133970307u, 2904163387u, 2929381044u, 2852811875u, 3486789427u, 3312981159u, 2897952520u, 3716688458u, 3312599340u, 2231560239u, 2736260178u, 2100166993u), - SC(2561748569u, 2171003952u, 3930314290u, 4171544961u, 4084487200u, 1829909478u, 4190664042u, 1205662930u, 1332053018u, 3102835265u, 2758716514u, 3094681405u, 890009818u, 1835725787u, 3657145276u, 2012429206u), - SC(1490727773u, 2663703693u, 1786667419u, 3911642156u, 1173781475u, 1032437218u, 949369190u, 3379245680u, 3855657643u, 102309733u, 3862169655u, 1953708469u, 2899532678u, 2185103023u, 2246792392u, 2140300644u), - SC(1105179994u, 3403119551u, 2151897995u, 2133026531u, 4095632628u, 1958582421u, 3756551819u, 1353448323u, 343568827u, 940163873u, 3647008605u, 2675342302u, 2020863909u, 3314608025u, 3678853306u, 2350764749u), - SC(3610890660u, 7527132u, 3948519712u, 999155044u, 1566318108u, 1592356541u, 1395933920u, 3725362820u, 1628394109u, 2361449910u, 3407340106u, 1370203307u, 1521539242u, 166450716u, 1562824595u, 815891091u), - SC(4169640806u, 3985781662u, 2412370154u, 452406588u, 105016225u, 176939651u, 3796204183u, 875428687u, 2497589429u, 82221910u, 4277856341u, 1375558239u, 286683641u, 3316069361u, 519521869u, 2295715438u) -}, -{ - SC(1272080061u, 1249052793u, 3406223580u, 3180222548u, 3305857569u, 3627944464u, 989639337u, 2790050407u, 2758101533u, 2203734512u, 1518825984u, 392742217u, 2425492197u, 2028188113u, 3750975833u, 2472872035u), - SC(23055961u, 3145183377u, 2430976923u, 2926141735u, 1297155725u, 3931229778u, 1820665319u, 2985180446u, 3042883880u, 2460902302u, 3663963302u, 4048537328u, 3995357361u, 2497655514u, 2584741032u, 1771542440u), - SC(3555045486u, 1984442910u, 1340694232u, 3778110580u, 1134128670u, 754930307u, 645413801u, 419876731u, 718672506u, 2655370853u, 650960778u, 1175245889u, 3468383881u, 2671574337u, 44753822u, 3359158981u), - SC(289419990u, 2387037467u, 2851881154u, 4063189789u, 1829943773u, 2629576813u, 942097665u, 562844855u, 2647906183u, 117874787u, 202211775u, 3519990636u, 3082138694u, 1836881245u, 583992800u, 2183831281u), - SC(2721107251u, 1807232970u, 3202569269u, 3708638735u, 3532027994u, 4114767065u, 2764680156u, 135914892u, 1473879964u, 2935607101u, 4201045944u, 3202280567u, 3793176244u, 41830505u, 969791663u, 1519485648u), - SC(1497249350u, 1416277963u, 4236912956u, 1827689230u, 1595876921u, 792380080u, 2973128767u, 43523726u, 365213078u, 1703541227u, 1608568996u, 2447861933u, 4236202627u, 2270952660u, 996772411u, 1327926083u), - SC(930257564u, 986864131u, 3788206015u, 4282936823u, 3575152799u, 1711906087u, 3523467955u, 1026809541u, 3754676659u, 126901401u, 34761162u, 674497989u, 546239979u, 3916171265u, 4169565745u, 1773808675u), - SC(1188611875u, 4038625723u, 846346399u, 3124471166u, 3540873247u, 133640400u, 3354116939u, 2182269853u, 3158440321u, 538434017u, 508437111u, 2461460484u, 1662547818u, 3578959375u, 209001526u, 3335522863u), - SC(4264155336u, 4248354463u, 3273048757u, 2876562537u, 4290560912u, 509206354u, 1722430555u, 1796475043u, 864985283u, 4161684480u, 1401260098u, 2472895218u, 2342429930u, 827590760u, 300446032u, 2313806596u), - SC(2581459341u, 3429172659u, 2024065972u, 4099664542u, 1148350145u, 3444652410u, 3577141975u, 2981349935u, 4203645620u, 3053918552u, 3258443245u, 1577847138u, 1635931506u, 873577721u, 2391948063u, 3880308298u), - SC(348781524u, 168814463u, 525438717u, 333282992u, 3413546488u, 563982782u, 3571937262u, 2168075485u, 2567967190u, 4135534212u, 2773230423u, 2560090101u, 4070935767u, 1086323696u, 2826348049u, 1398744384u), - SC(1019826995u, 663251023u, 3152709102u, 4103744231u, 1372971676u, 1214523997u, 1159949230u, 2703418845u, 786011241u, 2156179212u, 1156040729u, 3454726929u, 1928366760u, 4000343119u, 4288863167u, 3214674902u), - SC(2681260382u, 4128008241u, 2510236484u, 1511367526u, 1684226652u, 979685907u, 2954161581u, 3173181201u, 2348267479u, 1347783270u, 1149362033u, 739573388u, 2484197607u, 335176176u, 4239049161u, 739872951u), - SC(2990421330u, 2634202447u, 3179573376u, 2783566953u, 2521510477u, 3781882024u, 2239710944u, 2912891640u, 4089020966u, 4152247187u, 3694477470u, 1764138981u, 2507816564u, 3857045441u, 3960587447u, 1062920229u), - SC(2607237939u, 3082469982u, 2290705462u, 3066564076u, 3196897175u, 4248068159u, 2751492888u, 1096521131u, 1350638971u, 3209282660u, 3725272910u, 717966828u, 1468400702u, 1807609199u, 332456241u, 3283231722u), - SC(752680913u, 2889161941u, 555836002u, 2587892579u, 793746532u, 2681266768u, 719050347u, 3803221u, 1422540107u, 1615046554u, 1724888503u, 923959013u, 3231965435u, 2753642578u, 1839210672u, 3344430910u) -}, -{ - SC(35118683u, 172484830u, 3416100291u, 3700412376u, 540823883u, 3117923166u, 4211300427u, 2853939967u, 3346783680u, 988896867u, 2435731911u, 431849862u, 1744411117u, 2614624696u, 297543835u, 4045956333u), - SC(2040009399u, 3617093372u, 1922089948u, 419196583u, 488784755u, 779735420u, 2537006207u, 704283906u, 1092151363u, 2578829348u, 2820670194u, 2121866485u, 3135057501u, 2561548080u, 1838738028u, 3520705790u), - SC(2347233873u, 2021920507u, 3747005552u, 3302704092u, 1421533666u, 2091592699u, 3349570591u, 3813605549u, 115030445u, 3350012162u, 2428670067u, 3833734570u, 1834087037u, 3648785167u, 3795974654u, 230561124u), - SC(3166315679u, 1499753232u, 1332394568u, 512254231u, 3188709397u, 2787249743u, 4120940214u, 2887173650u, 3906489413u, 2295240998u, 2578634494u, 1588397589u, 1261609842u, 547227344u, 3285763119u, 2699176838u), - SC(2920964533u, 3740093834u, 2438698186u, 1924062654u, 745692322u, 2251363856u, 1198363872u, 1945834517u, 3791006786u, 4021475876u, 1202959856u, 137650558u, 3764418806u, 2028729507u, 3549185474u, 4085572018u), - SC(2715838951u, 1959655040u, 1103474341u, 961883214u, 3220165814u, 946461598u, 3310562057u, 3895921046u, 3423737504u, 3466676673u, 4053794032u, 4003999722u, 704282430u, 186242539u, 1929875533u, 2743489242u), - SC(3863164996u, 1689760206u, 3183192577u, 2929742795u, 2741898431u, 3788088914u, 2356234821u, 7039846u, 36640443u, 397902308u, 1207730645u, 450227359u, 3243815017u, 2084858847u, 1390053102u, 1800322698u), - SC(2899288970u, 284742850u, 4164169257u, 657423444u, 1943078242u, 2187671316u, 2338824812u, 1463135135u, 2886625321u, 272841068u, 3193451269u, 275059886u, 893727404u, 1588413844u, 3713690958u, 858582046u), - SC(220208151u, 2716463025u, 2076296789u, 1220608226u, 1158026410u, 3025895717u, 2670689841u, 80726308u, 1182245224u, 514737744u, 1549626516u, 2794996864u, 1140029757u, 2873715616u, 2877687374u, 2336796195u), - SC(1712499527u, 3009254442u, 159655935u, 3126441867u, 4265886590u, 3094626983u, 2035167860u, 2311303989u, 3444838362u, 2596170866u, 3801673179u, 1837914686u, 3231006463u, 1247923284u, 584065013u, 4147287941u), - SC(900839097u, 216650153u, 2150488455u, 1952211291u, 2276027011u, 3518121564u, 3433005808u, 477320989u, 4007917006u, 2860081630u, 3686269191u, 921073036u, 3922496269u, 1487331039u, 3974930220u, 2054391386u), - SC(3348685354u, 1508268709u, 1715972206u, 4188610176u, 2563479521u, 2178972493u, 3288192040u, 3754144178u, 1173914019u, 454089507u, 3398639886u, 574196980u, 135948897u, 105476021u, 2877469782u, 2140775314u), - SC(60661201u, 2505799644u, 1330476086u, 2641855913u, 3370908611u, 3545887069u, 2369313011u, 278373074u, 1677987717u, 2174519857u, 2497481396u, 1568231376u, 3671812134u, 1893623337u, 1526376990u, 3328774765u), - SC(2836826686u, 3566150450u, 1220364883u, 3711427451u, 3528155780u, 2723292785u, 3326692341u, 2222164977u, 1858144237u, 1869912598u, 665154087u, 1299959695u, 2415334423u, 2100885199u, 1677986979u, 848478053u), - SC(2293836559u, 1740853836u, 1031472293u, 3209927466u, 2722427870u, 1686533972u, 3134525842u, 43165427u, 4133377528u, 4179858803u, 3614537390u, 3380004165u, 2699323023u, 2351902646u, 3408173486u, 2494501357u), - SC(1820258417u, 3371479244u, 1743152481u, 953496909u, 4267482844u, 97428203u, 2755286865u, 830318058u, 1082737155u, 2096588114u, 869939293u, 1138867599u, 3414628151u, 3300388932u, 2755674787u, 886356844u) -}, -{ - SC(1981590337u, 957784565u, 3778147127u, 3909235993u, 1637480329u, 2280601867u, 1059949562u, 2968107974u, 4043469535u, 4159249472u, 895867525u, 402468881u, 3186079639u, 86430659u, 4027560590u, 4067278225u), - SC(174847206u, 2629171882u, 2333280466u, 3666750170u, 1365991192u, 1932613341u, 769674425u, 2870677148u, 3091982589u, 717533940u, 691292429u, 746447527u, 2346750954u, 2424023836u, 2489851473u, 1000862947u), - SC(1294470925u, 420276534u, 18534679u, 2910625938u, 3592407247u, 3676292946u, 91786365u, 2630448437u, 4060747756u, 3372072880u, 766751258u, 2899531047u, 631745164u, 3523898915u, 3168717447u, 2801541394u), - SC(4228902076u, 3340600279u, 3364406353u, 4167190351u, 39030410u, 2148305555u, 4106423272u, 4019775241u, 1048613489u, 896239533u, 2278643848u, 649090509u, 1858593869u, 1017004108u, 2725922618u, 2362479567u), - SC(3279186701u, 4095625861u, 3191586341u, 3252046177u, 4161721618u, 2329134038u, 751155705u, 2989611709u, 942304573u, 3648059604u, 2883823407u, 1492175829u, 54393633u, 3106238944u, 429976962u, 1435978615u), - SC(3849622377u, 2984399872u, 690474125u, 61954906u, 3671421106u, 3429544548u, 2830056933u, 4242121816u, 952897126u, 3854066003u, 462125754u, 3261473627u, 4248077119u, 2601130223u, 2596495819u, 1081964366u), - SC(3544595842u, 126020837u, 2577264196u, 3433073867u, 496013073u, 2132398305u, 2482253446u, 1347711182u, 3954364337u, 261394336u, 1107608476u, 3443266300u, 104305688u, 870955527u, 3446753045u, 646876293u), - SC(164137956u, 1354687087u, 347069906u, 2162313159u, 2097666782u, 2177194309u, 1083298213u, 1791764705u, 445921337u, 2034078155u, 2254058003u, 1297019339u, 2457505957u, 3923390662u, 3364713163u, 2092921u), - SC(2010686846u, 2180989257u, 2265174665u, 208481647u, 547071646u, 2570387552u, 227431381u, 3946252713u, 1802054573u, 2876468168u, 3435214380u, 619729504u, 96719536u, 601795828u, 1679578869u, 3266813859u), - SC(1689091897u, 2850488954u, 85895902u, 2363909390u, 557966933u, 189022184u, 4135255025u, 2090271113u, 2804992462u, 2897353835u, 3129164865u, 2671868525u, 1204434986u, 2421048110u, 1069687644u, 573230363u), - SC(1864118934u, 1742326766u, 130305247u, 3848358018u, 448383585u, 389136808u, 676464280u, 133776905u, 3973153497u, 15653017u, 4189644276u, 1910866015u, 4017185152u, 3100723612u, 137322886u, 3499754296u), - SC(2165760230u, 1978556390u, 4038887110u, 3280144759u, 2755863878u, 1292009146u, 4196675347u, 2883653205u, 2360229279u, 2940095236u, 4183119698u, 122598923u, 483221264u, 2336117478u, 1200036442u, 1470973u), - SC(22625049u, 2110942382u, 3865539390u, 3568657648u, 4280364838u, 467068956u, 1638706151u, 934686603u, 1016938107u, 1378881668u, 2052861738u, 969631954u, 3114829317u, 2704079673u, 4202235721u, 1896331078u), - SC(1272877817u, 322275610u, 2048255u, 3828419764u, 283292018u, 656555904u, 1730883898u, 407673382u, 3259565233u, 3319282763u, 829721223u, 1466466546u, 121051626u, 1142159685u, 3894622225u, 1384264827u), - SC(3763136398u, 3055118026u, 3433748869u, 930030556u, 2135841826u, 2075894041u, 2845381068u, 3086878324u, 257833966u, 160279206u, 524657374u, 1855318297u, 1760771791u, 1248968332u, 2414205221u, 2464430473u), - SC(3809273981u, 900900763u, 2895572448u, 3283497701u, 1349213062u, 580961411u, 3299214221u, 3628519825u, 3643683404u, 3319374656u, 3868217535u, 427844533u, 3841842588u, 2749654710u, 2681210929u, 1051800659u) -}, -{ - SC(1622151271u, 634353693u, 3884689189u, 1079019159u, 1060108012u, 22091029u, 115446660u, 534633082u, 1649201031u, 4042006969u, 137296836u, 1833810040u, 1562442638u, 3756418044u, 1181092791u, 160208619u), - SC(1041667920u, 3037209402u, 1477404634u, 1440610569u, 2797465015u, 2054982250u, 3391499235u, 3605494419u, 3639198696u, 1933432209u, 1915711520u, 2741088986u, 3869566747u, 1879175626u, 717801628u, 458685614u), - SC(2768417957u, 2138940313u, 1896672548u, 1414723957u, 827016389u, 745281061u, 1045174332u, 3577682097u, 2169383377u, 1730416479u, 712654956u, 3155052928u, 1776219501u, 3353461099u, 711436547u, 1497369655u), - SC(1896697766u, 3621973902u, 926548253u, 4069206549u, 2297004301u, 3251063401u, 993943014u, 1270589313u, 3281589988u, 588955836u, 2429665887u, 1734915238u, 3409902793u, 2578722241u, 654727507u, 3216225031u), - SC(2536890957u, 2554531636u, 2109372546u, 2649000077u, 358086224u, 3391808161u, 1211714614u, 2605265326u, 2606629887u, 206756474u, 1092207840u, 3362434504u, 3945886373u, 4232252600u, 2886868947u, 3532954370u), - SC(65718672u, 4071991225u, 2060698395u, 2198173427u, 3957878549u, 4022831630u, 3461473682u, 419893418u, 779469249u, 2019627177u, 2019172804u, 3609556656u, 2681069216u, 2978123659u, 1249817695u, 2366599297u), - SC(2811735153u, 3049657771u, 1390752797u, 1411409994u, 2127695318u, 3083850245u, 787626821u, 1929564189u, 855492837u, 4008216334u, 1809444437u, 2182869717u, 813270534u, 2247412174u, 1161082081u, 1381922858u), - SC(3920648469u, 503487540u, 2083562080u, 327383264u, 2785608988u, 867359286u, 1036950980u, 431152821u, 1419040671u, 2665230771u, 2455357484u, 351717207u, 3187759581u, 3348793239u, 2511298896u, 1213040259u), - SC(2396309679u, 670711827u, 2849604206u, 3201137057u, 818618388u, 2531623890u, 3805810347u, 1463443182u, 79508933u, 3480790940u, 3579218280u, 263259195u, 3368747551u, 3044188079u, 1352272344u, 3090026690u), - SC(337838342u, 789695791u, 185502398u, 1517725636u, 783544345u, 2877621235u, 2946546356u, 1215973672u, 1208860651u, 725001171u, 1289736233u, 3756237869u, 1654092362u, 364807179u, 4279861158u, 4016003402u), - SC(1113567525u, 3780565260u, 836674522u, 1827009520u, 756906197u, 2663480421u, 3902552087u, 3507352398u, 774509259u, 224530498u, 2361577079u, 3744385228u, 3961162378u, 2586454589u, 3040342450u, 332039963u), - SC(3041171145u, 1474749273u, 2282851768u, 649990155u, 2952549483u, 1360702019u, 1809905451u, 544396952u, 68636355u, 2878101257u, 1478326650u, 2199663643u, 320705780u, 628185476u, 2087425498u, 3828181698u), - SC(3988280964u, 459019854u, 4007245269u, 1946776277u, 125932076u, 3922945473u, 608655237u, 759981570u, 1458494773u, 3686363491u, 3746534866u, 3692063331u, 290340676u, 486223220u, 3313127929u, 2280570810u), - SC(233319658u, 3886064320u, 853251650u, 1236563554u, 538386922u, 1967845333u, 3003439052u, 2872751142u, 150287328u, 2176354561u, 3956114759u, 3858039u, 2003618785u, 4212993191u, 2956509701u, 3196752221u), - SC(2121593903u, 3906201458u, 1137774967u, 3978600103u, 780659717u, 3484790562u, 769856015u, 36405780u, 695767695u, 3397748350u, 3377872749u, 1577340836u, 783581424u, 3804923626u, 2896998870u, 1723843622u), - SC(2572703671u, 2154230449u, 1195305676u, 4208655231u, 922600921u, 448134411u, 986012643u, 2442352758u, 1662902878u, 1367546113u, 2863017129u, 59878996u, 2111442975u, 648834983u, 865532037u, 1000323350u) -}, -{ - SC(2802315204u, 2299944053u, 2128407100u, 3463617348u, 2448441666u, 1070000794u, 1884246751u, 210372176u, 4075251068u, 1818330260u, 3223083664u, 3496698459u, 3376508259u, 4156094473u, 3718580079u, 1962552466u), - SC(3866141502u, 1978128229u, 2646349807u, 2688968712u, 1012393569u, 2539553175u, 2230158790u, 2206981245u, 3747509223u, 1243575365u, 3510697084u, 4007723917u, 859148499u, 1713821117u, 199178654u, 2644187203u), - SC(1964672019u, 297703434u, 1518880848u, 3373273121u, 959853764u, 2251122694u, 723413077u, 800337307u, 648287930u, 2947400245u, 1113383775u, 3610122168u, 1829970570u, 2892296971u, 1554744636u, 494969279u), - SC(4031050415u, 1835549397u, 2490029791u, 1131956513u, 1204048760u, 1914510905u, 3436953651u, 3943499769u, 1759802551u, 3820069122u, 4025269834u, 2717988015u, 2671631612u, 1159803272u, 1951365142u, 4085381442u), - SC(606110736u, 4064038873u, 70240913u, 2494945854u, 3729188113u, 2063877878u, 3912150605u, 3215847250u, 2977890044u, 3389766053u, 356841724u, 356991784u, 2228722660u, 3145515298u, 2594559598u, 1158432841u), - SC(1794017518u, 25183950u, 1671020817u, 785574353u, 95301808u, 1715172822u, 2718673424u, 1470113919u, 1142251437u, 2499778479u, 4281783303u, 1325560741u, 2913926884u, 3804531669u, 3139007483u, 1406557472u), - SC(2970751291u, 2450850294u, 545967636u, 1959629374u, 3303894193u, 455065073u, 41447235u, 1831795469u, 3594460859u, 4077235761u, 722461030u, 598330044u, 192707446u, 509790368u, 1051867275u, 1446366645u), - SC(1959543921u, 1887295052u, 3154544834u, 487969766u, 2252004301u, 996805128u, 2018864848u, 597352487u, 1136669046u, 533675042u, 981364938u, 2653382923u, 1408807893u, 2742559841u, 1833041360u, 1912794731u), - SC(2721713526u, 3549551325u, 601974093u, 2790584575u, 3951999363u, 4215366345u, 2845142034u, 4218934731u, 1726020765u, 823952138u, 3809833u, 4233069287u, 1129914456u, 1399496316u, 1915356031u, 4169077603u), - SC(3926695685u, 1849292395u, 1522137139u, 1552827989u, 4109112844u, 2060253220u, 2853920191u, 801241282u, 3422535773u, 1693187125u, 2113050221u, 2708536698u, 2777027446u, 4174902187u, 1811957361u, 3772547370u), - SC(3930825929u, 747327770u, 2505687587u, 2880650279u, 583976081u, 3834434841u, 1957901663u, 82062519u, 1246607062u, 4096185443u, 1298601955u, 3551964017u, 2293924654u, 2316870880u, 1326950040u, 3135743003u), - SC(2476396705u, 2790106263u, 443544224u, 2802435205u, 819417773u, 177556618u, 4130535785u, 2505448107u, 2591437865u, 1610510350u, 3815578981u, 4114533339u, 2461835810u, 3856846001u, 1439644255u, 3343979676u), - SC(4065627430u, 2927818196u, 950831561u, 4171626868u, 1177734694u, 150634338u, 2487656862u, 796691698u, 2119716392u, 2975402883u, 833495592u, 2179672277u, 346833760u, 3054174076u, 3573945862u, 3318693908u), - SC(2752867821u, 4203551149u, 1685153083u, 1110714758u, 1962211454u, 2837810663u, 1792364454u, 4089022191u, 3967274249u, 192406218u, 3350506767u, 1577386058u, 1497165592u, 1817646171u, 1066733732u, 617241273u), - SC(307712584u, 3903562077u, 681601120u, 3047177738u, 2486055863u, 3842609448u, 3660507009u, 2553494609u, 3174736607u, 3482954246u, 1496988826u, 1025695462u, 3184242644u, 1095387068u, 949053977u, 2083266597u), - SC(3022399010u, 1538609936u, 2420072227u, 990220729u, 2914167049u, 3768364162u, 1346299210u, 1681335666u, 2574961060u, 4053930867u, 303191498u, 2606902764u, 726562386u, 2306023171u, 939416980u, 608183941u) -}, -{ - SC(1862109024u, 2933191225u, 198801920u, 104305860u, 4011109577u, 4122560610u, 1283427153u, 1072910968u, 1957473321u, 1766609671u, 2854361911u, 4075423370u, 2724854995u, 3336067759u, 2831739585u, 400030103u), - SC(3665137971u, 362515859u, 3613170351u, 1634568159u, 2407386812u, 2769867978u, 3661728638u, 966943982u, 2329232814u, 928287686u, 386060431u, 2380767940u, 993235698u, 994357638u, 4262826729u, 789587319u), - SC(700222805u, 4205189715u, 1681820282u, 2408317852u, 3145763515u, 149703318u, 2996102375u, 2778856747u, 1243021847u, 118692771u, 660320701u, 2037909966u, 3471407521u, 3539034550u, 2338530850u, 798101514u), - SC(202761792u, 3072251152u, 936980226u, 2112028598u, 55725596u, 545941282u, 2866544613u, 2541609642u, 2986914411u, 250525398u, 494419489u, 904338436u, 448237071u, 2519815520u, 3547503723u, 3479815920u), - SC(2591263445u, 2313710919u, 2225850186u, 2907469855u, 1923973028u, 2439332754u, 1359667863u, 1147453888u, 591668157u, 1802961428u, 2115337573u, 3814501239u, 1652114003u, 3286770823u, 2320492326u, 1627762005u), - SC(915583786u, 1541647557u, 857793588u, 1120457139u, 593298997u, 1235522530u, 3835902793u, 4029633796u, 2892088014u, 950803214u, 2067553664u, 3466102617u, 417988445u, 1721721291u, 2995105031u, 1833135847u), - SC(3713015457u, 984220366u, 1636921821u, 69668826u, 2853588756u, 3372417728u, 1514016965u, 3165630303u, 549067200u, 2237752955u, 3528219045u, 2819816242u, 2536477233u, 430232621u, 1219272797u, 2682238494u), - SC(4158909478u, 628504302u, 1961569314u, 3701318609u, 1298978065u, 2797817112u, 2778611026u, 2986972418u, 2728592083u, 1350107926u, 261737783u, 1726357156u, 2342206098u, 3937750792u, 3688276065u, 1598643893u), - SC(673033353u, 989709407u, 1304069795u, 4233856570u, 603282839u, 3834722266u, 3349356388u, 2690783748u, 318351191u, 3370905692u, 2347975280u, 2009518842u, 2234183321u, 2940030960u, 2623873751u, 1542240694u), - SC(2380479990u, 2443937714u, 165899369u, 1753008008u, 3688956092u, 2346743686u, 143829732u, 3830274100u, 446444093u, 1705814492u, 2316415254u, 1337109896u, 3093454689u, 1928322219u, 2296006624u, 2093435857u), - SC(4072133379u, 1665275533u, 1975626640u, 3338948757u, 3639875020u, 2527617364u, 2537708733u, 3825629008u, 3956434656u, 2047924528u, 2149850378u, 563001677u, 1364815414u, 2503665164u, 637530147u, 630327427u), - SC(2169035971u, 3667715128u, 133026623u, 1213164483u, 1858042667u, 1566345391u, 3257221880u, 1553218197u, 1494901497u, 2543246705u, 3407410762u, 149097838u, 2595763051u, 3921913476u, 3975713216u, 1013875562u), - SC(4285039888u, 3972750160u, 2508056116u, 3828502305u, 1554885499u, 2478771653u, 3465835374u, 2839338634u, 936668484u, 3860842840u, 1796057260u, 539213045u, 1979230663u, 2637220243u, 3822691920u, 124051918u), - SC(4008482152u, 442930842u, 3844390262u, 1477377511u, 2570068482u, 380269897u, 3550124210u, 1507268577u, 1690622835u, 1216029693u, 2876552462u, 1409060125u, 862828291u, 1145788484u, 2966975851u, 3091998876u), - SC(992351977u, 3038251247u, 1125019979u, 3468273479u, 2933515034u, 2848650947u, 3581678949u, 3449520008u, 3870604714u, 2854135121u, 1257402460u, 1206940695u, 2996845551u, 725641056u, 3899090423u, 600507448u), - SC(1594814264u, 3363681343u, 1687711901u, 1220822433u, 2890970125u, 4169329849u, 1095390946u, 3969022672u, 2174219653u, 1940964660u, 1237339498u, 2965031440u, 1016584643u, 2590104317u, 4235803743u, 3748725935u) -}, -{ - SC(770670183u, 2030489407u, 913827766u, 28354808u, 2556411291u, 589717159u, 413516142u, 20574376u, 1695189435u, 3750527782u, 3546610407u, 1435363367u, 2770958348u, 2608593137u, 3331479090u, 2086258508u), - SC(386282669u, 3729286075u, 814752142u, 1413230862u, 2616133966u, 2483044279u, 1602859126u, 1971292416u, 3070813417u, 3451205972u, 735409142u, 4007950155u, 2905395594u, 2869625175u, 3709680291u, 2952203732u), - SC(3404816958u, 563114856u, 2100979818u, 2101934521u, 2503989815u, 1063833326u, 1723163772u, 3130704072u, 2515274210u, 1396315966u, 393457735u, 2691705207u, 828877164u, 3349330754u, 122605524u, 2602269000u), - SC(3709941627u, 592327138u, 2051205206u, 810649302u, 871212350u, 1541388603u, 4163983787u, 2631105522u, 665062813u, 2612020092u, 3229205070u, 3819479307u, 3310127863u, 1843015221u, 2875318880u, 3723951791u), - SC(1567440489u, 946197176u, 1275093448u, 4236630568u, 3990268727u, 196525149u, 15396621u, 1859637416u, 3138749279u, 3859238173u, 3227404352u, 2720346799u, 3006927153u, 2147957966u, 397899810u, 870180302u), - SC(1039540230u, 838590221u, 2330450212u, 923346890u, 4067788704u, 3619481496u, 3864357516u, 1659963629u, 3299501842u, 1079788777u, 949881347u, 2502746723u, 3228809289u, 247884983u, 3118597092u, 302086001u), - SC(3566621623u, 1671359399u, 3923258138u, 1638982085u, 325268348u, 4006635798u, 1207442469u, 3002539627u, 4047574291u, 2011583803u, 1713508996u, 1060703309u, 4012225302u, 3776068377u, 2784459927u, 3025510009u), - SC(4215947449u, 1997878089u, 1026649407u, 646510252u, 850804277u, 1871694871u, 3390738440u, 3114862405u, 3567086852u, 195428920u, 1556755650u, 1851670178u, 2207687769u, 3388294264u, 4058594964u, 4248126948u), - SC(45480372u, 1361999478u, 2195192123u, 956464540u, 1294436548u, 3045580134u, 2390633033u, 757048237u, 1350268583u, 862465366u, 1780970485u, 3285033794u, 559081924u, 163710122u, 3170983363u, 2626972150u), - SC(90053239u, 741607095u, 3003181022u, 3546281037u, 1996206866u, 2019149839u, 2216417072u, 1170259974u, 4159879668u, 130215053u, 2605146665u, 3967236653u, 1930867601u, 2409157952u, 3775975830u, 1489883331u), - SC(40478381u, 3873592210u, 35609037u, 272986081u, 3051595606u, 504620408u, 1019656134u, 250693036u, 942133950u, 156032543u, 3738710122u, 1712961843u, 2888111563u, 1171258741u, 645705716u, 511104714u), - SC(239657447u, 2278853730u, 2391081998u, 746810345u, 3484552464u, 1369592268u, 2655434121u, 1213868536u, 2934523673u, 3058111393u, 4281279490u, 3966376385u, 1307651904u, 1645528218u, 3652190772u, 1126527756u), - SC(123809694u, 110218531u, 117547539u, 2035819815u, 3596140063u, 1382818318u, 3664758070u, 3019339789u, 2719299822u, 3892472009u, 2876096109u, 412140786u, 2578091481u, 2196346764u, 3068803053u, 1395690512u), - SC(880155357u, 791542602u, 112062960u, 2175792069u, 531560395u, 3155859615u, 1042526138u, 680268271u, 1355330482u, 2485441305u, 148200464u, 964096786u, 3215229166u, 2660485876u, 3076499838u, 353883041u), - SC(2388114644u, 1552848777u, 1649071283u, 2325568488u, 3165393822u, 2695611152u, 2713875122u, 898903657u, 2377088931u, 1138573339u, 3366910425u, 3238180215u, 676550680u, 1043832292u, 1583145576u, 3925456200u), - SC(3116588854u, 731097341u, 35427079u, 152855963u, 655343116u, 2522648040u, 3048497137u, 3838372571u, 777022751u, 2851975543u, 235569549u, 3020787559u, 727642795u, 120522014u, 2406411931u, 4235508200u) -}, -{ - SC(2533741935u, 4150033708u, 3133949860u, 2798619408u, 806119564u, 266064305u, 1385120185u, 1697466874u, 3309272849u, 2305765083u, 4237655511u, 751372374u, 3319766406u, 1139025033u, 1880631363u, 2216696728u), - SC(531691765u, 3457214584u, 2884896024u, 292273176u, 250051106u, 4144042126u, 176967583u, 4132839552u, 2406879878u, 872979134u, 3029052987u, 2283805120u, 2613859206u, 553294045u, 1245122721u, 3840523078u), - SC(1249934121u, 993078438u, 2897493833u, 1681305911u, 57100476u, 365202891u, 2111004277u, 4247410280u, 1628827737u, 3793711703u, 3364391257u, 3510640052u, 3346661510u, 885793286u, 3903378618u, 356572920u), - SC(680178688u, 1413780236u, 356581993u, 2539116542u, 3091268161u, 952393142u, 3601213640u, 3759147734u, 3201912600u, 2029303323u, 3233109971u, 3469579370u, 4191225303u, 2727922547u, 4241219026u, 1108397896u), - SC(581424072u, 2231376178u, 2556335427u, 507971440u, 4133814232u, 3831053002u, 2090051536u, 2682264467u, 1696017056u, 2590078109u, 3496563305u, 1242917226u, 2491190071u, 2058502209u, 3614091208u, 50680464u), - SC(1148224059u, 3153210519u, 1979896166u, 3699990000u, 2774705970u, 4177914488u, 1097495713u, 3943642621u, 28438271u, 1936652546u, 2951976972u, 917798112u, 3345031007u, 3414386063u, 2086388059u, 3336786964u), - SC(3207879285u, 3245056275u, 2753912038u, 3444068917u, 3619101580u, 301796681u, 469710494u, 37792426u, 2324375961u, 3765435021u, 2308122387u, 186365381u, 1748483921u, 2929955002u, 2507797221u, 1450081310u), - SC(2628113752u, 657975440u, 4188527535u, 3642824575u, 1167948061u, 570005820u, 1209373950u, 3114955026u, 2156903999u, 3426648275u, 258877187u, 4116394669u, 3424577769u, 1876755024u, 3610721045u, 137959590u), - SC(1295746957u, 2893879416u, 2731249393u, 43796623u, 1509060380u, 3580712054u, 2063633991u, 246915731u, 245935590u, 2758600953u, 1174591025u, 3759438209u, 874703696u, 3900497366u, 2032803558u, 741576512u), - SC(737124188u, 2899307081u, 1769647158u, 617077642u, 1659909664u, 278863054u, 4232490889u, 625515113u, 3013249184u, 3621100329u, 3078044036u, 1407642415u, 2069197169u, 551433765u, 2836890938u, 3978268035u), - SC(1956698332u, 1096426127u, 1006277939u, 3889489220u, 4030026180u, 3579514159u, 4250029335u, 2203857202u, 3553085216u, 3293255490u, 1237506477u, 1050435484u, 3944172449u, 3169627003u, 1477888937u, 2421667267u), - SC(867315816u, 669003983u, 4033294932u, 3994270030u, 1836283861u, 4220295811u, 3981502955u, 1254544883u, 2953929766u, 3399467612u, 2767815501u, 1837724890u, 359769422u, 525366934u, 2275330754u, 1596174485u), - SC(2757381304u, 618201396u, 1587888624u, 1754675322u, 309402992u, 1862772816u, 1766295424u, 776578164u, 3139660404u, 2518031939u, 4144540600u, 2162413735u, 2788510259u, 3413511116u, 1497090248u, 130610227u), - SC(4221771265u, 792248867u, 928054053u, 140258355u, 1340321712u, 917602285u, 1586319677u, 1429062327u, 3604542914u, 1952132240u, 3586261493u, 1380920077u, 1224870626u, 1321897505u, 3109874655u, 2938496454u), - SC(2321281375u, 3760646295u, 420407446u, 4154009512u, 2825227525u, 4188075686u, 2041350513u, 1285713851u, 1670924786u, 1104780793u, 3524777730u, 1315724274u, 2655303597u, 1675669649u, 3173211461u, 1286635196u), - SC(1138423224u, 1326909178u, 3451890502u, 3840823688u, 3093921534u, 4140902218u, 2007985143u, 2980979703u, 3539657192u, 1914000311u, 3861983402u, 1995841174u, 2739822780u, 4269529997u, 1752802206u, 3674790048u) -}, -{ - SC(1529327297u, 3326406825u, 3128910982u, 2593525414u, 42156971u, 3661621938u, 1244490461u, 1967679138u, 1025455708u, 720268318u, 2871990393u, 1117479541u, 1562094725u, 697888549u, 2324777980u, 3391621955u), - SC(1194208642u, 570517940u, 3796480395u, 3996975496u, 1891180536u, 2012913508u, 2586036803u, 2779419249u, 2424448764u, 654631266u, 3378681847u, 1794600320u, 850887774u, 2610529382u, 3440406071u, 442629809u), - SC(3922776395u, 1021134129u, 4161953411u, 3695042522u, 416696694u, 3141869998u, 2208946602u, 2248782897u, 3791212714u, 2183092330u, 2442693998u, 3821686193u, 359924765u, 1313892u, 732537261u, 3441185514u), - SC(3832647873u, 4126820624u, 1633739521u, 1776853127u, 1990846870u, 2931750872u, 723350088u, 2100866125u, 1353427778u, 3735236517u, 2936890827u, 1037652209u, 3538242522u, 1205440750u, 2681851721u, 3428134171u), - SC(3715940368u, 3100195993u, 139205042u, 933899119u, 508675941u, 2073279390u, 3838896736u, 762162827u, 2670162920u, 363468845u, 4142816880u, 2331633868u, 1859516459u, 2571514805u, 1415575689u, 3310370398u), - SC(1850103477u, 2861511197u, 2158258814u, 1914352173u, 4112609179u, 1613408074u, 2229142795u, 2743410061u, 386541358u, 4131835227u, 238820765u, 350328321u, 796595210u, 325800094u, 1477199872u, 130087432u), - SC(3503083399u, 2168288449u, 3773780757u, 707691176u, 2640783803u, 600372304u, 3521490788u, 1266639681u, 3049849833u, 3696342843u, 1559948576u, 3113774976u, 2979720549u, 3508429388u, 1393959701u, 716360542u), - SC(2281167118u, 2404489970u, 874916137u, 3296730075u, 4266077966u, 1052198560u, 3487426822u, 379036992u, 918125804u, 3064034925u, 3007906638u, 2843799763u, 13395259u, 1525101299u, 3917909303u, 323214095u), - SC(4272733253u, 1134926458u, 1071872991u, 1594198106u, 2743911342u, 1759781849u, 3909986783u, 357998405u, 4054491364u, 588230484u, 3248723140u, 4206364217u, 407716541u, 1660843258u, 3535395038u, 735131513u), - SC(3679104282u, 2103136756u, 3192389130u, 3635496721u, 3762160259u, 813057806u, 1922167568u, 196643685u, 1370854030u, 2657803320u, 3197001343u, 2838705898u, 1256322653u, 3731470140u, 1658864516u, 4241135314u), - SC(4138122573u, 1064712956u, 1914688217u, 3980579663u, 234064841u, 1340868250u, 2408246134u, 2334390091u, 3574856083u, 4185747404u, 2592066932u, 72932352u, 1132443153u, 3084950430u, 2850577555u, 531426487u), - SC(2552518597u, 1814188589u, 3771797408u, 1688271073u, 1392417060u, 1864411028u, 2178912172u, 2411760311u, 772279774u, 2791980611u, 2940533230u, 3149501999u, 370215731u, 2968115262u, 942881455u, 2310941126u), - SC(751991992u, 3546574605u, 2773077774u, 2498170045u, 3288367839u, 3030402134u, 1196921751u, 3823185297u, 3245569995u, 3802879953u, 493640893u, 3321821285u, 1141758187u, 3411864659u, 306737884u, 2761165281u), - SC(1865741334u, 706283811u, 2318095713u, 1419794148u, 2504644337u, 1922210484u, 263491957u, 3084520625u, 705689999u, 2554474009u, 3818190952u, 2133768662u, 3690402460u, 3381523320u, 831084441u, 1146769937u), - SC(831531101u, 3633896804u, 1996958159u, 636851001u, 4007892767u, 380666960u, 2826737942u, 4021398986u, 1411635481u, 515161969u, 4199924051u, 371116192u, 1868116156u, 397223417u, 972171737u, 2331326509u), - SC(974457928u, 3569708670u, 2643527780u, 699675161u, 2627045402u, 3565281489u, 1504374419u, 2979851122u, 688725044u, 4064400308u, 4156347928u, 4119156622u, 2098702491u, 2615488234u, 1090654007u, 3790938610u) -}, -{ - SC(1397828129u, 1248172308u, 2194412927u, 3657598991u, 2085616102u, 1202270518u, 3253032741u, 2632389423u, 1019922267u, 332153082u, 1521672215u, 2163564334u, 3102124007u, 582149809u, 329417494u, 188520915u), - SC(2484409087u, 165527253u, 332794704u, 523511269u, 3524328119u, 4077596669u, 3681267981u, 2969751460u, 3456338723u, 628364217u, 4089156990u, 1135761223u, 1241363911u, 2843043452u, 1927162020u, 1187988850u), - SC(3424784620u, 4001207648u, 1967060425u, 33527184u, 588161341u, 2216089406u, 1194534688u, 3972415390u, 3430941953u, 3671974564u, 355464831u, 2638417624u, 987848314u, 3854256447u, 2513703271u, 847178398u), - SC(944122325u, 1095537200u, 1611102749u, 3845108718u, 3985128242u, 1188491807u, 3783427529u, 722821803u, 2594736624u, 4038805042u, 2146959275u, 3199724336u, 3631416672u, 3989329185u, 1113423723u, 925573746u), - SC(536468163u, 2790961065u, 141113925u, 985919057u, 2438788330u, 374449238u, 2980068000u, 621714839u, 2454037345u, 2810385667u, 3189321079u, 794373297u, 4178743943u, 2630861151u, 1229894711u, 2665151675u), - SC(71889345u, 3684655732u, 3834974630u, 40555081u, 3804280840u, 423207811u, 1620826812u, 3717508581u, 1813258849u, 713714932u, 491517868u, 2389605511u, 767769458u, 2826892693u, 3923819122u, 3331321015u), - SC(3333750894u, 150650636u, 3555142699u, 1161199649u, 3068475424u, 1509735584u, 1033908609u, 3073273527u, 3313105177u, 3410735718u, 2770838598u, 2161939200u, 1654309303u, 1247727621u, 4123284974u, 3218452135u), - SC(4107359918u, 3667881557u, 4099213325u, 905728122u, 3167924799u, 3731720507u, 1537227227u, 659110227u, 2101733778u, 2731849932u, 1103266972u, 887588276u, 2413509058u, 3876926094u, 2675347623u, 834362982u), - SC(3178393694u, 2636806389u, 1832500758u, 186297941u, 3662837586u, 3282938029u, 1064394039u, 2117567716u, 95811670u, 1968831533u, 3070787872u, 2658254448u, 3676980228u, 3909574788u, 2135784404u, 3803100103u), - SC(2624310917u, 420491519u, 3322620679u, 3357048697u, 614451586u, 1196461215u, 41516451u, 3256616699u, 3715883496u, 2257787428u, 2455669147u, 880443853u, 2246776764u, 3074399406u, 278369115u, 1177356599u), - SC(439711555u, 2231299488u, 1079942678u, 677737570u, 563039018u, 2032266501u, 3704274118u, 1877323449u, 2386821791u, 2066266240u, 2520835526u, 1611863315u, 3800297318u, 2553770190u, 1751820038u, 2175904420u), - SC(3515911639u, 4055231138u, 2717511782u, 6831543u, 3016647759u, 2007513585u, 1217171617u, 3815960975u, 2720128636u, 364849140u, 4285658094u, 4211508323u, 127732138u, 997100418u, 3152669382u, 146802488u), - SC(3082714386u, 513166810u, 2182067081u, 798923178u, 921230382u, 1956178560u, 883901335u, 4290259857u, 2290170782u, 3274942148u, 2025203706u, 2950735447u, 3706997198u, 979032741u, 1714061744u, 1756952042u), - SC(1785121933u, 665679939u, 3927612276u, 926826810u, 456860581u, 4247102861u, 1802871345u, 3111467239u, 2947918463u, 4090223916u, 2765919892u, 3848356305u, 2236940933u, 2379663516u, 2033761836u, 170415812u), - SC(723418419u, 3083992977u, 2885930256u, 4084559514u, 3550295439u, 795067132u, 3902666387u, 98659646u, 3559229619u, 3518103022u, 3093345450u, 3504265473u, 3135355783u, 1746911831u, 3896748938u, 1982334610u), - SC(4151598450u, 129451956u, 3923175367u, 306344029u, 336516292u, 3560777935u, 2695409605u, 934056748u, 4131395595u, 112767211u, 3377236273u, 797539933u, 516899453u, 2089210576u, 1999558205u, 4107023428u) -}, -{ - SC(87353816u, 3198238907u, 1232123158u, 3291424375u, 3695263554u, 2608617182u, 3798070797u, 3966302680u, 3847946128u, 278442153u, 3929504461u, 3056452729u, 3658519828u, 643043450u, 684101279u, 121314490u), - SC(4041434108u, 1283940781u, 3208791522u, 2974918612u, 861706326u, 3183082284u, 508820598u, 682206875u, 1177134745u, 1065833400u, 1830916342u, 1348337823u, 1877305145u, 2647094535u, 2714586296u, 2450197741u), - SC(2726369020u, 1580548584u, 150986819u, 369792970u, 2983651480u, 3064179956u, 3511715342u, 1538695618u, 3829066845u, 578378703u, 2038030944u, 3732775932u, 1174552062u, 2377418012u, 375009203u, 1203897576u), - SC(3480144388u, 847968760u, 3831609064u, 2454845771u, 827762235u, 3561019074u, 3068061128u, 2125290281u, 500142325u, 2613926927u, 908976630u, 461018064u, 1790330457u, 2138554260u, 3099515250u, 2668195629u), - SC(1153226571u, 752634643u, 4102962367u, 2953166708u, 3172028384u, 1546019245u, 73810680u, 2123706323u, 2289283451u, 1736737040u, 4246735980u, 196740994u, 886758605u, 1893565373u, 3405498929u, 3744937024u), - SC(768993978u, 3888906052u, 3538251248u, 352204151u, 4022234611u, 1471705290u, 4243963811u, 2027117811u, 1763868778u, 1322271979u, 3608278288u, 3888498758u, 3465093513u, 3125049811u, 2129222282u, 295188310u), - SC(2552844131u, 1588346847u, 4175462227u, 3528353039u, 48525488u, 1810438463u, 342094266u, 3279671133u, 111165134u, 1329165912u, 4063411685u, 1911765579u, 2818934337u, 3808545183u, 3789526924u, 1948478023u), - SC(3331030119u, 905985030u, 3533623355u, 799989600u, 1593247216u, 4044824934u, 3057376453u, 1132187407u, 2788031862u, 3252641138u, 1745792893u, 1362467427u, 2194538589u, 4207162080u, 1731158987u, 3426969514u), - SC(282742454u, 1925220542u, 3537150606u, 1044967349u, 4104410814u, 3036747834u, 2170951116u, 4063975818u, 2876870249u, 40785387u, 3225638952u, 2818597718u, 1556539976u, 2301588618u, 2800555653u, 916700871u), - SC(607531008u, 2820787318u, 1270007122u, 63140951u, 2489460286u, 3749254552u, 3480926448u, 2300022433u, 3335552281u, 3577740253u, 4083676266u, 1879037356u, 3793973091u, 653990091u, 981292091u, 2669230849u), - SC(1168110979u, 889306226u, 331429321u, 3194220363u, 4271486769u, 2440942709u, 3008822642u, 561011853u, 2621371879u, 1149493671u, 1110535664u, 2670803472u, 394628735u, 4014155619u, 3742604108u, 1418371877u), - SC(1139004104u, 1152838795u, 3053437035u, 3533998804u, 965296070u, 2842987726u, 3847937142u, 3591812355u, 1659887171u, 3058851485u, 1843832825u, 2284970388u, 153709291u, 2147381595u, 1241923942u, 3246474482u), - SC(2372841964u, 95150550u, 785345036u, 3777509922u, 3777338585u, 1256811659u, 530593057u, 2218391448u, 163045439u, 4110451435u, 940149273u, 3289892018u, 1950559815u, 2046468986u, 785769535u, 229305669u), - SC(4222560409u, 1251917359u, 3419952330u, 3518946758u, 2125025139u, 840904710u, 2104865575u, 3206919775u, 407519472u, 2004634252u, 1712650404u, 3590313236u, 840286442u, 2628712493u, 3254945248u, 1148702071u), - SC(3313735124u, 1648160975u, 2356873919u, 1752134136u, 1812666743u, 1155388994u, 2048656880u, 513774477u, 495906662u, 2103152333u, 2943961999u, 735251223u, 2523783965u, 2210023145u, 1945848363u, 2437613245u), - SC(1086803487u, 4028294733u, 2247710942u, 1830793111u, 1634316303u, 2935377055u, 600165818u, 1578619540u, 2988076738u, 457218665u, 4176910460u, 454493682u, 1199052867u, 1940805269u, 347367761u, 1212452462u) -}, -{ - SC(3715433378u, 171840999u, 971741983u, 2238541363u, 3192426674u, 4094492328u, 467620204u, 194258737u, 3399274574u, 3279461044u, 1351137305u, 2503870624u, 193649547u, 2998335432u, 1712991547u, 2208648311u), - SC(2715750837u, 1950207216u, 2432412079u, 3161034889u, 3163700758u, 2527560734u, 1574123740u, 2830017576u, 1235654592u, 1173758764u, 3503805913u, 3353556737u, 1972267538u, 2593804497u, 4050894516u, 1536909338u), - SC(4252707359u, 3437282014u, 3776749445u, 203710275u, 463138159u, 2772620289u, 1182212975u, 1132575015u, 2008846240u, 1446588540u, 1178588185u, 2810502365u, 3189501211u, 3192046357u, 3703545124u, 2781338651u), - SC(127281203u, 3251296097u, 4229877600u, 1655402395u, 2971465573u, 744237737u, 3839563968u, 1414447733u, 2055975912u, 547297398u, 3544526703u, 1086573241u, 4145442250u, 370020177u, 2948813570u, 1970539713u), - SC(3163465607u, 792227545u, 605650287u, 3454430637u, 4436412u, 957261079u, 2917570432u, 3199157324u, 317922439u, 2607400867u, 3201779931u, 1812841573u, 973872378u, 3838606231u, 3221928943u, 461831659u), - SC(246719913u, 1498935408u, 1945961723u, 1327338499u, 2917210822u, 1660082997u, 597934446u, 1244971072u, 662537876u, 3851981101u, 2064803568u, 1228771649u, 4273868614u, 3144280868u, 3367923741u, 2660003700u), - SC(958115915u, 3015255252u, 3159655209u, 1681296573u, 2092702329u, 3275820278u, 1666603934u, 3861667140u, 2501203189u, 4234907638u, 1084271161u, 60369385u, 1104875606u, 3495688315u, 3738262066u, 4032927728u), - SC(1265262733u, 3131514218u, 237040963u, 4104455196u, 2691347880u, 3487609649u, 1785135800u, 1176579745u, 4089650722u, 3141152552u, 3206481300u, 1333364227u, 276607745u, 113027050u, 176916027u, 1602590030u), - SC(2774594376u, 3129694750u, 2287032514u, 2766750820u, 29083039u, 1069500497u, 840365222u, 3485333678u, 2555809577u, 3972967703u, 629036427u, 3011729266u, 1526288233u, 1119437732u, 917067812u, 194168105u), - SC(592881983u, 3318575349u, 4127058062u, 1732571107u, 3503756272u, 837953701u, 482225210u, 1269788935u, 1504556881u, 1896976655u, 165783184u, 328929494u, 4077662490u, 1253488686u, 3518656631u, 977900779u), - SC(4160682596u, 2908983358u, 1718640008u, 3588190607u, 1505225185u, 4179103009u, 1685793395u, 115536342u, 817223934u, 1402206707u, 3062750872u, 450873212u, 3409531894u, 2142975045u, 1392180850u, 3108320562u), - SC(1943394512u, 2513880371u, 1620134863u, 1529322591u, 4060169700u, 3770293993u, 1183592156u, 3047089385u, 1457468150u, 3671110754u, 1216162597u, 2044466392u, 888112901u, 3589252991u, 523705271u, 1679814981u), - SC(2715251449u, 70744868u, 3381212136u, 1205646623u, 2056792384u, 3523601635u, 3273403565u, 2609964048u, 1635414738u, 3927671477u, 2002719738u, 17329846u, 673666863u, 4128776449u, 1023303890u, 2113317599u), - SC(678583802u, 2909193903u, 1603800869u, 1698604501u, 292539447u, 3194048567u, 1222053939u, 4292027072u, 1744031112u, 463670025u, 1002183205u, 880963334u, 2427537891u, 2521706813u, 3815796576u, 836594698u), - SC(945238598u, 3719965767u, 2849528520u, 3282124488u, 1093917226u, 3479450861u, 2561471910u, 139299258u, 3917471374u, 1798050709u, 2851226278u, 2410252745u, 1571541746u, 2877491529u, 1276119582u, 4206041035u), - SC(3869162698u, 1114491339u, 1196187395u, 1533960773u, 3407411925u, 765004505u, 1831463563u, 3761422880u, 841664315u, 226257499u, 2314441323u, 2186776430u, 2801566686u, 2703073796u, 3780881787u, 1370189991u) -}, -{ - SC(3356584800u, 529363654u, 613773845u, 1186481398u, 3211505163u, 123165303u, 4059481794u, 1428486699u, 3074915494u, 3726640351u, 881339493u, 977699355u, 1396125459u, 3984731327u, 1086458841u, 3721516733u), - SC(3675076449u, 3333909775u, 1262445603u, 3668028655u, 433069981u, 3324184640u, 206500128u, 2656010471u, 782457265u, 4053687660u, 3895856132u, 315252919u, 2755213770u, 922519354u, 2055252100u, 2429801305u), - SC(2756940336u, 2847978751u, 1709353190u, 1195969566u, 1965491900u, 3974470294u, 4065860779u, 457378802u, 2625680435u, 4168918960u, 912437805u, 1940496017u, 2831564708u, 2681452721u, 2977785501u, 178951684u), - SC(2809970073u, 2149172818u, 128792792u, 4173216994u, 3752778392u, 3547909179u, 2139546257u, 363162335u, 1029632619u, 226065897u, 1871318430u, 3511308809u, 4293432909u, 733440206u, 3154916386u, 2246758263u), - SC(731502074u, 2752951666u, 3348551978u, 3130709972u, 1526861742u, 2511266125u, 4044638365u, 215744304u, 1267320586u, 1960868675u, 3421832152u, 2257930073u, 2620941002u, 851383950u, 547951559u, 1340068454u), - SC(2684856551u, 174120198u, 1829892583u, 1225976594u, 2442169561u, 2751359631u, 1396256832u, 4190566994u, 616089248u, 1633732935u, 1633964885u, 3929429044u, 842800539u, 676918864u, 1428572539u, 219449459u), - SC(133428457u, 620472331u, 1882141266u, 1679319159u, 679060192u, 3481716513u, 213482586u, 3423863792u, 4201383258u, 1319777873u, 927348830u, 208213775u, 4087467606u, 3653264448u, 3835415188u, 3916570843u), - SC(1895413499u, 3284443662u, 1774671761u, 36215094u, 1302729892u, 3712548907u, 689399756u, 809699792u, 1542256887u, 1010909539u, 1793915800u, 371041697u, 3719334021u, 1415418990u, 3304256413u, 1722896741u), - SC(4292037144u, 3413799593u, 431584770u, 554753321u, 1212891070u, 139387849u, 4633456u, 4145076332u, 2956733683u, 2226540590u, 257006677u, 3020881975u, 3400787219u, 587473979u, 260993303u, 3410840543u), - SC(4018910540u, 3254488333u, 2078930374u, 2245837925u, 2632570996u, 3139405325u, 1623001428u, 3612462970u, 2032232089u, 519993838u, 198517548u, 1752888302u, 2236384752u, 3428944014u, 3264747145u, 2955960571u), - SC(3519760330u, 3333709979u, 1048481536u, 1985059447u, 2643412116u, 3131942587u, 1137942580u, 1547604917u, 2831143240u, 2752062158u, 438973315u, 216212421u, 839130203u, 4170782680u, 1103599719u, 3606044489u), - SC(3979124118u, 943995448u, 2583700510u, 3458129573u, 1268799005u, 2693058918u, 2421470342u, 2310844252u, 4161944025u, 2910466020u, 1520150746u, 2594375360u, 1025693596u, 3356457299u, 1405172368u, 3357345029u), - SC(3608628529u, 1093067289u, 2172624909u, 336171229u, 1137437622u, 2177129887u, 3319848621u, 3625148145u, 940129946u, 3128586787u, 111536296u, 1792339610u, 2781599252u, 3659875306u, 872551800u, 2302213340u), - SC(1104919194u, 189973497u, 2565652941u, 2930155667u, 3463454839u, 2388313768u, 2445171637u, 16202936u, 1593006897u, 2191020511u, 2084184836u, 1467463398u, 2313657914u, 2691464051u, 4089268188u, 4294499481u), - SC(4188734592u, 3528391612u, 40836399u, 4036867171u, 4090825107u, 2939803682u, 140442162u, 2546416492u, 1084596508u, 3326586985u, 72576332u, 3780421002u, 3675044591u, 2008171921u, 3141075467u, 4288443118u), - SC(3852374110u, 4271371075u, 2076634991u, 3101716180u, 518739558u, 3284103928u, 1607286758u, 3505817896u, 42970787u, 1339303318u, 3280473330u, 1956150319u, 790791234u, 1449585627u, 3814185461u, 3901254732u) -}, -{ - SC(3892284764u, 2210224198u, 97085365u, 934022966u, 3120556498u, 264721182u, 4011343025u, 1936310374u, 2593930315u, 3833725723u, 4141640186u, 2218699022u, 3726005369u, 649732123u, 1594208266u, 3687592104u), - SC(2459115622u, 155132544u, 2344650987u, 2337329027u, 2478875455u, 1363777389u, 666384305u, 779524970u, 131624810u, 1099629813u, 755087667u, 1116544707u, 3462583113u, 1765615231u, 1221263451u, 345614861u), - SC(283432140u, 3102718597u, 937211953u, 3334135604u, 2242058317u, 3044145753u, 1441000856u, 2163904099u, 654999768u, 3976748269u, 4108102772u, 1209693616u, 3022484925u, 2592361118u, 3806239715u, 2457345174u), - SC(1983572202u, 34789206u, 3963513429u, 2661898079u, 3999779459u, 2657216026u, 2570146353u, 810465768u, 1310539449u, 3517224567u, 1830164911u, 2328664885u, 3323158486u, 200812613u, 1588943475u, 1631047872u), - SC(1996456687u, 665652044u, 360516388u, 3634015955u, 3932508085u, 3762889476u, 2869080596u, 2179691892u, 1880327422u, 3850327759u, 1653803674u, 236673399u, 2154944705u, 3229042401u, 2981554507u, 485288416u), - SC(264936494u, 3091907543u, 2050111855u, 2694936127u, 1954787063u, 722933256u, 3813405263u, 739130277u, 2256053561u, 3585156690u, 2029190911u, 3133350308u, 3458910883u, 3499638057u, 41852560u, 491183838u), - SC(2808085465u, 1288453772u, 2477084166u, 3837131567u, 1141955368u, 3112183866u, 1372456734u, 2203526963u, 2954171016u, 3969349716u, 2868857569u, 414601865u, 4013256181u, 468368341u, 1996835394u, 3658768313u), - SC(394302887u, 1097097404u, 3291468368u, 1194224926u, 1035172467u, 1541144594u, 3844885672u, 3479557309u, 3116596876u, 2815221788u, 2598284757u, 360029902u, 1618439794u, 2569763994u, 3258655905u, 2917038348u), - SC(2305403224u, 515881048u, 3401955316u, 2294640138u, 2523482065u, 2913659188u, 1840514079u, 1334322081u, 1545396585u, 4197671987u, 447162882u, 3846426473u, 2663235502u, 750784192u, 4164775689u, 2390294077u), - SC(2816642384u, 3952759529u, 3784236377u, 1797857230u, 1881467157u, 3886776601u, 754213935u, 2085935272u, 3814437883u, 3598631313u, 3014087408u, 1480756254u, 2838244491u, 132661795u, 909841870u, 675503551u), - SC(2053456581u, 627096201u, 3974668317u, 245144267u, 3845450294u, 1209560693u, 1003623636u, 3431474873u, 3952764341u, 3855863791u, 1357940588u, 3374805012u, 2942824193u, 2988435703u, 329942625u, 4139666589u), - SC(73006928u, 4068145413u, 2752900485u, 643186737u, 2386201439u, 296363448u, 2965535934u, 2202307569u, 1300692310u, 3766694667u, 2421404412u, 2295288621u, 1987219755u, 3682346025u, 885571108u, 1086202535u), - SC(3800801259u, 1729576293u, 2024334221u, 266315944u, 3877353536u, 2983817286u, 1164606138u, 2981999790u, 2626097845u, 3537364374u, 3559786635u, 2149380619u, 2137897542u, 2218263339u, 206251476u, 3754285811u), - SC(1009857555u, 1650586423u, 3853695002u, 1715580147u, 1146669099u, 1380681899u, 2219018152u, 1791877891u, 3247738482u, 1042579957u, 4035547117u, 2619207487u, 2408116465u, 3045899420u, 1771645449u, 1340019342u), - SC(2004305920u, 978372350u, 1705342765u, 503429310u, 3635208103u, 3659317811u, 3957481997u, 297103567u, 2521968324u, 599616959u, 1167498361u, 357125999u, 3158983160u, 3114128384u, 3086595483u, 2336612985u), - SC(4103187540u, 1182325894u, 97419735u, 1615223731u, 2031918136u, 2818146326u, 1038685355u, 1330155299u, 2657284062u, 4126074186u, 2871281156u, 2738191090u, 1922990674u, 2689532011u, 4040564095u, 99693623u) -}, -{ - SC(3639643416u, 3974502485u, 1527161781u, 180938703u, 2788643910u, 3418867931u, 2912046968u, 1776807950u, 1185488163u, 2433308651u, 3682797092u, 1938004308u, 753534320u, 795320477u, 3620835863u, 105275502u), - SC(2971894151u, 635573958u, 1662864280u, 3637757763u, 1966418418u, 2382544768u, 3521712538u, 4180511568u, 1216311665u, 1622710591u, 2836323703u, 1065095206u, 3046512769u, 2304432132u, 1370910091u, 3540050165u), - SC(3003078502u, 1266710982u, 63268125u, 3769826631u, 2161222028u, 1624738852u, 2999285769u, 2485757266u, 3350017650u, 1836975640u, 3947916645u, 3226839039u, 3416803572u, 2607406281u, 3224012241u, 1574498192u), - SC(2417128114u, 3148595382u, 316383238u, 491687931u, 3782721648u, 71265990u, 725842943u, 2574280796u, 2910592942u, 1266732336u, 3293910730u, 3812834954u, 758280869u, 2044998492u, 585388705u, 2220041893u), - SC(492257517u, 927280821u, 3326474467u, 3418658462u, 175063450u, 4228793954u, 2332128647u, 2793872080u, 3349562222u, 3060602442u, 1750735766u, 864506271u, 3021446456u, 1089650280u, 684313887u, 2273360774u), - SC(569437869u, 3392548160u, 448456633u, 786222873u, 1891470348u, 56622530u, 1988234620u, 1200550357u, 3540465428u, 1566012807u, 3682627310u, 3118219502u, 421481320u, 474517348u, 4276632114u, 3506654966u), - SC(200012878u, 1289466640u, 383837247u, 2978212823u, 641013196u, 1218428129u, 2429292619u, 1428313217u, 4155302101u, 1036892035u, 3775206351u, 778853475u, 3322870631u, 4195074838u, 3725481759u, 3550082329u), - SC(126839072u, 3914304851u, 1035784989u, 2867617428u, 1989254908u, 3724484330u, 1316610484u, 1040102649u, 1452719164u, 210631948u, 1224888518u, 1113840153u, 910511278u, 2297844676u, 797967535u, 283877762u), - SC(1244500121u, 2493482314u, 3779000024u, 2685901143u, 2759844693u, 2465008309u, 2989069530u, 1046572576u, 3374497605u, 2414541412u, 1726159904u, 3650454710u, 2872643374u, 1536622747u, 1381290537u, 3538573283u), - SC(1982773073u, 895953548u, 653968243u, 2944168854u, 1891156211u, 862699673u, 178384938u, 2122337777u, 3992617936u, 1827424625u, 1827918311u, 4247768891u, 2116109311u, 2389157370u, 3259962586u, 3018719650u), - SC(16401953u, 2306633926u, 2338480543u, 3225473112u, 3429377887u, 2444554167u, 3036218027u, 811186210u, 2350667613u, 3590742085u, 2594672781u, 575072326u, 272468093u, 997542396u, 3031146350u, 3776453205u), - SC(1784787552u, 1031272746u, 3302965053u, 805306745u, 3874552409u, 2790720051u, 483200429u, 1779723984u, 1097599486u, 1897611475u, 2456960784u, 1754250527u, 3808506348u, 3902842183u, 2596972722u, 2928554842u), - SC(2323692909u, 829274841u, 1103316386u, 1866432209u, 1938371795u, 4027514213u, 3989131198u, 2637747342u, 2193562562u, 1183535102u, 290853894u, 707762868u, 1909722738u, 2733745164u, 2354524179u, 94921256u), - SC(390966983u, 2005348047u, 1183001210u, 3460046175u, 1194344520u, 3385791048u, 306982602u, 876126480u, 3192052847u, 3055117485u, 1493712024u, 239443620u, 3677526258u, 3935077241u, 3195438491u, 2508943164u), - SC(3776157658u, 1760005001u, 3371368706u, 4151959572u, 4117952947u, 2782084300u, 3075220020u, 3130861900u, 3220322643u, 4251107806u, 2765944679u, 2454606920u, 3864173523u, 2241965276u, 1056706189u, 2253371852u), - SC(10455103u, 669421195u, 538798805u, 681593482u, 4243109638u, 2765550308u, 1560790187u, 2332940655u, 157674749u, 358872640u, 2549359913u, 811329072u, 318369228u, 2192271276u, 2616093049u, 3105543667u) -}, -{ - SC(3392929934u, 3483303263u, 1976307765u, 4193102460u, 1186037029u, 2559946979u, 3008510830u, 4008303279u, 2792795817u, 3991995u, 311426100u, 3736693519u, 1914150184u, 2000710916u, 1829538652u, 896726226u), - SC(1506989834u, 781231698u, 1423994091u, 932436763u, 2811140941u, 235158077u, 3312925598u, 1277169313u, 2161654787u, 95045550u, 2507009285u, 3400899479u, 1327874861u, 2641030305u, 845165129u, 3067306163u), - SC(81377829u, 4112377516u, 996390415u, 1466127523u, 1087938057u, 1370439327u, 2374941315u, 3221315808u, 35184362u, 4155013651u, 4157224703u, 3036174627u, 820839223u, 644204168u, 3814924360u, 2548030643u), - SC(1091124676u, 3446444543u, 108918031u, 285417020u, 1457053816u, 2518578419u, 3204558864u, 1447981867u, 3090612039u, 774503865u, 3344583272u, 2737274269u, 3562442510u, 1127429989u, 2804182977u, 1775681652u), - SC(2318905039u, 2047942274u, 566069924u, 123115342u, 2915025724u, 2614503051u, 611479778u, 1680640702u, 111791999u, 3565934367u, 3623017458u, 358904698u, 718271833u, 2594429479u, 2455462208u, 1049889789u), - SC(2072590390u, 2994175732u, 776612573u, 3305897523u, 938985307u, 4037860099u, 405398386u, 312125617u, 834030222u, 4269222652u, 3952042783u, 188369721u, 969558599u, 2241466312u, 1494637662u, 3640394545u), - SC(793329188u, 1680204464u, 4194525713u, 1397937237u, 2203558613u, 193170132u, 590149348u, 3837254789u, 2629901211u, 1547324833u, 4256276761u, 178627910u, 1204782838u, 3049171442u, 2847310157u, 1633221731u), - SC(1445130399u, 3305816299u, 706740166u, 1986021205u, 2637844550u, 1419078314u, 1678054887u, 2432697110u, 870544859u, 890225672u, 4294515721u, 4251895411u, 1276311012u, 1177847908u, 2958585231u, 4245816799u), - SC(4225912221u, 703507803u, 1922376483u, 3748563847u, 841832204u, 937238929u, 1762562329u, 2321245641u, 3396851205u, 4196168123u, 2898493537u, 4105193320u, 3913075709u, 3714213782u, 3736794417u, 1813506206u), - SC(473058800u, 1281200026u, 2096535567u, 1916392924u, 2499055699u, 1592813861u, 1665248526u, 1352252079u, 2539722497u, 3800235497u, 2456011531u, 2486813252u, 2969323588u, 2786889819u, 264256920u, 4162650714u), - SC(4093970658u, 1112717313u, 4105391438u, 692152127u, 3191447576u, 765356874u, 3774754898u, 3659714922u, 1417146611u, 4116649329u, 2382824064u, 4091923584u, 2943998996u, 2572469258u, 2350556732u, 4055180934u), - SC(4241530692u, 3958450744u, 2400383404u, 466315350u, 35062538u, 2419973666u, 1574066566u, 718969713u, 2103427683u, 1844215170u, 377438369u, 3472936858u, 4219642124u, 2727593550u, 2415179286u, 530554266u), - SC(1717990860u, 490767589u, 4104938990u, 1912533482u, 1727757083u, 4081637760u, 2971627803u, 4227474711u, 2482396781u, 1077462396u, 1040490667u, 188422725u, 1078987146u, 1905877850u, 3465315863u, 3779881072u), - SC(2343360099u, 2602377036u, 540592495u, 3215700530u, 2276091252u, 330543342u, 1521140429u, 3101043196u, 1353643940u, 4257187260u, 3766970644u, 3977679607u, 2139641066u, 2691703488u, 1191064988u, 3899819176u), - SC(4020334744u, 3662481612u, 4168714619u, 3391835711u, 3785299560u, 71469795u, 2493742903u, 3412561168u, 3292204549u, 1481564183u, 2157273751u, 477496008u, 931448839u, 2827709521u, 2133135454u, 3513095854u), - SC(1821292885u, 77067071u, 2713776553u, 2767520127u, 1059460035u, 985220275u, 2884538737u, 221640066u, 2657382407u, 232264137u, 3155923068u, 3788271780u, 2919723565u, 1308585734u, 3615447351u, 9588952u) -}, -{ - SC(2320406161u, 892569437u, 3092616448u, 1707673477u, 2810327980u, 4012118332u, 4142748730u, 3869507620u, 92116036u, 2366184953u, 1613655167u, 3287845172u, 3562699894u, 416962379u, 1296831910u, 1764080884u), - SC(220529260u, 249394787u, 707093586u, 3327680194u, 3905189366u, 612327964u, 3292761054u, 3030686883u, 1334491337u, 3207860077u, 3280619568u, 1041320647u, 2483468975u, 1479881667u, 3211575507u, 3039423798u), - SC(2075210586u, 859890386u, 3979249840u, 1571749934u, 1787834945u, 3779262932u, 3834468444u, 2848979155u, 3949299214u, 3265482052u, 521566179u, 4090178483u, 2634506734u, 537774764u, 1760986104u, 1885781444u), - SC(2157623553u, 1245488719u, 2108443037u, 4226304849u, 1701247415u, 4110744868u, 1746909616u, 3191493799u, 846028927u, 3826268145u, 3155840342u, 1303740777u, 3325552898u, 2580884535u, 3592783405u, 4209959030u), - SC(535271984u, 3867256577u, 2621667187u, 479852461u, 3031868718u, 681291605u, 3866870888u, 975222367u, 189285295u, 2489945122u, 4002580885u, 1631683077u, 2806354223u, 990581176u, 3013857114u, 805874285u), - SC(4221232460u, 3061114345u, 3434676469u, 1406782470u, 155821803u, 124504941u, 3888697140u, 2788501814u, 1026476732u, 2216503728u, 3089015914u, 2063998098u, 272392246u, 1587339314u, 677528523u, 2432699241u), - SC(3643892943u, 4282202220u, 2100563362u, 826776443u, 1365722925u, 2702305724u, 679208928u, 3149950187u, 1446692720u, 2990196076u, 3121167752u, 25041546u, 1204401671u, 3950457476u, 478874733u, 4191001246u), - SC(1002796340u, 395169719u, 3087599283u, 10336612u, 2123927609u, 504611529u, 4163730275u, 706425703u, 1588733263u, 4149509341u, 1952228143u, 3819719132u, 766367752u, 1435203845u, 1906598194u, 3492363785u), - SC(1774340829u, 3089482890u, 2870005976u, 919794943u, 2035504962u, 4034646005u, 3486869666u, 3458779364u, 2688966610u, 4246698276u, 241215855u, 1193302498u, 1307583268u, 129792487u, 301354381u, 2759318534u), - SC(1993945167u, 2379081822u, 2587040362u, 3154537819u, 1926143939u, 2749781524u, 935556830u, 4138641196u, 1781637476u, 2939621229u, 45782825u, 4247420511u, 1775642409u, 3169645376u, 1224651656u, 1411268824u), - SC(4099217380u, 332485632u, 702660355u, 2932600301u, 2644542769u, 1705216342u, 2043283695u, 2373746705u, 2092217219u, 1660104946u, 3159676245u, 3674605841u, 226100099u, 3987250021u, 2436672589u, 1083744721u), - SC(775618835u, 2173251804u, 4192653515u, 3582997173u, 3769245096u, 484007740u, 503088416u, 1360222738u, 586791868u, 3760447547u, 3490651251u, 3534666198u, 2531156474u, 1207301882u, 832959081u, 3020069982u), - SC(298341207u, 1349761730u, 1369831393u, 1101983922u, 2409775356u, 3892600618u, 3875266737u, 3482966490u, 4002034047u, 2018792567u, 1932407387u, 1184232926u, 3015567427u, 301694942u, 437132459u, 3636206614u), - SC(4090425889u, 2348669465u, 2575850637u, 3995997864u, 3040420324u, 1615191584u, 2490849366u, 2670494936u, 2841563080u, 3763919842u, 3580970157u, 3864708123u, 187158351u, 2199194387u, 4160227448u, 2176418944u), - SC(3040328915u, 1001466289u, 3676795030u, 2946692141u, 3593888463u, 2224708622u, 4148397123u, 4253879884u, 1993280384u, 1176406404u, 3148404923u, 4180061590u, 1786680964u, 4036906941u, 1164279397u, 3562714780u), - SC(1286200509u, 4232891464u, 1656861418u, 3412215448u, 1086562483u, 2512121988u, 2650588176u, 3097245464u, 3192968944u, 2220731064u, 3414522916u, 4204353060u, 3690514744u, 3688465060u, 2246470987u, 498255717u) -}, -{ - SC(1167035839u, 2632944828u, 1562396359u, 1120559767u, 244303722u, 181546963u, 2941229710u, 561240151u, 1460096143u, 346254175u, 110249239u, 1849542582u, 1293066381u, 147850597u, 3876457633u, 1458739232u), - SC(3571928080u, 2436259038u, 1291130511u, 4109706148u, 535321895u, 223400632u, 1981907545u, 281269666u, 3986674262u, 1137333737u, 1403128295u, 1607985509u, 1996916063u, 3564990547u, 3398899933u, 2822030993u), - SC(4187142002u, 2183119934u, 1635192887u, 2899344980u, 2532710469u, 3583070294u, 1537984623u, 296183513u, 2324170481u, 3475303187u, 3887648540u, 634736823u, 1254765115u, 3808584578u, 3772430219u, 561684376u), - SC(513372587u, 1759503751u, 4262413842u, 2894839952u, 1546497784u, 1634597484u, 3075497476u, 1112503488u, 1318854936u, 1645523550u, 1808408161u, 1471049890u, 1607196116u, 1989192912u, 3845591311u, 3230210229u), - SC(4281800629u, 256065360u, 161761292u, 2162610453u, 3289868207u, 803664088u, 1737988317u, 3468667062u, 1313091619u, 3871261661u, 4163576187u, 3519070773u, 663580583u, 2181685257u, 1282501745u, 373224564u), - SC(1305532007u, 4040631353u, 3016994284u, 364840424u, 312087064u, 2832713285u, 813363164u, 1634515727u, 2857968226u, 2482770921u, 2702964276u, 1457003903u, 4233117491u, 978467573u, 454990490u, 2451215822u), - SC(3309788844u, 1373644165u, 2568421202u, 4021050421u, 3214613315u, 3179866441u, 2282215282u, 4192353052u, 766132975u, 1427735093u, 3905164154u, 3510365574u, 3650419996u, 1208798186u, 2311177541u, 3425106727u), - SC(1485656607u, 1872571460u, 3807266779u, 3227427836u, 1367154025u, 2087101352u, 2787930808u, 1683647111u, 611621831u, 1033465938u, 1055561737u, 1718623444u, 3674681330u, 3643294293u, 3841507882u, 2950124804u), - SC(3583452191u, 43558840u, 2702416786u, 2831018419u, 4179535508u, 3293628424u, 3781032090u, 4272940814u, 1561835153u, 3434531879u, 2033417772u, 143682419u, 2206689113u, 2885101743u, 3330838914u, 3213033967u), - SC(1563269339u, 3268845808u, 481878529u, 1366255066u, 188999428u, 2024859095u, 3740130866u, 1902201859u, 3294724532u, 3498902869u, 2063801661u, 3851840419u, 1697955856u, 1216829830u, 2472036433u, 2158918739u), - SC(3706632627u, 1854832685u, 4075722340u, 3009760070u, 1947919686u, 1613829674u, 3359356634u, 160149010u, 3211678034u, 1403957074u, 2395316449u, 232911190u, 3595342115u, 593590477u, 4003146812u, 1042747586u), - SC(3566751331u, 1293366329u, 237055278u, 781035984u, 3490518265u, 471671502u, 3279573882u, 4088428685u, 3341570902u, 1660948465u, 2602036180u, 3189056267u, 1448251311u, 3378653995u, 367559448u, 1247557023u), - SC(332188181u, 124235367u, 2908363616u, 57405667u, 3860321591u, 2915594808u, 3193053797u, 3103490367u, 2893876952u, 791722516u, 2759950240u, 2647310599u, 1060814304u, 1104815755u, 3283917665u, 954167246u), - SC(3633439037u, 1737408037u, 3240746577u, 2032524778u, 210349431u, 1157873376u, 3552462955u, 3068823u, 2593869163u, 1645741574u, 2624282012u, 1595174943u, 3150496822u, 2635369792u, 3670346328u, 1317499755u), - SC(3066163224u, 734815666u, 3189326611u, 2603442644u, 551273493u, 3201260612u, 896218759u, 1203901890u, 3082479753u, 4206490018u, 1615910957u, 3112412856u, 3354260034u, 1776181406u, 227950091u, 2452682654u), - SC(2235295503u, 3336503999u, 656069002u, 1855251063u, 1400966644u, 100804460u, 3316705750u, 794158471u, 3220130150u, 1524496317u, 4024763824u, 915138624u, 1872936127u, 829155670u, 1406327784u, 3285915916u) -}, -{ - SC(3539989726u, 2664422354u, 3717852078u, 3493347675u, 431408204u, 2534904428u, 166307432u, 1071633271u, 2817060747u, 2307358268u, 3433391820u, 2071844151u, 219511979u, 303896099u, 3062367591u, 2892429963u), - SC(4169968731u, 2129799654u, 437437237u, 369342547u, 1225909990u, 105177072u, 378686654u, 1403688950u, 3897807924u, 3252342965u, 1215424641u, 560413328u, 1897408132u, 317929004u, 3828647679u, 1630564758u), - SC(2120346993u, 1574861569u, 4055542703u, 3156063114u, 2155135979u, 3395705935u, 3607950162u, 1649229112u, 1891339524u, 2871189526u, 475543260u, 4035849276u, 919486311u, 4103998043u, 2581732188u, 3337457769u), - SC(2650342494u, 2112594502u, 300482146u, 4214370423u, 3712572735u, 2394678491u, 944484075u, 2859174140u, 1298074617u, 4123981874u, 2931863188u, 4060402101u, 408241016u, 1141274074u, 2343754010u, 2412599648u), - SC(1561545950u, 3513590208u, 46110254u, 2131948246u, 1318148204u, 2154872738u, 1632214749u, 3758828119u, 3082206346u, 1424038120u, 2361241545u, 845137641u, 307971779u, 1724404993u, 861282060u, 1237934782u), - SC(2774909901u, 771645224u, 1285073837u, 2193431137u, 1992145786u, 1323638656u, 695741715u, 2225025760u, 1506694954u, 4281622541u, 648809495u, 1264275594u, 2179049970u, 2134563430u, 1143161913u, 1676304803u), - SC(146493114u, 1026262009u, 3602767471u, 2183478058u, 1903997235u, 4037497130u, 232766761u, 3333583275u, 4037065903u, 338762279u, 3658077565u, 3465013868u, 2987748329u, 1503145496u, 1553131083u, 2250198737u), - SC(2341715858u, 2700579248u, 3859696179u, 2395756825u, 1875611477u, 3083700335u, 3413235310u, 1368601544u, 2011324934u, 2489277894u, 3393073269u, 1479863073u, 1546719681u, 1270920228u, 832404816u, 4096637834u), - SC(3098090164u, 3937526885u, 3922595589u, 3117243593u, 3619511456u, 687964457u, 2049777986u, 2737216841u, 904576627u, 2497431372u, 3782524472u, 2176150332u, 3538905622u, 1249874595u, 386091287u, 597337724u), - SC(653517061u, 2613638042u, 3043803086u, 3430911227u, 3939946327u, 3394071887u, 1634025406u, 422896314u, 2056719107u, 2825344479u, 4064697313u, 3122017483u, 3752686726u, 3984230999u, 2989927946u, 36279219u), - SC(2977387875u, 1756856293u, 2305658602u, 3898809838u, 2022534013u, 3053356239u, 1719149320u, 1006974664u, 3980567886u, 911250528u, 3970581037u, 4208855094u, 2375475175u, 3461024498u, 4207299460u, 172606632u), - SC(2123341088u, 2610619360u, 3636249805u, 2405928311u, 194895330u, 4166746397u, 1666551241u, 3089845290u, 830253287u, 1769367456u, 492844122u, 2898915009u, 1465071417u, 1748645392u, 3136192983u, 3149049830u), - SC(182090295u, 2773063932u, 2875617227u, 2014878906u, 4034576690u, 3504190878u, 648632813u, 578906269u, 3395653562u, 3622802446u, 1642118462u, 1105217635u, 3484288771u, 4187487776u, 3066363798u, 3248936252u), - SC(154149828u, 3967951687u, 1435057545u, 77065166u, 3232269485u, 3912916706u, 592527655u, 4277917673u, 3417904405u, 3905839920u, 1437307359u, 2532079592u, 1386597940u, 4043192840u, 828125384u, 1712244674u), - SC(4144828863u, 1262971610u, 2738002832u, 3848745747u, 554156666u, 3660926287u, 1405749523u, 293551868u, 956195932u, 2061195588u, 3476646641u, 1003448777u, 4182963546u, 1462193925u, 2827901865u, 1370898532u), - SC(287054389u, 4206061741u, 3909899140u, 2957058664u, 2712205523u, 1231432323u, 1252507865u, 2198483068u, 3163354130u, 595880373u, 2050058791u, 535083586u, 4093274722u, 251534866u, 1425149793u, 2349787856u) -}, -{ - SC(3015000623u, 325176924u, 3212623969u, 1014540936u, 2686878702u, 3453922035u, 257234635u, 689320672u, 395365200u, 3425465866u, 3351439740u, 3293249321u, 2261203941u, 1504215424u, 2365812346u, 2486464854u), - SC(2802351214u, 1019547153u, 1581443183u, 2237644987u, 2316167912u, 1277137594u, 922833639u, 1775757119u, 2259030628u, 3320484395u, 3474839377u, 3039388985u, 3157017009u, 701728799u, 45087422u, 1375130067u), - SC(1408178651u, 332882372u, 2572930650u, 1429622838u, 3740348959u, 3769865143u, 1102404486u, 2395773863u, 2055053046u, 1642858333u, 434575788u, 1458579645u, 1077283311u, 3435370625u, 412513198u, 1108997u), - SC(166351317u, 1290556120u, 1492697218u, 3828755332u, 1787027698u, 2627329842u, 818520792u, 3844511768u, 1093689215u, 2840813230u, 4268955351u, 1793367442u, 1197897289u, 1467402002u, 558600125u, 4039642298u), - SC(2618143148u, 4195387407u, 3571081448u, 176847982u, 3021045559u, 2151239299u, 4216918791u, 349987936u, 1438071630u, 2148079477u, 510134808u, 1844452199u, 3473619148u, 3775643892u, 3701006526u, 2069649956u), - SC(2536827719u, 256373429u, 82685205u, 2031847695u, 1685669223u, 3749398630u, 3100433967u, 2559626296u, 2614261735u, 2095898325u, 2650411530u, 4139725354u, 2433652522u, 1465137472u, 3074463995u, 2942034210u), - SC(950856594u, 2511634642u, 447889167u, 3271534101u, 3998181635u, 850059409u, 1500318444u, 2845728509u, 2319192144u, 1285732158u, 3307511706u, 1860111207u, 106597122u, 1317987028u, 3909997475u, 2833499319u), - SC(197466102u, 106471666u, 3969627291u, 425148315u, 2088018812u, 3287551129u, 2083642145u, 386904296u, 2967132086u, 417456225u, 2418726206u, 2685222098u, 3920069151u, 388803267u, 1008714223u, 4223482981u), - SC(1730602173u, 1587573223u, 1136504786u, 801576255u, 1239639300u, 3897044404u, 2640640405u, 3098571739u, 2095045418u, 1782771792u, 2216047065u, 2006450887u, 1019963460u, 450135304u, 1704523436u, 4178916267u), - SC(3045516080u, 2837283309u, 3652809443u, 3617799274u, 2953845221u, 1870697859u, 1987277049u, 671334013u, 2347392220u, 1637733040u, 408564290u, 531095235u, 1714215546u, 2668823252u, 4291679007u, 1499030154u), - SC(1785804164u, 3771923969u, 1688952513u, 4078905240u, 4219818381u, 2140263698u, 3560443409u, 1027592498u, 981877075u, 1273450409u, 1808708945u, 366130160u, 1509712333u, 1419790056u, 3592515372u, 1023304152u), - SC(689558936u, 2052202277u, 1573780309u, 1046114431u, 1768897198u, 1193436549u, 613072153u, 961650488u, 3203433527u, 2587127126u, 2088764244u, 3898254742u, 1779313411u, 2448405043u, 2102013432u, 2635393468u), - SC(2025692259u, 905848568u, 1759010770u, 1792571870u, 4118995060u, 266283808u, 4139640706u, 3438115348u, 2780184652u, 3445643695u, 656585512u, 181166262u, 2272629776u, 370943424u, 1751557846u, 2309122167u), - SC(267180733u, 424783777u, 1080203254u, 2661909603u, 1424050736u, 3737445342u, 2397112235u, 1140319020u, 3540605726u, 1560404816u, 714090654u, 3305695922u, 4001926073u, 4235374954u, 2250613806u, 603974704u), - SC(244840167u, 1554020100u, 3702066775u, 2862773506u, 3785435454u, 3651035430u, 218349583u, 1404753202u, 3766478445u, 2586133471u, 1533117238u, 4149938439u, 2210912076u, 3594357012u, 575816505u, 525962129u), - SC(4146528898u, 2136081288u, 1410528199u, 2682243562u, 3659634297u, 3884779676u, 1276188622u, 3650143718u, 2534539131u, 69352587u, 4188728680u, 4144009400u, 528573366u, 1948891771u, 2778384350u, 3961787045u) -}, -{ - SC(771871546u, 3238832643u, 2874232693u, 1176661863u, 1772130049u, 1442937700u, 2722327092u, 1148976574u, 4122834849u, 744616687u, 1621674295u, 3475628518u, 2284524224u, 1048213347u, 4058663310u, 153122870u), - SC(2125145888u, 3034373129u, 148397811u, 141146887u, 2520820550u, 761993323u, 2298029094u, 2891332110u, 2829144983u, 2531560926u, 2167918181u, 3311166313u, 1986747894u, 2110826144u, 1833688282u, 2697250572u), - SC(3869871954u, 4004844136u, 2445592287u, 191554676u, 1824322074u, 1934754654u, 1806989779u, 631655906u, 1640478312u, 3779394326u, 3878618879u, 1897296401u, 116845712u, 1282189569u, 1638341398u, 253193742u), - SC(869049848u, 3185853214u, 1086566153u, 574813225u, 768296876u, 2336838903u, 1037196762u, 3581040974u, 1545806877u, 1185761684u, 533220394u, 2594450382u, 518321105u, 3416686830u, 2271268151u, 3918676320u), - SC(3856331543u, 2684505765u, 649861433u, 2052378851u, 4281491040u, 1056350427u, 1268888422u, 3791019043u, 2372988231u, 1754646015u, 3964172838u, 3080977165u, 1940074122u, 2762476976u, 3389041795u, 1131517310u), - SC(1630655860u, 1949945516u, 3883647184u, 3029959080u, 1311781856u, 408642488u, 2800393690u, 3410356207u, 115351401u, 3420630797u, 2709679468u, 2872316445u, 1790203899u, 1997501520u, 3278242062u, 551284298u), - SC(2323279372u, 1575922229u, 4047150033u, 1372010426u, 3148623809u, 2453870821u, 2339486538u, 2280451262u, 2466099576u, 2994948921u, 132102763u, 1776872552u, 3906687848u, 1416385780u, 2716658831u, 3839935313u), - SC(1482060017u, 4064599659u, 4201421603u, 1862488009u, 1206323034u, 1506270647u, 4148487892u, 2940354206u, 221477839u, 2184047858u, 1052602625u, 1800724448u, 2376949890u, 1248004043u, 4042069004u, 1001474649u), - SC(1973975072u, 2109156381u, 895285550u, 2806725496u, 4257596779u, 2294716595u, 2126073388u, 4029509053u, 2287557214u, 3863235224u, 910675328u, 3403565516u, 2460443864u, 4145068647u, 1675629270u, 2972605807u), - SC(3067953236u, 2487048107u, 1053067642u, 2406833819u, 1120120518u, 2019615106u, 2151977185u, 2444444329u, 3698388134u, 2675794597u, 2346696087u, 3691916163u, 416413840u, 2548582733u, 2519917531u, 3323365251u), - SC(4258867839u, 1450083676u, 3423817219u, 2338254228u, 956448310u, 2038800503u, 2270893323u, 23474499u, 4001071451u, 434241187u, 4225947271u, 3009484949u, 1212186223u, 3021170789u, 3408787844u, 4241328442u), - SC(544425045u, 2335106449u, 1970249987u, 676962447u, 2451092807u, 3397085111u, 644609608u, 622894566u, 3012162452u, 742316904u, 1183695331u, 1942632009u, 3993963459u, 2025380463u, 2934502595u, 2424729664u), - SC(489227787u, 2064607364u, 749046162u, 1223089239u, 4103152782u, 944881113u, 2156101348u, 2809656549u, 2750173639u, 2290439348u, 455194332u, 3662094961u, 2388553957u, 2373693996u, 3087294434u, 714908241u), - SC(844100070u, 1293873339u, 240400805u, 2741251793u, 4185619158u, 3756747900u, 2600026127u, 4095003808u, 2551250677u, 1982555415u, 1538344606u, 2598805396u, 1759235723u, 1251966u, 1750681115u, 626531732u), - SC(3996016258u, 3876613311u, 1191787057u, 3901742282u, 1577096572u, 270596184u, 3165567618u, 4061944625u, 3613068329u, 3912630805u, 2056061785u, 2568706449u, 2343664228u, 1807908509u, 1314728487u, 1028342757u), - SC(2729604648u, 2866824008u, 1921075953u, 959207538u, 460881358u, 1786258799u, 989199155u, 1140694999u, 3534517067u, 1671080238u, 1077292982u, 69981150u, 2456995550u, 2177711190u, 3355630373u, 505438766u) -}, -{ - SC(2470971363u, 1622646280u, 3521284388u, 611900249u, 53592433u, 1667691553u, 3986964859u, 3228144262u, 4160240678u, 1357358974u, 796266088u, 2135382104u, 2999113584u, 425466269u, 866665252u, 3795780335u), - SC(1943673032u, 163567132u, 2998325065u, 4151760187u, 4286963295u, 2037110896u, 4023804057u, 2843670454u, 4267379728u, 470850548u, 1360194572u, 542908383u, 117354082u, 3909600634u, 3301531838u, 585104523u), - SC(421763950u, 3621776882u, 1804759030u, 1922063749u, 28357531u, 2718763721u, 3528327041u, 2594458380u, 1745913977u, 1705774731u, 3785007083u, 1889010688u, 4275556992u, 2808027536u, 1706627542u, 967259307u), - SC(3761989171u, 2069950976u, 953323220u, 30139149u, 3360357391u, 466334029u, 1085748790u, 717259079u, 3822910993u, 1348849055u, 4159668773u, 3924702853u, 4257335520u, 1714446370u, 3394938265u, 2541598048u), - SC(2132231371u, 3951042779u, 332537683u, 2179456991u, 3112576172u, 2873883577u, 502046554u, 4014018248u, 4272356370u, 2124475345u, 3140973257u, 1234959848u, 3468807232u, 3812306463u, 2768101189u, 3493652974u), - SC(2983624056u, 158967077u, 546553405u, 3473936990u, 3742593866u, 3986716933u, 2905591308u, 285301696u, 2640868047u, 3062221467u, 70156428u, 150492378u, 3977001273u, 1087159682u, 1233481348u, 3391921638u), - SC(3432795737u, 4256529583u, 3151717298u, 4190687875u, 1563633254u, 158068428u, 685294219u, 733826550u, 2829744078u, 4225504275u, 2375584227u, 1429440840u, 2192098666u, 1015042413u, 840775854u, 41702830u), - SC(3231767315u, 1865273494u, 1093659663u, 1873962287u, 1664376931u, 1435837948u, 31100007u, 316783664u, 996300708u, 334486049u, 1648124912u, 3615910102u, 2480590997u, 2253624363u, 548978494u, 3975730498u), - SC(1923874249u, 3947343158u, 2264687656u, 1121555015u, 3593673308u, 289357572u, 3048054908u, 3707221766u, 2043411687u, 1708537123u, 3350208529u, 2939237811u, 2793137666u, 3370678100u, 1405378414u, 2235087472u), - SC(139882711u, 1304366355u, 1276034712u, 2139658031u, 2197726287u, 3663457902u, 2357615523u, 1611719773u, 2323318078u, 260257531u, 2850134214u, 3099029628u, 553263652u, 173876122u, 2118167747u, 1771928540u), - SC(566458485u, 3545725305u, 2257836680u, 2245189792u, 1605297549u, 245844769u, 2016071772u, 1896412522u, 821618527u, 1870442187u, 3958912319u, 4032980189u, 2069248247u, 4226059888u, 3345680132u, 1791157180u), - SC(4148097755u, 2486537082u, 4003164230u, 2318687306u, 2491702264u, 229564758u, 4126839602u, 211561653u, 3452304873u, 2572510204u, 1630441069u, 3167885411u, 4175966562u, 1295680948u, 161732432u, 107333173u), - SC(1923252062u, 311708286u, 1678166990u, 3717252154u, 3161198614u, 1069601573u, 4091259962u, 359278439u, 3768419820u, 2520693990u, 650972975u, 383288062u, 1217231824u, 2559091429u, 4278580592u, 2250271391u), - SC(510621576u, 1629846927u, 3397488683u, 961386517u, 653633283u, 1754007094u, 2769834941u, 2247122605u, 2701964981u, 3912616774u, 3406969249u, 63999109u, 3141040146u, 2619453260u, 1468121925u, 4171492447u), - SC(3961993547u, 1155134029u, 1496861029u, 1279080034u, 2846121209u, 3483514199u, 2468398271u, 505281559u, 3532558643u, 2311328115u, 2310583909u, 3085705085u, 2999958380u, 2683778623u, 32663880u, 1366954658u), - SC(3799286526u, 1580228485u, 2766986278u, 586308614u, 2894037718u, 587959438u, 1301020570u, 2323176208u, 3827747523u, 2955860540u, 455053544u, 124753776u, 703403555u, 1658788582u, 3867772588u, 3276199889u) -}, -{ - SC(2899222640u, 2858879423u, 4023946212u, 3203519621u, 2698675175u, 2895781552u, 3987224702u, 3120457323u, 2482773149u, 4275634169u, 1626305806u, 2497520450u, 1604357181u, 2396667630u, 133501825u, 425754851u), - SC(373198437u, 4218322088u, 1482670194u, 928038760u, 4272261342u, 1584479871u, 2503531505u, 354736840u, 303523947u, 2146627908u, 2295709985u, 233918502u, 3061152653u, 3878359811u, 3090216214u, 1263334344u), - SC(2076294749u, 898460940u, 2754527139u, 2099281956u, 3551675677u, 4195211229u, 3603181913u, 1984445192u, 1121699734u, 573102875u, 2187911072u, 656800898u, 1477748883u, 3685470532u, 3965328576u, 4253954499u), - SC(1876288412u, 2267864341u, 434083874u, 1779401913u, 2781669786u, 3073195348u, 669142308u, 3636028767u, 127310509u, 372075961u, 2537369503u, 2705808591u, 971889633u, 2718294671u, 1415139024u, 276903675u), - SC(3596445084u, 2918342013u, 1827011883u, 3900260359u, 1783558754u, 1921301616u, 3293933601u, 1111091218u, 3238604202u, 967515902u, 1208493040u, 1614341552u, 903992012u, 480937886u, 28823639u, 2379076161u), - SC(1968094521u, 1600813704u, 2958098796u, 909224758u, 1752381729u, 3115930502u, 3643078327u, 2863416031u, 2510423171u, 2162796973u, 1796627662u, 3678673773u, 239312629u, 2457874359u, 3809753210u, 2494718541u), - SC(1731463174u, 4265769542u, 194787641u, 1036371942u, 1745836602u, 660344840u, 1082796561u, 3963871960u, 4001246025u, 3118794916u, 3886266100u, 1928084049u, 3032262555u, 2306541818u, 3921311698u, 2426451176u), - SC(4018285402u, 658949239u, 1329629679u, 2738829796u, 776877685u, 1774949833u, 2797031752u, 3236392582u, 2542061420u, 1832249084u, 183211998u, 1840198657u, 1314474881u, 3361925365u, 3440999944u, 974653576u), - SC(1671164742u, 4271520021u, 1517391404u, 3289979834u, 1233503784u, 3050636514u, 3728319521u, 2919957525u, 3518724155u, 1272537958u, 3303667759u, 3864284110u, 234069183u, 1495943844u, 1989482539u, 3056780355u), - SC(1575547612u, 2187321001u, 2701011625u, 2761636008u, 1864623673u, 3995428494u, 1950725639u, 3749309698u, 2711714857u, 3743669273u, 3222519898u, 621366782u, 2554696188u, 176315043u, 1467854493u, 1806812435u), - SC(1182422499u, 3354985654u, 814715964u, 4226927046u, 3360200226u, 2503195953u, 1526762508u, 3747376732u, 1505823655u, 3718914053u, 2708056196u, 1868291203u, 1664951819u, 1982491563u, 751360443u, 1075645602u), - SC(101076600u, 386741863u, 2955045918u, 1653351871u, 1070602553u, 321875967u, 3200546966u, 2632915072u, 225765461u, 1759013254u, 4169466720u, 3880757831u, 1769634729u, 2642211393u, 4245887731u, 3909815727u), - SC(2379322656u, 1554830911u, 1971754317u, 1058862290u, 623917994u, 2775317172u, 3261049248u, 1667374591u, 3883068608u, 3752131736u, 2607464936u, 1251402973u, 4056909038u, 937468613u, 309280197u, 1804321090u), - SC(395093976u, 2154850233u, 624748058u, 3473623511u, 530005996u, 1656467301u, 451942772u, 3238178099u, 691726480u, 2563588439u, 3675387583u, 3294893253u, 1205949092u, 3844564019u, 114533547u, 4193437592u), - SC(1241354591u, 1121646490u, 1686974686u, 3373490541u, 1189649937u, 2948191343u, 2978671156u, 3827318062u, 3377194192u, 3805066092u, 3271994064u, 2484020181u, 549626522u, 1166583694u, 3299399570u, 764854172u), - SC(2808929206u, 427994673u, 2338143204u, 3942895356u, 2304289727u, 1468778908u, 1350679341u, 3972686632u, 2399853022u, 2097821409u, 3799931826u, 2500883276u, 1352425312u, 3372587055u, 596007302u, 2017539287u) -}, -{ - SC(172527491u, 737404283u, 1378219848u, 1967891125u, 3449182151u, 391223470u, 304889116u, 3996348146u, 1311927616u, 1686958697u, 766780722u, 1429807050u, 1546340567u, 1151984543u, 3172111324u, 2189332513u), - SC(3269764283u, 1288133244u, 1314904801u, 996741356u, 1884733412u, 1544206289u, 558284137u, 1518251699u, 1924323147u, 1635892959u, 1275016917u, 3776324356u, 1705865502u, 202621081u, 499067715u, 3311904259u), - SC(2660619816u, 3307703068u, 1451637465u, 3851776926u, 2364760323u, 1977782632u, 1515607226u, 1445106389u, 2327693248u, 2319920969u, 1115274896u, 1834441597u, 402374626u, 1205432354u, 1396686295u, 491780324u), - SC(1996097434u, 731516361u, 974312078u, 3421366629u, 3812294134u, 3978884039u, 3352635742u, 1797690428u, 13489496u, 1642706934u, 3128398168u, 106641350u, 4016459895u, 2470770670u, 115922099u, 2925890710u), - SC(2686884812u, 2748914055u, 1937433663u, 756783569u, 413219250u, 1566264233u, 3400883298u, 1726270584u, 1877719428u, 1988282262u, 4210071735u, 1623567192u, 186026227u, 1235988261u, 878101455u, 3591361377u), - SC(4053231115u, 4124107153u, 3534184341u, 1110486344u, 81952807u, 4125498697u, 1693462482u, 2990125452u, 3439709895u, 1055710168u, 4246237022u, 1943085528u, 719511299u, 700284484u, 1082914808u, 1529874921u), - SC(1481485493u, 1935423659u, 913226612u, 2395711383u, 1541429099u, 2771316424u, 3338417471u, 399999946u, 26796724u, 1562275554u, 2290450886u, 1574607684u, 2722372873u, 1229315759u, 1998792801u, 1299123352u), - SC(3949810665u, 1328858449u, 2680298883u, 4060684833u, 1165923991u, 2656262528u, 835037267u, 1633040358u, 3109606689u, 3612027263u, 1850965274u, 2501035455u, 1956880692u, 2989837601u, 2991272131u, 514909703u), - SC(3542886422u, 2995653583u, 3564619313u, 2091503271u, 1371789218u, 2765269616u, 3068810600u, 1666719265u, 2118314133u, 3335278251u, 3361418207u, 807286765u, 899334530u, 3994904643u, 2747385847u, 3528707340u), - SC(3132681349u, 3533155425u, 2330764867u, 3555018576u, 1500828005u, 1243623897u, 1071818853u, 2130356426u, 4099162373u, 1333917673u, 445413180u, 915835391u, 3998951530u, 3932499234u, 2014496944u, 1476384528u), - SC(2104877156u, 1430391164u, 3607724722u, 2456386351u, 3275987562u, 653382938u, 360082336u, 281545563u, 2556998173u, 802173963u, 1898654040u, 2873697709u, 3526274706u, 30023701u, 1532464389u, 335648001u), - SC(1216717657u, 3420164715u, 1026103527u, 2814363815u, 3399248527u, 2265457834u, 4230549954u, 3191596424u, 2096767009u, 197782440u, 661821193u, 3129199915u, 3603027595u, 571989255u, 3350141303u, 902722054u), - SC(86788496u, 2319129483u, 1051755765u, 871757145u, 3910221139u, 2373267495u, 991927221u, 3506242540u, 2918237538u, 555183593u, 3050652275u, 2550066259u, 1935622924u, 1141386013u, 1915989302u, 1193809339u), - SC(2961067645u, 912271025u, 3829956364u, 976054309u, 2426360429u, 3756714048u, 860863671u, 2976390123u, 651422564u, 3348472580u, 4062622529u, 3566918328u, 1262646615u, 526922344u, 336090107u, 3690353753u), - SC(1104160934u, 638409761u, 4090697585u, 3951520784u, 412890746u, 3037968225u, 623962484u, 1861465265u, 4172453316u, 2731726287u, 468253494u, 2636411583u, 2233875405u, 976659501u, 1885152597u, 441456529u), - SC(228814647u, 3127034711u, 536841111u, 970423620u, 335496573u, 1496573821u, 3839638808u, 2076574157u, 3960354230u, 1830746438u, 2136594363u, 1397484405u, 335074021u, 421124372u, 4043995000u, 1296743377u) -}, -{ - SC(2759056966u, 2773771898u, 915395955u, 378399267u, 1065424189u, 3786627878u, 2430240867u, 1910948145u, 1268823138u, 2460932406u, 2049702377u, 3729301642u, 2270156417u, 2935515669u, 1488232015u, 333167852u), - SC(3963231590u, 2717344665u, 3330507643u, 2069094492u, 1576271806u, 844971343u, 3725773593u, 3293220801u, 1933125411u, 1106657228u, 3650404527u, 3511000962u, 3309805512u, 23235466u, 884265026u, 3867812812u), - SC(2380535986u, 2007649740u, 291610222u, 4151143005u, 2330231880u, 3336494284u, 4079710776u, 3045731925u, 300175272u, 1753290057u, 2323446107u, 2448133203u, 1897525100u, 62520621u, 938748110u, 2483424933u), - SC(3941565796u, 4020457560u, 536627435u, 849338423u, 1622694903u, 2253013822u, 1890968103u, 2458058141u, 2431563444u, 3273994144u, 2920282564u, 2871620844u, 315460419u, 2331615405u, 105614140u, 3825521500u), - SC(1770365960u, 436268948u, 2889892729u, 3688514673u, 3952720709u, 1774783907u, 605504449u, 2947048934u, 38294098u, 846447109u, 2199988078u, 482652009u, 58745901u, 1043251865u, 1692020085u, 2977904741u), - SC(3749156389u, 3930496686u, 342096417u, 2961755248u, 1791611872u, 2622150301u, 1430397623u, 2049694734u, 1457522946u, 1307567328u, 1594457791u, 2920040322u, 2838823131u, 3221083429u, 2327375059u, 307491364u), - SC(439175999u, 704562179u, 1530705937u, 343762620u, 1895613568u, 82869187u, 23704978u, 3831637605u, 1611450850u, 923617677u, 3571146990u, 2520538539u, 2376639038u, 2377370369u, 3624250410u, 3615349574u), - SC(764309941u, 395778606u, 890380761u, 1156064327u, 244397938u, 560614464u, 4033284221u, 1090955901u, 3643294611u, 2912576497u, 772374999u, 2861631454u, 564730390u, 3124994653u, 646536012u, 3616789797u), - SC(3040822479u, 2767342245u, 2776280569u, 3485527708u, 3592541314u, 980436690u, 2153312390u, 215781809u, 2169043418u, 2501125521u, 3698439429u, 3999324854u, 2793459908u, 501030861u, 3583683133u, 3712651293u), - SC(4078810936u, 708788696u, 3557269243u, 3488736225u, 3893932756u, 4164798985u, 1241795187u, 3595203666u, 2393791384u, 3416169943u, 714289829u, 1522223608u, 2613922570u, 3640037692u, 3871460094u, 693107847u), - SC(2095442944u, 4280954881u, 166522183u, 982064125u, 4072843681u, 2413289870u, 966372633u, 3054322365u, 3306439070u, 657208192u, 175957468u, 411297739u, 771116169u, 1596617487u, 3454202820u, 2489020407u), - SC(1474971529u, 4158663721u, 2047384831u, 2598838221u, 256974012u, 2456523417u, 631366020u, 3323296862u, 3331748634u, 1360209248u, 3346726166u, 365777010u, 1290850614u, 2085594058u, 2979720197u, 2832663037u), - SC(1555709774u, 2326491405u, 2273744879u, 2585453209u, 2182701308u, 3405285511u, 2624534747u, 1273093088u, 862771016u, 2571185727u, 2627816705u, 753650915u, 1122934423u, 1670176575u, 3747348599u, 2369664950u), - SC(90900628u, 2102730721u, 781890942u, 2802660398u, 1018645876u, 4115262915u, 4149550831u, 3399458752u, 3886843346u, 2763694604u, 1310436099u, 1905281291u, 3814148817u, 4190880658u, 4069475791u, 3679310561u), - SC(2090876031u, 2877257381u, 2723690078u, 1430728835u, 1519931567u, 1820574481u, 3028789440u, 1269332520u, 487867652u, 423473929u, 386546855u, 57358783u, 1188070806u, 1428826466u, 1782333616u, 177182180u), - SC(1560550296u, 3093603077u, 293048812u, 568213435u, 3420818052u, 2217333393u, 3134601365u, 71485947u, 1184987600u, 3737951852u, 162939585u, 1604396734u, 102336303u, 398862141u, 820178097u, 490472018u) -}, -{ - SC(1198357412u, 890731121u, 697460724u, 351217501u, 1219769569u, 940317437u, 2678867462u, 4175440864u, 2131908090u, 1470497863u, 3243074932u, 494367929u, 1767796005u, 457609517u, 3543955443u, 4149669314u), - SC(3330984275u, 2556191310u, 3686726368u, 344917147u, 3386773283u, 2065247867u, 3908122913u, 3695674005u, 2012204991u, 2693522884u, 103992040u, 209624682u, 1376640025u, 3686868767u, 2902487256u, 913177313u), - SC(51667624u, 2920015049u, 3017253519u, 1071812123u, 2571723173u, 2160964558u, 1290623835u, 537361271u, 825729747u, 1392761590u, 1142623949u, 609149740u, 478665972u, 658807909u, 3553467330u, 1636424506u), - SC(3616504574u, 1808500084u, 668829693u, 946464586u, 1979729368u, 406956181u, 4175922839u, 412791377u, 2386664246u, 1192624407u, 2943858119u, 2548487829u, 1705793661u, 3457595727u, 202485393u, 1924721832u), - SC(2189382710u, 4186169698u, 1109472631u, 1983920883u, 3607145598u, 92147950u, 1402492489u, 429006982u, 2674194346u, 4283195956u, 1593180543u, 3760708566u, 643378372u, 4031840072u, 3394015175u, 1558737750u), - SC(1805700700u, 1754525187u, 1654624487u, 2216136944u, 68436239u, 2233918826u, 2968997668u, 4123197178u, 634669625u, 2517670383u, 3007433093u, 3522650191u, 696793327u, 1110232330u, 152147442u, 726198231u), - SC(742639492u, 3149716575u, 880320409u, 4630949u, 1505653181u, 1071542118u, 3069898832u, 2578767084u, 1314905164u, 2213468220u, 3680194608u, 2445142726u, 2802637025u, 3977804516u, 1184600151u, 419058566u), - SC(1336605659u, 403108152u, 2724587657u, 3679190711u, 2874389193u, 1647236788u, 3333657299u, 528273159u, 3515102004u, 947876802u, 3658623910u, 174276546u, 653934448u, 3828171172u, 1444811038u, 2933240663u), - SC(339431464u, 3233735983u, 2646677300u, 43177515u, 392637796u, 1436471495u, 1239428896u, 2348305406u, 2289915967u, 3084305790u, 3250948245u, 178888356u, 2146779246u, 4234024427u, 1032696742u, 3905672369u), - SC(961540617u, 2841143833u, 962675692u, 4171962245u, 2791421965u, 2368576296u, 3328980779u, 2916707843u, 1558316022u, 134331787u, 2460382133u, 1215270659u, 146717643u, 3198704598u, 2091590890u, 2460305557u), - SC(1042706599u, 2034894580u, 690504458u, 2345543782u, 4005260856u, 2432547988u, 112379796u, 3543073874u, 835904670u, 2590827554u, 918469413u, 3408148837u, 1789043194u, 1729294718u, 1834822488u, 2928788408u), - SC(3301658713u, 837504950u, 1727706187u, 1845900341u, 896114239u, 2352826711u, 3111232113u, 2017659422u, 2679415011u, 2370224692u, 3953323203u, 2250773775u, 1103871456u, 1933857783u, 3328123972u, 3307902309u), - SC(1767706194u, 3006067357u, 35851140u, 3240494485u, 2221989856u, 1899667734u, 6385932u, 2363969169u, 4105037265u, 1831329288u, 2027489194u, 884350865u, 1094001278u, 159320441u, 4110377537u, 68569781u), - SC(1525490260u, 665735034u, 2452169880u, 171203360u, 1236274187u, 676156893u, 1374080130u, 357190845u, 1839504596u, 1514713169u, 4060710869u, 1096636593u, 2588809028u, 3627704311u, 1809407212u, 476953361u), - SC(957000182u, 26105440u, 3440739633u, 2098069989u, 1584380370u, 2860012851u, 1732766592u, 212521659u, 3179187407u, 887560394u, 2490695882u, 2732057577u, 1018218231u, 3635922188u, 2062474881u, 2513446682u), - SC(1107263183u, 578424674u, 37103195u, 466969755u, 2523291988u, 291121216u, 3279675483u, 2003600853u, 4199013737u, 2715326244u, 4169142308u, 3686083459u, 3512922856u, 3093381668u, 1195683747u, 1393205701u) -}, -{ - SC(1331866444u, 3086683411u, 308412705u, 2554456370u, 2967351597u, 1733087234u, 827692265u, 2178921377u, 289799640u, 3318834771u, 2836568844u, 972864473u, 1500041772u, 4280362943u, 2447939655u, 904037199u), - SC(2575383612u, 3753748540u, 2811819999u, 1587868018u, 1038431720u, 790984055u, 3731301644u, 1846621966u, 951964491u, 415041564u, 2200992348u, 4272384400u, 296027191u, 4287888493u, 2854418940u, 3573682726u), - SC(1970740379u, 2607713160u, 3470587124u, 930264002u, 1173824281u, 122965335u, 3335069900u, 326806848u, 3632692886u, 129472919u, 3226625539u, 2728837633u, 416887061u, 1130551300u, 356705234u, 1369994655u), - SC(4223755401u, 2079062379u, 3389104769u, 4073338565u, 3689225172u, 440818499u, 856809827u, 381405275u, 127244068u, 376610605u, 2598268701u, 2534766433u, 2820385475u, 4294123141u, 330930335u, 318185845u), - SC(761419527u, 3536226585u, 2328998689u, 3591334816u, 1578134205u, 1103093801u, 3418753973u, 3588283844u, 1530820786u, 2684864777u, 924992522u, 3557568163u, 1869705595u, 3313643247u, 841618349u, 1632346896u), - SC(3475240082u, 1688964704u, 2950217939u, 2829510968u, 4218043142u, 1723444205u, 599182149u, 3585292920u, 1201476124u, 1461631424u, 3796636907u, 3015591958u, 325310290u, 4221903599u, 2685464188u, 843835594u), - SC(3270571096u, 3849271420u, 2838244847u, 4029431364u, 3703574760u, 3266810236u, 1964057057u, 1045028730u, 3535646880u, 4117469088u, 268273252u, 28527135u, 616206627u, 3498685014u, 1783632491u, 2430589238u), - SC(1270864764u, 2335784868u, 3187652054u, 3487500065u, 3514696661u, 4279511860u, 2691960889u, 1283768022u, 3239440117u, 3088430000u, 3270700109u, 2562105500u, 920167200u, 797042551u, 4008345612u, 1713652205u), - SC(1233553764u, 2449552413u, 3139739949u, 2886523083u, 3648218127u, 435238208u, 231513377u, 3598351734u, 1003225207u, 1550611030u, 4262337852u, 2819804714u, 3244463273u, 2073740987u, 855086785u, 975917304u), - SC(2715954175u, 3495328708u, 4029028922u, 3684471179u, 2815956881u, 3599669751u, 4163140273u, 33191313u, 2635890672u, 3683103094u, 1579697202u, 287936530u, 2496546027u, 832886459u, 1241267398u, 3564329642u), - SC(718666875u, 1628061148u, 3834972005u, 11037458u, 3790987439u, 2312775807u, 3375415349u, 3089087440u, 2679862136u, 918687461u, 3176925215u, 1435039099u, 1342114588u, 1906963252u, 3488735014u, 1611160706u), - SC(4216184459u, 1084561028u, 249927207u, 3584932419u, 1355984265u, 990857900u, 1870305536u, 582023708u, 1966962179u, 1733088207u, 1190083164u, 3785297292u, 1004947745u, 1784159416u, 1841702516u, 180335137u), - SC(4084089742u, 2441136551u, 426220168u, 1375299216u, 1841338030u, 1250354698u, 2728864721u, 2959990011u, 1071025467u, 1691914484u, 2858760972u, 1516700275u, 2771651049u, 607063247u, 4219381388u, 3373946171u), - SC(2146554811u, 2380633398u, 431356428u, 2501496525u, 4195490782u, 4281443977u, 1707183170u, 3515016439u, 43334925u, 2064458077u, 4149827026u, 2544422546u, 1259302114u, 1919625668u, 729425798u, 2757346641u), - SC(2475010648u, 501654469u, 1262984133u, 2284058265u, 3864896735u, 3216144340u, 3043718887u, 3290359029u, 2513504704u, 1583873907u, 787550022u, 889877880u, 4155285556u, 2519357244u, 1887123831u, 2544852082u), - SC(1329107374u, 3899397847u, 1931705980u, 3537599611u, 2074239136u, 1267070685u, 2447524924u, 3173107761u, 2842541385u, 924561908u, 2664553616u, 395476463u, 813764142u, 3107511895u, 179660379u, 2380654703u) -}, -{ - SC(286197159u, 1217476806u, 1373931377u, 3573925838u, 1757245025u, 108852419u, 959661087u, 2721509987u, 123823405u, 395119964u, 4128806145u, 3492638840u, 789641269u, 663309689u, 1335091190u, 3909761814u), - SC(2458775681u, 3448095605u, 3846079069u, 1243939168u, 2712179703u, 2514528696u, 1400411181u, 3792085496u, 528921884u, 1230512228u, 4062090867u, 931590129u, 3669288723u, 1764179131u, 2650488188u, 764612514u), - SC(3981461254u, 1881876860u, 3861653384u, 1419940889u, 3890280301u, 225359362u, 3772709602u, 2406778923u, 1744011295u, 836946168u, 1547583643u, 2969842237u, 3997288340u, 2150480638u, 3129156617u, 1325216902u), - SC(3592470591u, 3671101194u, 2792523734u, 2070472959u, 1473838345u, 785123121u, 2721504084u, 2212009910u, 4070989896u, 1696639999u, 2859248441u, 3104578877u, 2309769016u, 4267049236u, 2484173427u, 1626540609u), - SC(4267160019u, 2981312649u, 344263087u, 698599319u, 1002907346u, 93565259u, 286808078u, 1804582990u, 3599771325u, 2181306538u, 1961279765u, 187428107u, 223299791u, 4043449191u, 587626985u, 2106033479u), - SC(501761768u, 2386293097u, 1180388710u, 1812775472u, 918601490u, 3009070794u, 1574279477u, 1505824867u, 3643095372u, 3370828988u, 832869144u, 404837899u, 3152252263u, 3925885097u, 69867335u, 3741018586u), - SC(2051920526u, 1020215512u, 2058830843u, 1611771091u, 2552120098u, 75944844u, 1802229404u, 915313553u, 2313215016u, 1745739579u, 443475191u, 2998247588u, 3289885130u, 1289464560u, 2961919458u, 3798282256u), - SC(1496487624u, 2215532014u, 4148657376u, 3923080315u, 216179279u, 3856996518u, 2014567019u, 880786726u, 2125033974u, 58008256u, 4039109547u, 402585883u, 2182540617u, 437175766u, 1441865826u, 1665450276u), - SC(3078919323u, 1109978808u, 3102316446u, 4252174800u, 1046362670u, 3864571927u, 2260100326u, 3682270765u, 2139319322u, 1066628173u, 240059747u, 1164853046u, 1454716611u, 512654137u, 1544275853u, 2556727566u), - SC(580428655u, 115762757u, 1593355348u, 2740341778u, 1504897999u, 975028678u, 2401832824u, 4197869940u, 3667767462u, 644880229u, 691878327u, 369150353u, 4026243769u, 737605979u, 2791271214u, 2620684209u), - SC(624678531u, 4114750403u, 1274989179u, 1531504358u, 3520816024u, 2554021149u, 1865577096u, 1362433716u, 1638936249u, 3016959317u, 2526207810u, 3033412199u, 695904139u, 2060012285u, 3230414132u, 860289224u), - SC(3442642063u, 1520946900u, 218826564u, 968761561u, 4098434233u, 3360677602u, 2204368028u, 486310067u, 2601372374u, 1399175099u, 2183933043u, 806379489u, 2424203087u, 2668736829u, 1664637882u, 3005713727u), - SC(700899790u, 1066183324u, 3546718434u, 998702102u, 2557230354u, 2084117292u, 2934243163u, 1545771642u, 3688392810u, 3908656537u, 3447657276u, 840000010u, 2955752477u, 44371204u, 3799655472u, 3734995825u), - SC(3265506533u, 942399325u, 173917125u, 161041810u, 2297418901u, 849604788u, 2703870825u, 2810175425u, 3617296913u, 1432689375u, 3133875354u, 1118654553u, 2616257301u, 495686053u, 4127407123u, 1943733376u), - SC(2005668850u, 485568946u, 2260461782u, 2622034876u, 2693998905u, 2811925574u, 2831747304u, 3217266392u, 2520502878u, 1176196783u, 2567958416u, 1525744035u, 2841811417u, 1157609637u, 3871707993u, 2765099676u), - SC(207989197u, 368293876u, 3237374184u, 1394768686u, 1254103141u, 935691540u, 375090092u, 2481205522u, 2920254212u, 492683984u, 2055637221u, 4291235240u, 3889542314u, 2465899605u, 1694380507u, 757371549u) -}, -{ - SC(136266275u, 1782161742u, 3530966629u, 586004249u, 4076565170u, 3312577895u, 876489815u, 1337331291u, 888213221u, 1813863938u, 1374206604u, 2668794769u, 1377764865u, 784024905u, 1937217146u, 3627318859u), - SC(3161427495u, 2344678392u, 1808682441u, 2396619894u, 3034006140u, 1044331129u, 4102609084u, 1058091322u, 1515502621u, 1258860285u, 1406233340u, 127619173u, 3057107171u, 225762630u, 1651671815u, 4285298193u), - SC(630785468u, 1344100570u, 1929331818u, 828088181u, 2313124884u, 1302120759u, 3180735860u, 313275450u, 1008942268u, 2707820177u, 4248947940u, 1732478629u, 3645496831u, 611830707u, 1937638387u, 61731419u), - SC(1347537282u, 2857000226u, 227299159u, 1108544547u, 1181072563u, 1291715943u, 3752803919u, 2688390945u, 2484326219u, 1350060758u, 452823659u, 2363636452u, 2152205190u, 1812507720u, 607624535u, 2319475408u), - SC(3222638329u, 3875752446u, 758301165u, 51152840u, 2430504171u, 1189996379u, 44948392u, 232960619u, 3026371583u, 2974537914u, 3244781723u, 3702394182u, 2835938901u, 663347918u, 3320069474u, 3071978352u), - SC(1947047272u, 3022037725u, 949698504u, 1728470528u, 283847009u, 1458268020u, 360012619u, 1579646653u, 4005878207u, 1765381301u, 20903539u, 2558445559u, 757888638u, 2604781527u, 2240457927u, 3990518442u), - SC(4281545336u, 1208697934u, 2578865021u, 2456188396u, 1796646478u, 3757714293u, 2622755030u, 1606025966u, 30472258u, 3850691354u, 1208779266u, 405050222u, 3807844323u, 3748806955u, 358470323u, 4212845387u), - SC(2041619043u, 3711576883u, 835794591u, 2392116351u, 2862318436u, 689502669u, 2866163103u, 2052898811u, 576580608u, 1144506306u, 542475550u, 474572979u, 4137279429u, 2221684538u, 331268239u, 1556318477u), - SC(705880713u, 2092991958u, 815360595u, 3449491044u, 1305192012u, 2057063005u, 3299868133u, 1114733861u, 730760330u, 1129737257u, 4233249504u, 1217580888u, 452658791u, 2612091783u, 1764043106u, 1669202162u), - SC(3689992902u, 700129090u, 282055655u, 756126609u, 382876308u, 4262209576u, 2436932760u, 484247369u, 1415138625u, 2340918814u, 3058199817u, 4145497883u, 334812059u, 461523021u, 2221122791u, 2995497332u), - SC(706669295u, 3007808000u, 3728730665u, 3241577762u, 3126001367u, 292940936u, 1126531898u, 3913205978u, 304146054u, 2548053118u, 3490807704u, 3465095661u, 3938930443u, 804039554u, 297557674u, 1669808877u), - SC(2395818908u, 3199065200u, 4060875213u, 1731284266u, 1022607637u, 1154299144u, 3879751917u, 384430926u, 86892497u, 2036004815u, 2668116514u, 901861508u, 2277490553u, 1312485879u, 562264334u, 170374972u), - SC(2192479620u, 3046309306u, 143307916u, 3468295982u, 3110013374u, 699221760u, 273412494u, 3153322038u, 2886126025u, 1296005576u, 2326933823u, 3713038344u, 919578907u, 258326637u, 1991591857u, 604405680u), - SC(3283196708u, 902217854u, 1295144146u, 503984315u, 566424671u, 1755595238u, 2455519229u, 120267530u, 1004363245u, 1611271287u, 1013059281u, 3646183010u, 183890924u, 188417891u, 1612883046u, 2255154239u), - SC(1231171449u, 2524105034u, 653815517u, 585754026u, 3098352226u, 866901449u, 4223318963u, 1071806142u, 3239364285u, 4077877700u, 423690458u, 2222266564u, 4117269051u, 1893556406u, 3304547745u, 215164118u), - SC(3229321461u, 3443938850u, 803179772u, 3340311630u, 2749197592u, 565049216u, 1674980657u, 45735981u, 3858875409u, 2208179057u, 2167864606u, 3853383863u, 3320158569u, 901453102u, 2505912317u, 1486241881u) -}, -{ - SC(768143995u, 3015559849u, 803917440u, 4076216623u, 2181646206u, 1394504907u, 4103550766u, 2586780259u, 2146132903u, 2528467950u, 4288774330u, 4277434230u, 4233079764u, 751685015u, 1689565875u, 271910800u), - SC(2894970956u, 471567486u, 2880252031u, 2717262342u, 4077383193u, 1268797362u, 4257261832u, 2560701319u, 2691453933u, 1607372210u, 2771176414u, 58794458u, 4272438220u, 2521311077u, 642919262u, 3613569198u), - SC(549667688u, 1635817891u, 3597742712u, 2133548191u, 983618585u, 1077056145u, 1016537981u, 3024916594u, 3788763915u, 2354027825u, 234019788u, 1129974745u, 3836449602u, 132091652u, 2429034711u, 3714188356u), - SC(3752023309u, 1237246457u, 810507218u, 1575719630u, 2984629402u, 1312110059u, 1532351529u, 3778270553u, 500991970u, 3016414634u, 2451804626u, 3116044735u, 2749076428u, 609078974u, 343845623u, 1628221103u), - SC(1079050562u, 537097107u, 2113045556u, 1216978919u, 795109794u, 494396817u, 3615304214u, 3016596136u, 1485503229u, 2246940765u, 2872639209u, 812577075u, 3970992077u, 816616346u, 4279493103u, 2696304890u), - SC(302016674u, 1709668681u, 88411267u, 3337357281u, 3061995584u, 3396993199u, 1858891069u, 2509301562u, 3807375387u, 3567949934u, 3737724046u, 4137514111u, 1709156749u, 1400722499u, 3253197246u, 830289695u), - SC(86642997u, 2517748533u, 1802616926u, 3224858276u, 667521935u, 294768443u, 3699185630u, 2619978653u, 1654256627u, 789295435u, 4056501046u, 2298266369u, 3425028365u, 3740463800u, 2064449616u, 423401599u), - SC(587205175u, 208206623u, 1253389730u, 3674422134u, 284316357u, 2112208954u, 1196434050u, 302049830u, 985808817u, 4037289748u, 2191325460u, 4289570719u, 592322138u, 3671063901u, 886295122u, 2540475213u), - SC(2164961127u, 4048157441u, 2790139366u, 1435011700u, 4142835891u, 3320410016u, 2681849481u, 1047872443u, 2885564134u, 874029678u, 2048520878u, 2934385850u, 1097367713u, 1997417466u, 2045706034u, 898129538u), - SC(3451958921u, 95403444u, 4056502814u, 671939501u, 2069116441u, 3101129770u, 553516228u, 1712496197u, 2639919391u, 3157824758u, 2182076931u, 2920510603u, 91421090u, 3496854290u, 1333938225u, 2005754623u), - SC(469295760u, 426796598u, 3855795018u, 970866434u, 856973549u, 2439780350u, 2385957015u, 2589908140u, 3781058972u, 4109407963u, 32316753u, 3931244779u, 68560366u, 1699148814u, 843806029u, 3772908229u), - SC(3846833357u, 4119412096u, 438094070u, 2645426661u, 884548695u, 2876447138u, 80918210u, 2029354870u, 135282137u, 3030947473u, 2960763605u, 1898348122u, 4127316996u, 2240743006u, 2934791826u, 887094286u), - SC(1897883656u, 1406242187u, 2434671426u, 2734794757u, 2714201131u, 3046668149u, 257451999u, 3794951424u, 152449195u, 3454838096u, 2737741298u, 821046884u, 2554260361u, 962889686u, 1262263641u, 2203109889u), - SC(1985684731u, 222483668u, 2849949193u, 1221492625u, 2084499056u, 1235444595u, 2655267198u, 1020186662u, 1447071023u, 3629752849u, 651251319u, 2167418603u, 2268535831u, 2985934672u, 2652239173u, 3259021212u), - SC(3062826974u, 1796450254u, 1939504794u, 476729966u, 3521076442u, 3086668105u, 234121934u, 986487065u, 1570879569u, 2820662853u, 1206879400u, 4271520206u, 4242315964u, 2749978648u, 3007865079u, 4114755771u), - SC(3649818358u, 3409857055u, 1537210569u, 2398557069u, 3130583052u, 536941530u, 3880813719u, 1419070102u, 1164730147u, 2533104753u, 2046210979u, 2821557175u, 2327264610u, 1639358616u, 2001893732u, 1524105344u) -}, -{ - SC(294473811u, 4198428764u, 2165111046u, 977342291u, 950658751u, 1362860671u, 1381568815u, 4165654500u, 2742156443u, 3373802792u, 668387394u, 853861450u, 2637359866u, 2230427693u, 2824878545u, 103849618u), - SC(3462974251u, 3960356708u, 3970663027u, 1911703734u, 2602955995u, 2496279357u, 210580885u, 3874806640u, 2822070051u, 4063068709u, 2061277285u, 1429537360u, 2349584518u, 2910686068u, 3963567776u, 3972103816u), - SC(2016723458u, 2541590237u, 3532225472u, 3001659539u, 112442257u, 922189826u, 2246032020u, 3487464820u, 1658786807u, 2276379919u, 1596562072u, 457926499u, 2193005220u, 2575074329u, 529788645u, 1519231207u), - SC(1572936313u, 886315817u, 1530415140u, 2311860166u, 3941188424u, 45807153u, 2483174955u, 1469805839u, 3162970586u, 2454510043u, 2417743140u, 2783896043u, 4229304966u, 1351489836u, 284407686u, 4050060666u), - SC(1089549454u, 2684562245u, 1059803961u, 224950790u, 58262787u, 3033299806u, 927475933u, 1400133226u, 3082832878u, 1490904482u, 3040968407u, 593844137u, 1569781919u, 798746464u, 1083127814u, 1590280691u), - SC(1538536818u, 1828650047u, 3754703497u, 985555578u, 1002045074u, 767791702u, 915104522u, 465342914u, 1114045622u, 3426575950u, 1922317875u, 1070157234u, 3077282627u, 509171365u, 1607316331u, 668038565u), - SC(3323765415u, 1224391265u, 2469548057u, 3722781348u, 3031269370u, 4289586349u, 2226931390u, 957179955u, 2298143215u, 388542993u, 1780793152u, 2112973240u, 1502081645u, 1973971844u, 934878133u, 1618693887u), - SC(3954817210u, 3380652139u, 2572526672u, 1228436929u, 465848053u, 3939966705u, 2398020514u, 2900599831u, 2007674400u, 2727714272u, 2337519533u, 1681172994u, 4089802218u, 142069883u, 4261364192u, 2856729470u), - SC(4248537414u, 694781904u, 571619480u, 3221145068u, 2970038253u, 3370542615u, 2832314379u, 1807587465u, 1411648700u, 1964173012u, 121911610u, 1134463822u, 2574507072u, 885427058u, 3741638072u, 3097389771u), - SC(2158675312u, 116080836u, 3333803512u, 3797833536u, 984464391u, 4149942538u, 1145746749u, 1195624987u, 426540232u, 1021913877u, 3121679962u, 3390873776u, 3273678689u, 3851165262u, 4274383191u, 1915176720u), - SC(1158541955u, 1843489443u, 998849897u, 969171492u, 1791167915u, 2484857096u, 1119081920u, 1901041264u, 2534183757u, 1529097558u, 2956376281u, 1260291681u, 1159207651u, 3441978306u, 2518693280u, 4253362775u), - SC(1690661001u, 2213259738u, 3615956917u, 105152953u, 308358176u, 1328282355u, 1666389191u, 1019854259u, 2059193948u, 4244545599u, 1952864052u, 329670934u, 3592985517u, 571024701u, 1172799188u, 3135874872u), - SC(1184018396u, 889004172u, 1920099477u, 1964506637u, 189152569u, 1805931691u, 3250067608u, 3446883320u, 1471577127u, 2315956523u, 1588897116u, 2470229082u, 3602241877u, 554726955u, 1644067322u, 87402371u), - SC(1360270758u, 326216664u, 3362619326u, 1255989535u, 4140691901u, 856602972u, 2084629207u, 3858539838u, 78510889u, 2277092409u, 3136284616u, 1772786459u, 3229606238u, 94732571u, 2598206327u, 492226777u), - SC(1257123658u, 2873597433u, 3001150814u, 421725801u, 236310867u, 582305583u, 3367057659u, 2102668336u, 153914902u, 4226436363u, 290094468u, 690656835u, 1748591179u, 3668885459u, 165028339u, 2139821087u), - SC(2349582063u, 631395785u, 941018791u, 1503410647u, 181331585u, 2473834542u, 2528647747u, 3710284323u, 2364124560u, 3901998444u, 3224972026u, 605068436u, 546878913u, 356944705u, 3829683853u, 160452346u) -}, -{ - SC(1451965994u, 766802222u, 1324674662u, 350355960u, 2823290314u, 951779387u, 2914020724u, 508533147u, 1932833685u, 1640746212u, 1238908653u, 542788672u, 3642566481u, 2475403216u, 1859773861u, 3791645308u), - SC(216282074u, 1906267522u, 1852437064u, 1010678235u, 3729121535u, 4197231849u, 4150055440u, 1128246703u, 3264673345u, 1375783733u, 3415088931u, 34309836u, 2603881793u, 3106237815u, 2950890176u, 505684202u), - SC(3927516830u, 2488673756u, 327917152u, 614182630u, 2355346359u, 730432873u, 88446505u, 4240960753u, 4121410433u, 1398090547u, 2262743232u, 651724036u, 4138228417u, 3106475766u, 4179362424u, 750466827u), - SC(434692713u, 3111300976u, 3323560909u, 3413395188u, 601658363u, 2967722170u, 1070605430u, 74966422u, 813799229u, 4061279746u, 1996953298u, 1765274397u, 4035137864u, 2359104373u, 3535793255u, 618634298u), - SC(1231617791u, 3545122377u, 2628213180u, 2391855988u, 3734909337u, 2705206020u, 681643510u, 368801430u, 691450613u, 2224147576u, 951972679u, 2767063862u, 3676868191u, 158497152u, 2165075628u, 2832330233u), - SC(3529008459u, 1174295398u, 55914117u, 2816083797u, 205887723u, 1756010196u, 1648915894u, 1477354329u, 86311333u, 3889682737u, 1098085375u, 3464880379u, 1139759451u, 542536350u, 186494667u, 2442759451u), - SC(3094023174u, 1995851063u, 4191388160u, 1722723757u, 1329293492u, 727282912u, 2669776257u, 2772951118u, 1386276034u, 3089621174u, 2303649396u, 2292749559u, 1467806712u, 266878652u, 2651863592u, 1006978704u), - SC(2450691869u, 3012269556u, 3887712993u, 4048656504u, 2160727935u, 1940770088u, 174916584u, 3472792113u, 2648524840u, 990354037u, 1957678544u, 3888925732u, 1168435347u, 3720532709u, 3528212798u, 2624020545u), - SC(69863181u, 2459013627u, 4217968964u, 2735851825u, 1081344097u, 737361378u, 2157825722u, 2900791120u, 1412000158u, 1206005337u, 3067055303u, 230632577u, 601427243u, 2760861753u, 3679310020u, 2091861010u), - SC(2304197829u, 1531316041u, 2716383108u, 434697890u, 508817514u, 2929310544u, 3751532879u, 3785491984u, 2716598214u, 3666495867u, 3150261948u, 1306653078u, 2283636929u, 2492138954u, 1527136744u, 3312103429u), - SC(3387483809u, 1095455990u, 3248396980u, 3181117152u, 2258888938u, 2053848664u, 2160875912u, 553275695u, 1752757914u, 1504034431u, 1046528434u, 1855690339u, 2425857774u, 2142030048u, 237252438u, 3919745098u), - SC(3690358562u, 221287988u, 2268047572u, 3655202989u, 756646724u, 68846869u, 1965143185u, 513684595u, 404949341u, 3706987369u, 15990563u, 3409604325u, 658214808u, 2112012281u, 1742449680u, 1802932879u), - SC(2972942716u, 4184192946u, 4124576773u, 3089123761u, 1179063207u, 2093485395u, 512951348u, 59239037u, 3674464770u, 787225894u, 1288484371u, 1987692265u, 3767465580u, 4044585132u, 2916653148u, 2297816723u), - SC(3784876742u, 1057734114u, 4078669159u, 2003536621u, 3146165592u, 3800656487u, 297129408u, 4248472894u, 3906942491u, 4017607636u, 1285879766u, 3310681130u, 2653159866u, 2524355569u, 84128323u, 2374174391u), - SC(1598027967u, 344901367u, 413901309u, 2414916476u, 417612014u, 1371467558u, 1499802638u, 967537237u, 1571117481u, 1088564682u, 3141693657u, 833402800u, 723113978u, 882224086u, 3586817872u, 3592950853u), - SC(513582137u, 3376206006u, 3649593908u, 274710963u, 395026609u, 3340190413u, 1543782101u, 90195397u, 4157807658u, 412153222u, 558068169u, 2001737608u, 3474337160u, 1679447360u, 12885220u, 843004632u) -}, -{ - SC(2083716311u, 321936583u, 1157386229u, 758210093u, 3570268096u, 833886820u, 3681471481u, 4249803963u, 2130717687u, 3101800692u, 172642091u, 421697598u, 4220526099u, 1506535732u, 2318522651u, 2076732404u), - SC(3635330426u, 3675180635u, 4282523718u, 1750526474u, 1682343466u, 1292539119u, 2893227939u, 2897346987u, 1855384826u, 3916002889u, 4211021149u, 3439442996u, 241993264u, 1634586947u, 29890244u, 2635163863u), - SC(2111268073u, 1081371355u, 3873218083u, 4044562588u, 2141674529u, 2107952064u, 3689043955u, 3423481956u, 2548188353u, 2697516682u, 4235866514u, 2985306600u, 3687062917u, 2383095614u, 206503719u, 2548448480u), - SC(961167287u, 839569057u, 3482959339u, 4268254472u, 364097642u, 1343091094u, 3226753483u, 2159507482u, 3968394805u, 2518014496u, 3451298154u, 38127252u, 267735247u, 3484363065u, 957363479u, 1698662790u), - SC(2744437828u, 3863759709u, 3010153901u, 3500431594u, 2624982656u, 875272695u, 1378345519u, 1791692262u, 3726226549u, 2682325366u, 3925052276u, 389591343u, 3869112658u, 650251545u, 6263093u, 860194434u), - SC(309822299u, 841707800u, 2661553828u, 3383039256u, 238699224u, 1100968507u, 3534897900u, 4177846894u, 3463859410u, 1435499569u, 2006933774u, 3007046995u, 2819231184u, 288756524u, 1854189890u, 3858081977u), - SC(2088052675u, 3396090720u, 416722812u, 2597822221u, 1176386826u, 3290882216u, 1002529034u, 2156491632u, 4202546863u, 1988253003u, 164033721u, 941800849u, 1186836065u, 2298291750u, 1863561032u, 1437279190u), - SC(2858016010u, 775169843u, 2706497878u, 2821546952u, 2660836656u, 2077717717u, 3498848893u, 658545289u, 4048269927u, 418273988u, 1144587321u, 3094511386u, 4122354470u, 4225741678u, 603926280u, 979427875u), - SC(1933550557u, 635706492u, 1314164193u, 391588743u, 834468642u, 1475393570u, 467867971u, 1271027212u, 2540684860u, 3801872764u, 1235100171u, 2159823063u, 532708943u, 665828867u, 4215955726u, 3885758496u), - SC(3602864699u, 4002116109u, 644187852u, 1895585048u, 2776091504u, 72205071u, 554242761u, 4049640413u, 3149249833u, 688714164u, 687706448u, 3680924185u, 2274039047u, 303853541u, 2977107717u, 1196398757u), - SC(3014099531u, 1302405838u, 17960870u, 4110705157u, 3801652109u, 2085339416u, 223612049u, 2870889264u, 3353629397u, 3527061798u, 674241336u, 3525864585u, 2278818471u, 2069831593u, 2885891701u, 1329881521u), - SC(943450806u, 3704544104u, 3603194299u, 3757910007u, 502151885u, 765197432u, 4190577627u, 771063523u, 2436865367u, 678307964u, 1498061278u, 4120830837u, 3369466394u, 3399332765u, 1670894068u, 2891073104u), - SC(501595739u, 1876059299u, 4182005344u, 160804770u, 962098784u, 2636270989u, 1828906496u, 1316975808u, 4088133273u, 2943366134u, 216957582u, 1003216568u, 4242258589u, 3505873185u, 2810125978u, 3429220861u), - SC(2021386647u, 4046435053u, 1951135097u, 3941871277u, 2261999657u, 3808836272u, 2028063026u, 3659044589u, 3595750274u, 34514326u, 1889867282u, 1898224864u, 1659225476u, 3153868894u, 1647148554u, 1185039302u), - SC(4119269244u, 1304843028u, 2354051818u, 2031439365u, 533555049u, 1418960734u, 214120313u, 4187370667u, 4256529561u, 2635160409u, 1836564249u, 3828261559u, 3235640513u, 181194540u, 4018312346u, 680914749u), - SC(1914329770u, 3317667974u, 1413160514u, 2952053282u, 3332782151u, 3751637695u, 2146129829u, 167804454u, 2499496888u, 4213150810u, 223599992u, 2197202825u, 2869811316u, 2635473358u, 952082661u, 1532017334u) -}, -{ - SC(701959589u, 2450082966u, 3801334037u, 1119476651u, 3004037339u, 2895659371u, 1706080091u, 3016377454u, 2829429308u, 3274085782u, 3716849048u, 2275653490u, 4020356712u, 1066046591u, 4286629474u, 835127193u), - SC(897324213u, 739161909u, 1962309113u, 3449528554u, 2634765108u, 226285020u, 2832650161u, 324642926u, 2242711487u, 162722959u, 2264531309u, 2307017293u, 4006636248u, 1035416591u, 2557266093u, 3957962218u), - SC(1912896448u, 699621778u, 2975109255u, 1580597872u, 2818493758u, 515803157u, 1642586345u, 785148275u, 2098287545u, 1424779842u, 1039209855u, 4238164284u, 4173562747u, 3569896384u, 1089361492u, 1858690350u), - SC(2757340308u, 2538321018u, 2388474793u, 379482919u, 882562385u, 3129659692u, 4216198588u, 3565768337u, 1772023241u, 2931080253u, 3451485646u, 748689895u, 562737327u, 663797632u, 3315310934u, 2629536884u), - SC(242169331u, 1243063456u, 175561111u, 2950276224u, 3213816292u, 692329775u, 3181354285u, 3015261169u, 1744760252u, 3733849950u, 4219512025u, 693702734u, 2844842003u, 722286940u, 2391355922u, 3564773447u), - SC(2291286292u, 966238959u, 506903622u, 2122264528u, 1392182009u, 3447321781u, 3873294792u, 1373792940u, 991667700u, 2332723711u, 2764968211u, 2471301595u, 649629323u, 783169152u, 1459916213u, 3846736182u), - SC(2664330880u, 1149932862u, 1416201114u, 318583284u, 4140857901u, 1128356267u, 1095497693u, 1624736741u, 761312690u, 241788645u, 2036924781u, 1946525101u, 3225208750u, 4156033061u, 2590150721u, 3771407135u), - SC(2862143077u, 233168744u, 2659004990u, 155440145u, 3918377979u, 1360152661u, 627903232u, 1469886352u, 2876841580u, 3955906097u, 580277652u, 3039511497u, 1597126708u, 1404269416u, 42059925u, 2098341602u), - SC(812381463u, 3272442363u, 496180006u, 1236237424u, 2267310113u, 2237850197u, 1113026387u, 716498059u, 3503382440u, 328287114u, 1410789607u, 477863076u, 1362085890u, 3569642059u, 2006757845u, 675415451u), - SC(747557402u, 4212477852u, 3286869720u, 3708058361u, 3240421074u, 1188732842u, 916816078u, 2444327052u, 2111479336u, 1745064524u, 3637408011u, 3599633029u, 4230973048u, 1160089497u, 1136388910u, 4138160782u), - SC(1255139572u, 1856599651u, 1458352865u, 3271906169u, 3410637086u, 2119040671u, 1680850868u, 413922813u, 2782309328u, 3561735700u, 3723648708u, 609378416u, 268989415u, 3293584485u, 3271843364u, 1954072630u), - SC(4155626312u, 931793228u, 1049414704u, 1037617746u, 265265177u, 616902615u, 844384832u, 3477591939u, 3106685802u, 2357099686u, 1845236259u, 3355104451u, 3327830357u, 3100545339u, 1162051156u, 2646331847u), - SC(514329180u, 948073745u, 1774920952u, 105860125u, 2811186644u, 1695131452u, 940976033u, 2019732362u, 309099076u, 1607914408u, 4118428245u, 1337868060u, 3952860679u, 2578427283u, 265792106u, 295755030u), - SC(3882528435u, 2629929072u, 1617404150u, 1421619579u, 2309432083u, 724299897u, 2666040048u, 1096383838u, 1836447402u, 426930713u, 3934220119u, 3232225281u, 1000075862u, 3631628825u, 3529619355u, 1219322120u), - SC(3335633324u, 4194223138u, 3901817518u, 1335914529u, 3871871049u, 3709757137u, 3499113177u, 235348888u, 781652835u, 1102256292u, 3754223033u, 833068853u, 4178470716u, 1807198743u, 2733399861u, 3740356601u), - SC(228568838u, 3126580587u, 4000897922u, 1303869372u, 3850020302u, 1548458239u, 2356371812u, 3570971356u, 2544858219u, 4220062752u, 2062616152u, 953792592u, 764216612u, 2052428514u, 2314665964u, 2792116584u) -}, -{ - SC(2022030201u, 622422758u, 4099630680u, 255591669u, 2746707126u, 492890866u, 1170945474u, 626140794u, 2553916130u, 3034177025u, 437361978u, 3530139681u, 3716731527u, 788732176u, 2733886498u, 780490151u), - SC(4207089618u, 3411945447u, 1960753704u, 3552759657u, 1130668432u, 848791484u, 3810908171u, 353148861u, 3312275539u, 2963747704u, 2966813687u, 2483733320u, 2880725255u, 463405312u, 3340834122u, 1292390014u), - SC(2664721153u, 4108676217u, 2604619822u, 775242570u, 636236518u, 2873717047u, 1857718302u, 2091477716u, 1586310695u, 2528697445u, 2256487867u, 2787362203u, 2741360704u, 496928924u, 601271512u, 3586110309u), - SC(1791685197u, 4242641311u, 3369628733u, 2052809939u, 806398185u, 3412279529u, 1946210627u, 1398934260u, 3077042954u, 2276630414u, 814388665u, 1749609309u, 3367688729u, 1959714965u, 2411157301u, 2263996211u), - SC(439326213u, 4256425445u, 876987216u, 1314194194u, 3010100734u, 1576065730u, 598365157u, 3705087566u, 3427486218u, 1877721147u, 358249820u, 410263983u, 1386735339u, 573015435u, 3312164843u, 1274000474u), - SC(1340417963u, 1112802360u, 10328826u, 706586684u, 2526013892u, 4135069035u, 3566832565u, 2945858092u, 107866747u, 2114273476u, 1970904771u, 965191541u, 1793617219u, 1453495760u, 4269949644u, 41605060u), - SC(123137558u, 4245690796u, 820317976u, 1443287541u, 4203849632u, 2954045926u, 714382464u, 3076066234u, 1293485113u, 2554869888u, 1663243834u, 1823619723u, 3832632037u, 2772671780u, 1362964704u, 558960720u), - SC(104412626u, 1897841881u, 4081037590u, 3456756312u, 3025873323u, 2036419348u, 663042483u, 1254379139u, 1882881825u, 3296543036u, 153313200u, 916960321u, 2276001640u, 759388499u, 1134495268u, 1699779658u), - SC(4218137867u, 889442133u, 2322944798u, 2659784159u, 2592614267u, 3345396604u, 3647495000u, 2837331949u, 75759322u, 2350992064u, 2461684340u, 2333444962u, 60872001u, 106935728u, 2095087192u, 2026584532u), - SC(818402121u, 2851948581u, 2197490142u, 4158011576u, 1665124994u, 3116095068u, 4019154383u, 478938546u, 1455910301u, 1844755722u, 2818772446u, 2743310120u, 1907022363u, 1639658700u, 517605614u, 2705809838u), - SC(335193145u, 4147885949u, 3527556636u, 2575925391u, 2530836608u, 2938195122u, 3771589905u, 2663025172u, 4017017665u, 2146447634u, 3974365403u, 2994000421u, 3198356067u, 3382731724u, 2593683495u, 3554902256u), - SC(1108422413u, 1982378939u, 2047758090u, 246779179u, 2568353687u, 279750626u, 1730233650u, 784289836u, 2712478714u, 3614283837u, 1824826964u, 2514128237u, 3308726345u, 3623735281u, 887459898u, 3896777957u), - SC(3527405352u, 290146745u, 125808293u, 735109902u, 1788801307u, 3306408847u, 822599754u, 3798637803u, 1514985656u, 2967186195u, 716984495u, 3386310843u, 3156794500u, 1007814159u, 1629566196u, 4265651874u), - SC(1178327293u, 565847309u, 518944000u, 3901419432u, 941693255u, 4276272755u, 3595637504u, 1831384538u, 553054976u, 3799273120u, 516961220u, 3048859574u, 1887176404u, 3648800625u, 2905989893u, 2971331974u), - SC(561598562u, 3812086269u, 2571795641u, 1946669885u, 4094345694u, 1247304730u, 725275648u, 2382611624u, 3912910386u, 3657806663u, 2347179560u, 3311073478u, 3031523768u, 2672297551u, 829774364u, 4138790294u), - SC(3908534093u, 41076189u, 4026661177u, 1264946070u, 3582612650u, 3167460834u, 3305185564u, 1828271691u, 1883569901u, 567401887u, 2154847219u, 3599749472u, 834678216u, 1517326104u, 465030801u, 2253777505u) -}, -{ - SC(69398569u, 525452511u, 2938319650u, 1880483009u, 3967907249u, 2829806383u, 1621746321u, 1916983616u, 1370370736u, 248894365u, 3788903479u, 221658457u, 404383926u, 1308961733u, 2635279776u, 2619294254u), - SC(4116760418u, 3197079795u, 2972456007u, 1278881079u, 1399016013u, 267334468u, 3129907813u, 468505870u, 1237093446u, 3810554944u, 1980244001u, 1830827024u, 4255330344u, 3556724451u, 2936427778u, 3969278111u), - SC(3989128687u, 604159041u, 3302470711u, 1703086807u, 4153485525u, 2444501021u, 449535888u, 2817157702u, 3967126593u, 3774839729u, 4230523164u, 1130105305u, 2419296875u, 560268503u, 173246097u, 1794638932u), - SC(1735434103u, 3810847770u, 4216841726u, 1126260487u, 1019034952u, 4140633019u, 3223272164u, 440162565u, 3864068825u, 3275406276u, 2196958479u, 4212485308u, 539037402u, 431338309u, 4061221107u, 4289896057u), - SC(1802752446u, 2780168117u, 1133399256u, 2599868866u, 3158418134u, 2848371717u, 2893014484u, 1878597835u, 139427334u, 1841937895u, 2016179766u, 2330806831u, 3849381146u, 2224326221u, 2296824272u, 3983748073u), - SC(1520559143u, 1690628296u, 1614953069u, 1422707415u, 257987514u, 3063997315u, 2652769123u, 3445956897u, 843436720u, 4264023440u, 365609354u, 2250088148u, 2769492081u, 59746990u, 1275187671u, 1973406172u), - SC(2823162534u, 2631304853u, 2485683334u, 33106529u, 243176015u, 492943806u, 489814307u, 4023911334u, 4139752347u, 4133120235u, 2455727203u, 1293330101u, 1838339727u, 4219498628u, 2131345625u, 3646653738u), - SC(4198202713u, 3167956639u, 2765023077u, 3652537372u, 1708707687u, 2324231909u, 1009881825u, 1679047879u, 2515346176u, 794145218u, 554048969u, 3173445869u, 2193645289u, 1271864237u, 1006139617u, 1072905092u), - SC(4273823033u, 1749314885u, 4263358248u, 538495360u, 4104454924u, 1997598205u, 3080563305u, 3238994582u, 3099819109u, 3162260128u, 1706963773u, 405274298u, 1894479347u, 1596497438u, 1094591269u, 1522128209u), - SC(2640931764u, 1304425992u, 2939922746u, 3918107623u, 1248692482u, 1121191585u, 2062140937u, 1807331998u, 3643560968u, 3236720945u, 2667270358u, 411521120u, 3664086365u, 2334989504u, 2668098536u, 3236026237u), - SC(2404161740u, 567514400u, 3895963765u, 1201374790u, 674719322u, 2894222365u, 467511362u, 3395036514u, 1038550674u, 2948454520u, 1518702565u, 1362236790u, 157238862u, 3475771959u, 1415257606u, 2714484334u), - SC(1831986705u, 588754101u, 4075551797u, 2767613701u, 2944855428u, 1912813036u, 1398542170u, 3440695634u, 2367865816u, 842155635u, 2602621363u, 2143763320u, 4256143529u, 1826541687u, 1851134007u, 2997377819u), - SC(3699972731u, 227995919u, 3067674252u, 477404832u, 847958753u, 893077929u, 2153170373u, 3057114881u, 1197132301u, 3330088847u, 2465660906u, 549749504u, 722435391u, 4124201578u, 3419977887u, 636305133u), - SC(3346980455u, 338882355u, 1940861469u, 2106574528u, 4065634984u, 939438415u, 880899904u, 173329243u, 3962520186u, 3417951565u, 2532850810u, 1158609417u, 1846710650u, 305050726u, 600225342u, 3684765712u), - SC(1932816778u, 3409537322u, 2445361402u, 1740774412u, 3661005378u, 2854030637u, 1914937560u, 1558250179u, 3808763123u, 1298026979u, 2417248681u, 899022004u, 847010236u, 506303181u, 1296472514u, 648957572u), - SC(600303058u, 722185115u, 3110060002u, 3818809602u, 1551617161u, 4208042174u, 526230670u, 1957951010u, 3160030963u, 3295123990u, 3121214191u, 1337066151u, 2200271451u, 1066776105u, 1163805043u, 2606444927u) -}, -{ - SC(1137648243u, 3815904636u, 35128896u, 1498158156u, 2482392993u, 1978830034u, 1585381051u, 335710867u, 529205549u, 1286325760u, 863511412u, 283835652u, 936788847u, 101075250u, 116973165u, 2483395918u), - SC(2210369250u, 711585268u, 1961210974u, 1353321439u, 1215935705u, 1641330999u, 11213011u, 2020212318u, 695107713u, 3413272123u, 1378074688u, 2790029989u, 658491086u, 1881545465u, 3409839898u, 2042086316u), - SC(1723393102u, 3373492622u, 3599711002u, 3748987970u, 1143620470u, 2663282777u, 2229588531u, 2674289435u, 2963045423u, 2234232397u, 4178299567u, 2791622546u, 4001934471u, 757990509u, 2858420658u, 605204372u), - SC(4272330873u, 3840847353u, 659917277u, 1664684318u, 1563018625u, 821178295u, 3329580379u, 794312951u, 2169136998u, 1706378889u, 3017987093u, 1159314572u, 2524368718u, 2444830959u, 898030098u, 68613446u), - SC(3172236096u, 1547478676u, 3467968131u, 1603626860u, 1411948645u, 2916654969u, 2891471305u, 2110051838u, 1733578576u, 2788816800u, 1613389791u, 759324595u, 3991538909u, 4073480091u, 3323038139u, 2043658072u), - SC(3011536148u, 2207224783u, 101813390u, 4149858178u, 961260436u, 3760245299u, 2099300570u, 3143747485u, 3209436103u, 902146054u, 3598885374u, 597299239u, 1369786353u, 2099087354u, 1506359374u, 1017249349u), - SC(3137350455u, 1622014086u, 2828880803u, 599881832u, 2213606365u, 4248974065u, 675350384u, 1446749674u, 1254778294u, 1745968946u, 409433048u, 1103126998u, 2370471436u, 1143685003u, 3341252280u, 1003299547u), - SC(2019014241u, 1108099665u, 1035538349u, 2878848993u, 2585673617u, 1565675366u, 2261830657u, 117854892u, 1965053814u, 2351841804u, 4065720752u, 3747135308u, 959541091u, 1629950401u, 4236240320u, 189693687u), - SC(3443026785u, 3216851941u, 278623472u, 1568038608u, 1548544711u, 2243949731u, 3359141033u, 1425753427u, 2934907774u, 2301245979u, 2216178210u, 153063705u, 1690071616u, 791861830u, 1201756636u, 1249732113u), - SC(2497506925u, 3815453805u, 1308318422u, 1061717857u, 710358190u, 3797004413u, 1870767051u, 2099598345u, 845543228u, 2941187056u, 1083282999u, 1311194087u, 3227025541u, 423673289u, 2634724972u, 3297305091u), - SC(1394185841u, 1653557808u, 2313575976u, 1732811292u, 2133445032u, 171245194u, 3242484287u, 2667183179u, 1165233778u, 997752293u, 501180123u, 2529762237u, 429212016u, 1660866777u, 1766992150u, 2066419882u), - SC(945381459u, 1085161105u, 3490034658u, 983140246u, 425352282u, 2175943302u, 1166850024u, 3968884285u, 1417959566u, 3386676357u, 3168826489u, 2984241621u, 3305143707u, 246924146u, 4113453679u, 123892017u), - SC(1498291154u, 979168666u, 2565114847u, 3722708999u, 3116533535u, 2044826765u, 118913881u, 2684275795u, 30932180u, 3147559151u, 3769605849u, 2376328043u, 753602217u, 3789763983u, 1247346722u, 4123341034u), - SC(3203969599u, 2514533821u, 1007395325u, 2063305304u, 520326691u, 3823758018u, 3095693832u, 1864628246u, 2586004821u, 4190638257u, 2952735262u, 2977139992u, 1124651421u, 295756268u, 3428261546u, 3110485030u), - SC(1663042556u, 4114384947u, 1430450710u, 3825340149u, 1051862436u, 3194752601u, 3106848742u, 1383208530u, 3142397378u, 4065704146u, 1545077688u, 2297695627u, 3152458457u, 4134880529u, 2187655177u, 3419805764u), - SC(3081663242u, 3880428040u, 2670880433u, 1398290076u, 1232125961u, 3862005121u, 1297357575u, 3334998678u, 1135063881u, 1723120988u, 2716095891u, 1113861429u, 3955845594u, 88397004u, 1699846421u, 887623013u) -}, -{ - SC(2668669863u, 1518051232u, 591131964u, 3625564717u, 2443152079u, 2589878039u, 747840157u, 1417298109u, 2236109461u, 625624150u, 2276484522u, 3671203634u, 3004642785u, 2519941048u, 286358016u, 3502187361u), - SC(1979235571u, 2198968296u, 3104128030u, 1368659294u, 3672213117u, 1391937809u, 2759329883u, 1389958836u, 2420411428u, 890766213u, 2707043165u, 2738550562u, 3382941095u, 378763942u, 3093409509u, 2964936317u), - SC(738589056u, 2116353374u, 2279888429u, 1705963022u, 828292114u, 896734726u, 2179570630u, 199574728u, 977051187u, 779668316u, 2330529056u, 3992755888u, 1000402439u, 2191612089u, 357145081u, 1441305104u), - SC(3372185571u, 1990378702u, 1181109789u, 3007260699u, 2430812419u, 1342872134u, 2198044770u, 1122343273u, 492870646u, 795688582u, 3226537448u, 1245881435u, 1071312339u, 1997541910u, 3829149062u, 1964864598u), - SC(3005241683u, 2859584860u, 2297396821u, 999606499u, 3964655188u, 3075624064u, 1368424820u, 847579236u, 744318941u, 1201524211u, 1104903258u, 3771742070u, 4093550286u, 53333408u, 659192149u, 3026115299u), - SC(3415227510u, 2060701016u, 1724277801u, 2661091313u, 215175235u, 1719160017u, 2940192603u, 1942243742u, 2398510742u, 4053370504u, 720436957u, 3760614784u, 2014232625u, 4199009336u, 2658914393u, 246186938u), - SC(446126854u, 165933106u, 2141828870u, 892600041u, 4146883601u, 2127439849u, 3431174989u, 2697318886u, 754216027u, 2671089369u, 1463409379u, 2826265846u, 334206028u, 1562078629u, 62819702u, 350080249u), - SC(3607678201u, 1305808009u, 3724583207u, 482185919u, 703873206u, 1075587326u, 1772056430u, 1356871295u, 4212601732u, 3762698616u, 2707284202u, 752961239u, 3089561250u, 1634547883u, 2919906767u, 31529502u), - SC(299389109u, 1252069111u, 2304374236u, 1252642323u, 2415535563u, 271885157u, 592252779u, 1178960198u, 53568246u, 3149254195u, 2937703855u, 1474069228u, 1764301842u, 954790502u, 4245417136u, 3132108431u), - SC(2094400513u, 3190829985u, 2239253067u, 2918833540u, 4106202305u, 2502268912u, 1731261142u, 2453877410u, 1861934729u, 934615026u, 3785479199u, 3605446967u, 3582056355u, 3042887218u, 1961855879u, 496882544u), - SC(3179454680u, 881405516u, 158640787u, 2790186672u, 162147899u, 376983910u, 3379568747u, 1408037207u, 1411174731u, 535638557u, 1510230718u, 2856041085u, 1958999115u, 3678347246u, 2958940834u, 520309445u), - SC(1870118851u, 1980314816u, 3987573623u, 4117586697u, 396136405u, 3149345244u, 70002589u, 2314836548u, 1713919226u, 3789182954u, 2123295507u, 3015665476u, 4069315088u, 3980795614u, 2021907367u, 4155874670u), - SC(4078777812u, 3708497519u, 1529048728u, 3747007128u, 2780224299u, 2728976580u, 3953400499u, 550363476u, 3812495996u, 3116459113u, 2211909765u, 3967732138u, 315888386u, 4202077281u, 1437542127u, 2815522910u), - SC(3236576167u, 3189780679u, 2030714184u, 2121402515u, 772212369u, 2193424420u, 1417920098u, 2031545011u, 4110769775u, 697022136u, 1206489717u, 1691036150u, 88940849u, 535864250u, 547921653u, 2569798466u), - SC(598120112u, 3876471191u, 3533286352u, 3003233155u, 1039593763u, 2148663879u, 2659932582u, 279051507u, 988977723u, 3458445518u, 2950275676u, 4048574808u, 3093122873u, 831143981u, 214208408u, 3935649503u), - SC(2893621405u, 3242329790u, 1948255717u, 4083664057u, 3803596193u, 740414223u, 4293576836u, 3875047642u, 667197150u, 2081112783u, 2447275650u, 242164299u, 706345359u, 1928593492u, 1774391838u, 3660333945u) -}, -{ - SC(3009793609u, 3525092161u, 3245586135u, 574145899u, 4034974232u, 2828949446u, 3457574134u, 1546193476u, 3883480541u, 1976722737u, 3557056370u, 994794948u, 106991499u, 1626704265u, 3534503938u, 3271872260u), - SC(2939511082u, 3508735083u, 975571643u, 1775005849u, 4144127005u, 706007446u, 420750190u, 1296964164u, 3061654480u, 2268588398u, 258119220u, 1152421762u, 2183948554u, 3016917902u, 1186604447u, 3147111215u), - SC(405897674u, 923178082u, 1575208079u, 3088321769u, 2214762612u, 3893926734u, 3167279390u, 3951912989u, 2709000001u, 2390687969u, 3858727239u, 866338457u, 2045181240u, 3217044625u, 2328560686u, 1861539550u), - SC(1277015638u, 1098202702u, 1559301990u, 2587773702u, 236499920u, 458659357u, 2353007333u, 2611100088u, 3428309717u, 2008274629u, 3647015407u, 268886847u, 2626192792u, 3341061984u, 1515395072u, 3708589435u), - SC(4042661445u, 3420460388u, 402520550u, 3677541300u, 2230979515u, 1273170666u, 2514471146u, 827498216u, 1259202696u, 3072082970u, 475301020u, 2118811945u, 3612811582u, 1387362670u, 2779447975u, 2265478999u), - SC(2229583001u, 1885758268u, 2744744533u, 2751282929u, 3032060674u, 1949605811u, 1570835257u, 793354274u, 1683039266u, 449593771u, 109462780u, 1941150268u, 1808732776u, 139050949u, 2225765509u, 1246293964u), - SC(2802845617u, 3765730171u, 462111640u, 590276976u, 2549490668u, 1227143343u, 384473299u, 1872236586u, 2432932105u, 2621627369u, 29218585u, 3541815309u, 3762320683u, 3470760231u, 2011203130u, 2527437401u), - SC(796052351u, 4037990088u, 4017471553u, 1320960316u, 561010825u, 3728618461u, 3540350568u, 1334322515u, 2252671868u, 3217596003u, 3122272084u, 3124892250u, 146022162u, 3584383023u, 2911266650u, 2958817688u), - SC(161418820u, 3776882969u, 4050624816u, 1522984750u, 3239766493u, 3767349571u, 782872272u, 4177710199u, 1140123311u, 211837022u, 1955996644u, 402816745u, 3326870942u, 1443720320u, 1645866695u, 3832886909u), - SC(452931871u, 3201459109u, 3989748495u, 3779670060u, 3234605835u, 2462489907u, 3541849378u, 3952908948u, 2234764749u, 2534999097u, 1221823414u, 2220662906u, 2593424893u, 3688122472u, 2131104831u, 243658822u), - SC(1244527825u, 1331697159u, 1126644730u, 922926684u, 1475975786u, 704282514u, 1718439968u, 1878820141u, 2509443841u, 2182928123u, 1663057853u, 2828328506u, 1475048880u, 791101245u, 3209045799u, 807262644u), - SC(1506123994u, 75559732u, 2487617790u, 2776679170u, 2522687136u, 3704896305u, 945074946u, 2943008309u, 1088584510u, 2469322363u, 1078526500u, 2073262975u, 691596720u, 2702927487u, 380178128u, 704842212u), - SC(1460389583u, 4274587105u, 1447626425u, 3957246995u, 1621878179u, 1643627976u, 4030517934u, 1056559397u, 1438644008u, 32976965u, 2197709285u, 3567855255u, 2001746745u, 2603748421u, 3462117821u, 903804357u), - SC(3179129705u, 2297226467u, 1646197352u, 950157362u, 2929140164u, 4242027992u, 1652798968u, 4193267428u, 3343133888u, 2499845914u, 423061238u, 3494957413u, 3637365392u, 784231823u, 595573026u, 2713123590u), - SC(2810225213u, 3951319549u, 1905650326u, 3909017486u, 2335763951u, 3772810842u, 2983632261u, 489145948u, 4173940274u, 2703192453u, 2654763363u, 4064871590u, 1399005653u, 257836626u, 831912020u, 895345820u), - SC(4037755568u, 3145789767u, 2141184942u, 4120133888u, 346636610u, 3895536529u, 2259736314u, 1057113066u, 595225270u, 3051392771u, 2813693848u, 3877775276u, 1832280309u, 1138362004u, 3061980317u, 858203300u) -}, -{ - SC(941124125u, 1620226392u, 1431256941u, 3336438938u, 540497787u, 766040889u, 373284400u, 2979905322u, 177008709u, 2625544842u, 1096614388u, 1196846420u, 4186360501u, 3945210662u, 1143943919u, 3412870088u), - SC(2868459499u, 3255324438u, 807131982u, 2853200483u, 3487859623u, 3501857558u, 3107820062u, 2163227213u, 2115527726u, 2346720657u, 2251713340u, 3377131273u, 3223650794u, 3766790266u, 177525458u, 4167009497u), - SC(311132793u, 3961991670u, 3475828441u, 4275227465u, 4114440759u, 287999228u, 3329759386u, 2384037498u, 4228771259u, 844254234u, 256179964u, 1796107218u, 3127243322u, 1425447302u, 1385509204u, 1101567113u), - SC(2084416542u, 1837746358u, 3915669193u, 60671540u, 2731498203u, 842785439u, 103116859u, 3404407266u, 2713222963u, 3049100113u, 368142082u, 2923502225u, 3018451818u, 2169399182u, 3017634865u, 1845463402u), - SC(1620925474u, 3368534446u, 555437218u, 4144603563u, 1969376145u, 213474605u, 1856420595u, 3939242692u, 1705488978u, 252956811u, 1258322279u, 1776729832u, 3988114536u, 3572272198u, 1383845751u, 1398527932u), - SC(1762997475u, 799707654u, 1609033889u, 2324053368u, 2951656833u, 2545022095u, 1325992886u, 2638191889u, 737853621u, 891297811u, 1613139572u, 594983169u, 2686965496u, 4040759974u, 1496585540u, 294269531u), - SC(3866323582u, 3807637640u, 654389167u, 993860478u, 3985490230u, 874636344u, 2342980699u, 1928023737u, 1520117329u, 644165140u, 150615609u, 199275733u, 463804864u, 310744654u, 2057873049u, 1169977839u), - SC(239011286u, 715635161u, 1855226016u, 2750348850u, 4059485278u, 800137564u, 3998891997u, 4048007508u, 1194893107u, 3761772527u, 273800027u, 653240081u, 1187997500u, 310579555u, 786511222u, 3092283411u), - SC(3036944959u, 3482022954u, 3739636749u, 3919006909u, 4266819119u, 1212326408u, 103856594u, 597427799u, 1319114089u, 4260737761u, 1982976744u, 741084092u, 689793522u, 4260038527u, 1319231386u, 1661185367u), - SC(3846585080u, 1572901113u, 2683774833u, 3251385733u, 3753876990u, 849242549u, 4245340911u, 1064393430u, 3309340124u, 2842098330u, 2556268102u, 2033409485u, 757257328u, 2031055308u, 487255243u, 3197919149u), - SC(273355511u, 2413549351u, 710350577u, 1361281890u, 2485522754u, 1210096318u, 3839671116u, 3619357718u, 3954210633u, 312725146u, 3792397974u, 3833954588u, 1779821907u, 2701218449u, 2422680647u, 3829673069u), - SC(379167192u, 3494512635u, 855436470u, 2928216366u, 4239059924u, 4254878455u, 3617218283u, 739826290u, 3488721213u, 1288540569u, 2623691196u, 4237777587u, 1234356449u, 2367467024u, 185343202u, 2198868227u), - SC(333398980u, 1306721698u, 1267933489u, 3888643170u, 2305763143u, 1886386521u, 2247721544u, 1287414137u, 497238456u, 1934421131u, 1960709128u, 2688614248u, 3637710577u, 3756130276u, 1929365309u, 2796038772u), - SC(772805737u, 461244658u, 3551164236u, 4177074918u, 3920537361u, 4259237061u, 3625379235u, 3715444221u, 3444473673u, 2576271136u, 2750230085u, 2167864295u, 2571239709u, 3663560660u, 743894391u, 703945624u), - SC(2955504442u, 4192737708u, 2813336533u, 2037901957u, 1563142269u, 620241136u, 3249364868u, 1805455553u, 422364625u, 3061329310u, 3824436397u, 1640020182u, 2540832302u, 2063844885u, 2982901072u, 2809011473u), - SC(4188085081u, 1849071252u, 4251112483u, 1368274267u, 2811635355u, 3535120523u, 478922770u, 1090405967u, 2358353504u, 2249592823u, 2367480425u, 1158857070u, 1979230110u, 3661225756u, 2903524693u, 1830110173u) -}, -{ - SC(3638948794u, 3243385178u, 2365114888u, 1084927340u, 2097158816u, 336310452u, 231393062u, 580838002u, 3851653288u, 568877195u, 3846156888u, 2754011062u, 3396743120u, 2639744892u, 1431686029u, 1903473537u), - SC(3268926613u, 1818698216u, 1862252109u, 1578913474u, 4289804840u, 1885759995u, 2888888373u, 2636129891u, 2360477693u, 1672434489u, 4188472821u, 2046052045u, 437371108u, 3454488779u, 2151384078u, 1514762405u), - SC(3140765176u, 3623124217u, 3204258419u, 1994235030u, 4141313973u, 3067394014u, 3891883464u, 3387486245u, 3254639322u, 1970078634u, 2106725210u, 2833086525u, 1670513208u, 472865524u, 2121280699u, 2548725819u), - SC(309446023u, 3610145983u, 678094472u, 3223511337u, 4188624231u, 2675209562u, 619208065u, 1214683627u, 307823706u, 3407147709u, 2103429213u, 3636822787u, 2441204583u, 1675916090u, 1444359140u, 2979809856u), - SC(1982287011u, 2286805587u, 3436767742u, 3002584758u, 477850697u, 439716674u, 3863581947u, 2155905635u, 220608999u, 1402913678u, 2974580099u, 1207717136u, 3265452095u, 2174870701u, 464004734u, 3218951674u), - SC(2374025586u, 3926883961u, 3555874460u, 1238670328u, 856489843u, 4258163476u, 977941661u, 3889087192u, 2262660846u, 1677408901u, 2922467369u, 1043137100u, 4279650771u, 3357788771u, 1512036754u, 2539641395u), - SC(1142842756u, 272648505u, 914080820u, 4056304706u, 1529598235u, 1542384711u, 898735874u, 77881967u, 1035144846u, 702992091u, 2075420139u, 2454875215u, 1266516833u, 2974932401u, 3666315911u, 2262316403u), - SC(282628724u, 2966722803u, 3533567779u, 2474391608u, 1236598744u, 3094620093u, 2714845907u, 369896328u, 366951725u, 2971547133u, 2753808137u, 618960857u, 2006195012u, 551749950u, 1402811398u, 3808228405u), - SC(962649761u, 2486282608u, 1808066694u, 2361174774u, 234593415u, 400975056u, 83848885u, 1091105486u, 1020816894u, 1838575736u, 2668167699u, 73800319u, 2028242253u, 2121917721u, 1921251529u, 2828854963u), - SC(2717497535u, 366873177u, 336873963u, 978494261u, 2877822089u, 2054875183u, 2521644031u, 4057807064u, 3713415744u, 3955164880u, 2229410320u, 3755022307u, 3363858805u, 1398106956u, 800395520u, 1799982442u), - SC(399227430u, 164572050u, 2101616757u, 962629850u, 1654784623u, 3459989194u, 2240801569u, 1986371042u, 1911756881u, 2723553175u, 2964071573u, 3609789600u, 3185432638u, 2208423303u, 2967147750u, 4279453877u), - SC(282950688u, 2418348758u, 1686423600u, 1392917024u, 3343336708u, 976718153u, 671781049u, 4166009090u, 371505957u, 2474457927u, 1126253569u, 3355537407u, 4151375790u, 2105071839u, 941370857u, 331122028u), - SC(2127306191u, 1587304141u, 1137651997u, 1529991785u, 1356564935u, 726775332u, 1952136309u, 4003891353u, 61741949u, 780292838u, 1136081573u, 1836882786u, 528077243u, 30578492u, 465809744u, 2709331701u), - SC(4118645416u, 3394012023u, 348789448u, 3808052591u, 1284813572u, 265335400u, 545565522u, 929596026u, 744207086u, 3837069751u, 130735480u, 1107476780u, 910486599u, 2623115273u, 1478462314u, 2130033795u), - SC(1955617954u, 1897311939u, 3110934223u, 4221780767u, 1556888759u, 3849614629u, 306928433u, 3178221670u, 2099698284u, 308858727u, 2221495536u, 1221057715u, 974275765u, 2399830054u, 3285960273u, 1758193777u), - SC(1309372774u, 3725783295u, 3135972452u, 3122681380u, 3898315320u, 1245625291u, 3684458552u, 2498694383u, 145248803u, 3480764710u, 874108791u, 2482726617u, 434324108u, 1522025692u, 3554266182u, 2125028368u) -}, -{ - SC(4095464112u, 3774124339u, 1954448156u, 2941024780u, 584234335u, 483707475u, 286644251u, 3027719344u, 2257880535u, 651454587u, 3313147574u, 3910046631u, 3169039651u, 2576160449u, 696031594u, 3062648739u), - SC(3459141530u, 1009969738u, 35229281u, 2373814441u, 355537356u, 4228991558u, 213496956u, 1669603654u, 1552983955u, 3304370832u, 604896268u, 499179421u, 2737968344u, 807678026u, 3567168353u, 2353882345u), - SC(2454671851u, 2184874449u, 831795291u, 1169825676u, 1084590471u, 1942690394u, 2762211706u, 3042637679u, 2365319338u, 3552008694u, 348752618u, 993280940u, 1178602031u, 1559708076u, 3354759347u, 972286478u), - SC(2677560697u, 4247966509u, 151962163u, 3310844434u, 2986095882u, 3914030856u, 3436387520u, 860446559u, 4289606749u, 2343453766u, 3218454181u, 293342071u, 1238022655u, 3938175190u, 1394478132u, 4256084776u), - SC(3033685698u, 1795086146u, 719843849u, 255984080u, 2447365525u, 874035973u, 313642533u, 1163634918u, 2316564524u, 1195940716u, 1914843207u, 3907025376u, 23457264u, 1278433300u, 3111232984u, 668125878u), - SC(2135745017u, 2899432034u, 1819124473u, 2109840859u, 3124696519u, 2070710502u, 990727745u, 2752134271u, 1963223245u, 866344359u, 606159585u, 3867224292u, 3038840373u, 3295910586u, 2433460716u, 3384811471u), - SC(1744070416u, 383286836u, 3000319326u, 3310329765u, 4062980155u, 2749127191u, 1895582230u, 439084228u, 1884304792u, 326674045u, 377650590u, 3363592478u, 2947641322u, 1784390018u, 1332541121u, 4203919218u), - SC(472957101u, 1135650637u, 4212757570u, 185931877u, 2096733734u, 4238795506u, 481917546u, 1405180051u, 925427330u, 1923351053u, 2204480714u, 3944038373u, 372144582u, 3395978522u, 3795034464u, 1074487901u), - SC(227727393u, 2219043153u, 2909459085u, 3082645761u, 1970114976u, 3426610084u, 35253812u, 3123666967u, 4231900027u, 2888054525u, 2744804820u, 1500359618u, 191232240u, 3239664209u, 1569663960u, 1330983134u), - SC(996304063u, 2759713926u, 1022152104u, 4268512678u, 2870837640u, 3507597858u, 1252922637u, 3276898019u, 3824649934u, 1524401760u, 2559990337u, 1660220688u, 2350855385u, 609332995u, 2406016501u, 2406242521u), - SC(3333888266u, 3838886221u, 3016467419u, 3341790649u, 3667104212u, 783789160u, 1310400762u, 3633793516u, 4105695306u, 2973076533u, 455893547u, 2864660063u, 3696934279u, 2872882056u, 2264350097u, 539812697u), - SC(3263458726u, 2820785414u, 3760367911u, 628854049u, 1473785327u, 426717862u, 2025377226u, 3498407835u, 3577945153u, 1319190911u, 1062047947u, 3346460201u, 2590672215u, 2723591074u, 1487439866u, 4217021014u), - SC(2076058913u, 33130418u, 1949000294u, 3536165044u, 31327487u, 1891010986u, 2347335564u, 1669503944u, 3753248202u, 881959988u, 3846164684u, 3636142472u, 208517894u, 3407391141u, 3485893709u, 1074365179u), - SC(2175348532u, 3463201667u, 168136052u, 2889266255u, 4105885613u, 3068947090u, 2279310533u, 2649966235u, 828612565u, 2017635648u, 1260407590u, 1970316631u, 2447304459u, 2893112079u, 2425504835u, 1197046834u), - SC(2653983058u, 1419924288u, 2320709126u, 3640188854u, 2683911962u, 2643927342u, 3261193464u, 3929873787u, 2878724355u, 3436083049u, 3424148509u, 1311037973u, 3116391362u, 2037892948u, 454042580u, 970415398u), - SC(16199673u, 2464180001u, 89776423u, 672570852u, 2291071982u, 3899998968u, 4262439281u, 412856039u, 3677249728u, 1182323568u, 3472045521u, 3554674668u, 819725249u, 4078699211u, 2037243914u, 4166444096u) -}, -{ - SC(1740919499u, 3877396933u, 2326751436u, 2985697421u, 1447445291u, 2255966095u, 1611141497u, 1834170313u, 3589822942u, 2703601378u, 299681739u, 3037417379u, 4014970727u, 2126073701u, 3064037855u, 2610138122u), - SC(2959647136u, 3814991611u, 764778261u, 1677371416u, 497556143u, 1000564042u, 4065791500u, 1027030318u, 2636763418u, 2469599275u, 839050056u, 4115114412u, 3982189672u, 2204140838u, 1747652790u, 3786215179u), - SC(3812425833u, 3703652912u, 1980699604u, 1506061914u, 2330998846u, 3874717363u, 20614012u, 1484655664u, 2896690261u, 1196646483u, 159078055u, 1300317512u, 2570981831u, 1267318554u, 3037645632u, 3117135345u), - SC(2012483448u, 279997059u, 1908492604u, 1638405820u, 284407565u, 1607271004u, 1423855670u, 3949669604u, 1635878907u, 4045715556u, 3600475894u, 3387647818u, 3950223476u, 3109131487u, 2524676171u, 3329048150u), - SC(3505120665u, 1999377488u, 158974979u, 636438923u, 1767149410u, 2424026197u, 532320013u, 3350230775u, 3506414357u, 999737675u, 3415715721u, 797201045u, 3439137094u, 3636888232u, 1001867404u, 1070514934u), - SC(803341976u, 972240723u, 2174569332u, 4037031657u, 720363583u, 1532359940u, 222173943u, 3948724459u, 669414977u, 446802288u, 4195328223u, 2316597014u, 3039478974u, 1217500351u, 1058613984u, 3974805650u), - SC(2497689022u, 832535973u, 4012390289u, 3862385792u, 473134599u, 855172718u, 3160709443u, 2946049581u, 1340978834u, 1282260619u, 3672935594u, 1114896253u, 1194768191u, 2151967837u, 3557909289u, 83919397u), - SC(2685697085u, 4183307820u, 393931333u, 2425217781u, 2950365274u, 2300063381u, 3990090983u, 1961757942u, 3357278228u, 2993935030u, 779960569u, 3652282828u, 1743505267u, 3193034940u, 2134245237u, 4042181132u), - SC(2449311128u, 4037657778u, 318968012u, 1098807866u, 3241626396u, 745989749u, 4126255071u, 850508142u, 4075976689u, 357235455u, 2000916706u, 3900438139u, 2804084317u, 3036848582u, 604252796u, 2006800965u), - SC(101955641u, 2732365617u, 2730133770u, 3908553062u, 2872853047u, 264325893u, 2086018926u, 546076667u, 582367640u, 2242336949u, 2223649162u, 1521240572u, 178342991u, 3408523296u, 2216853754u, 1636770650u), - SC(1697876449u, 998213608u, 2367869150u, 3635535434u, 3029347602u, 2697162358u, 300760335u, 3790588806u, 3127970813u, 157171921u, 2766714052u, 3441353031u, 3760111386u, 1962222723u, 1338315915u, 1705537099u), - SC(2069540711u, 3174156395u, 3834082852u, 2243125169u, 1332693007u, 1773075089u, 820191370u, 262117783u, 184405617u, 469065021u, 1286610377u, 946922506u, 2233109630u, 2803987975u, 489850357u, 3341265389u), - SC(3152895344u, 3190413328u, 1371373852u, 2133030998u, 2097773989u, 3484604561u, 3233580762u, 2103971308u, 580626917u, 3723142348u, 1233964596u, 2884246809u, 1451113068u, 2274332609u, 834566918u, 4166322862u), - SC(474309298u, 31198476u, 474732582u, 1614612386u, 2339718649u, 702598622u, 2007092771u, 1563921691u, 3096928870u, 2036801390u, 3171632090u, 2666464957u, 2581592302u, 84487705u, 4066440296u, 250703600u), - SC(2850943751u, 3355276358u, 3608928556u, 645558581u, 1754003398u, 2401097307u, 4007141515u, 2306720640u, 2585847442u, 2486681168u, 916961025u, 2906286711u, 2183350629u, 3403456959u, 1234360906u, 608407455u), - SC(3919397u, 2910764499u, 1130649170u, 2504839137u, 475960727u, 4198145923u, 3575554927u, 727034596u, 3487299979u, 2134210036u, 1295494166u, 1094003986u, 3153584442u, 1125501956u, 1050325095u, 3018071122u) -}, -{ - SC(1456510740u, 215912204u, 253318863u, 2775298218u, 3073705928u, 3154352632u, 3237812190u, 434409115u, 3593346865u, 3020727994u, 1910411353u, 2325723409u, 1818165255u, 3742118891u, 4111316616u, 4010457359u), - SC(2413332453u, 1353953544u, 4051432026u, 303594340u, 1259813651u, 366336945u, 3380747343u, 2634392445u, 2066562619u, 120707135u, 1398541407u, 502464084u, 2984999938u, 3829298149u, 1120989122u, 3373752257u), - SC(1681071159u, 120984332u, 2029459879u, 1382039080u, 3634662556u, 54408822u, 48099449u, 1179080842u, 2669759950u, 3169946602u, 1520730683u, 3878549631u, 1666070500u, 1804495215u, 1101808889u, 1988315741u), - SC(1810699040u, 1982264875u, 1311915666u, 268159494u, 1265118580u, 1494821999u, 2740360551u, 3403457379u, 2370002476u, 3663200326u, 1969174367u, 2988878975u, 2261867571u, 1896957751u, 4228495601u, 268030737u), - SC(3788031612u, 1459331879u, 4195039120u, 148760443u, 2710036304u, 3803193725u, 2316636996u, 1290739855u, 2078515077u, 1158390637u, 187516666u, 1165781180u, 3871854912u, 2887741280u, 3432370474u, 3017515415u), - SC(2660400581u, 1115514969u, 819611304u, 2438542525u, 1149450061u, 641570348u, 4195260176u, 114239580u, 3415942550u, 2418164759u, 3596450733u, 4170880111u, 3742333800u, 707266970u, 294392938u, 1502400257u), - SC(4244209414u, 4144723933u, 1206802017u, 3395049043u, 1534528858u, 212213384u, 273948964u, 2465871688u, 98513287u, 526054552u, 101003852u, 2178852720u, 1739213138u, 2000068838u, 3443316390u, 2907641948u), - SC(4170329393u, 2397160575u, 698736458u, 1726629095u, 2059726015u, 608224441u, 940962377u, 3160021800u, 2474105021u, 1418624931u, 3220142189u, 3165061177u, 609263259u, 3526248509u, 2451110984u, 882122082u), - SC(1803413035u, 2626850042u, 3923382679u, 2501640460u, 887077755u, 2970691407u, 3982443858u, 546345352u, 545064661u, 1905866916u, 4137411501u, 4293519422u, 399697152u, 2101209662u, 4081268472u, 3745325674u), - SC(3913855272u, 3324082002u, 2401043817u, 1769760109u, 2460560183u, 875956117u, 1942607787u, 1641754800u, 1964565342u, 442388011u, 1687580604u, 293988342u, 3046598358u, 2835075967u, 920490836u, 349604594u), - SC(2643665013u, 1607952309u, 2279132309u, 992705865u, 1231530495u, 2682680275u, 2340070945u, 1036310446u, 2160469638u, 3849593659u, 569936175u, 133751759u, 1309000826u, 3681058360u, 1289881501u, 385711414u), - SC(1190130845u, 2798968177u, 277741425u, 3875973536u, 2502592372u, 251555512u, 1825737360u, 462006518u, 2334535950u, 3997809264u, 2012251623u, 3408888487u, 2549759312u, 3379458376u, 2301581275u, 4171117892u), - SC(1923456093u, 1653002750u, 3279649712u, 4281661052u, 1248011568u, 933375742u, 2109342469u, 751470571u, 2742486580u, 2572871261u, 3296809419u, 4075155428u, 3182626853u, 3435860599u, 3916597057u, 245531435u), - SC(514908612u, 2222061780u, 506774061u, 381342968u, 789366883u, 3683832850u, 9270407u, 528428861u, 590313143u, 483933274u, 1128871308u, 2791400346u, 3033966006u, 2397900561u, 174539653u, 2363998101u), - SC(3558289816u, 1015432688u, 3960686128u, 2087286003u, 446928557u, 4028273076u, 3055038539u, 885707705u, 942001648u, 3175434773u, 3929872598u, 2961036794u, 1122092143u, 2142675404u, 4054255588u, 1958229328u), - SC(2852327378u, 1383667573u, 3763466478u, 3195889922u, 2107642962u, 1739908882u, 157313327u, 492435243u, 4236498733u, 1510923342u, 3227437908u, 1896980749u, 154410481u, 2958311799u, 3270353062u, 1889012642u) -}, -{ - SC(822693957u, 1703644293u, 3960229340u, 2092754577u, 3495958557u, 4288710741u, 4092815138u, 1275224613u, 2592916775u, 472063207u, 2931222331u, 2597044591u, 1261640449u, 1272207288u, 2040245568u, 1417421068u), - SC(57865933u, 2591783175u, 1332940705u, 2361514832u, 2842982424u, 2581566511u, 1328343723u, 3898369656u, 2090549923u, 2179715082u, 2370481583u, 775215786u, 3850307123u, 2489521783u, 3999750482u, 1014134079u), - SC(2011629934u, 1914036612u, 3406392133u, 1425412057u, 1338374071u, 683386303u, 3190457777u, 428137206u, 1251032257u, 3672462899u, 2593185313u, 1953316437u, 2123216916u, 3258622817u, 3197533388u, 3442579011u), - SC(265734183u, 884987600u, 2786263189u, 3536027957u, 3885575220u, 1854265340u, 3853595664u, 1987453181u, 2744740518u, 512197390u, 114481815u, 96285071u, 3293497789u, 4015333892u, 4092376929u, 3025411574u), - SC(612519829u, 3198151239u, 3191059512u, 226844204u, 3503855660u, 764021515u, 3628841562u, 3951882416u, 3622158804u, 3603368155u, 2780109382u, 822859403u, 25907739u, 3882220368u, 3789068172u, 1684074913u), - SC(3520260226u, 1656105499u, 1676578448u, 838040958u, 3130046810u, 995588852u, 3233766730u, 2629592527u, 3096399775u, 1659682138u, 1365617549u, 2450677843u, 1725372848u, 2623357383u, 1402837393u, 1993344168u), - SC(2434333993u, 2901722469u, 518468307u, 3322336116u, 3303354477u, 2422295273u, 3584734361u, 1255342255u, 2224600785u, 3752112711u, 3720624102u, 3425652159u, 3563799906u, 957522630u, 501907560u, 3362627156u), - SC(3271809032u, 2402529419u, 3935184016u, 3639910664u, 659985988u, 2584831332u, 1091987512u, 224789177u, 2944016703u, 3591574599u, 1273021052u, 967556634u, 1019501719u, 1864898605u, 3453844870u, 4011599553u), - SC(1326048883u, 3477092042u, 1799777609u, 296885426u, 1109310872u, 255028335u, 163456938u, 2108662143u, 3501831646u, 225777648u, 4099069764u, 3428610561u, 4069711767u, 3876386370u, 1215899260u, 369937558u), - SC(3466874302u, 1921411468u, 3753149186u, 3739960133u, 1909238781u, 2219053499u, 4040572016u, 1651280893u, 754573870u, 383500798u, 2400558032u, 922698902u, 2125517085u, 2541623325u, 2827334144u, 2773618829u), - SC(2040368526u, 2190975469u, 1347589661u, 1684817146u, 2021572959u, 1656810013u, 330975936u, 994237514u, 2596719101u, 3800849855u, 600269956u, 1857741551u, 3033366103u, 1496147464u, 2628189942u, 4210116847u), - SC(3076719908u, 2490548320u, 377911263u, 2002478742u, 2549252529u, 839159951u, 230337140u, 3095221595u, 1528132928u, 2083899038u, 2503451113u, 272698731u, 2624407067u, 161482016u, 4135914440u, 2519252428u), - SC(2556876861u, 2107629748u, 2377697213u, 1433609947u, 3343742332u, 3505415093u, 2690575000u, 2017949066u, 4133794057u, 4184820210u, 2960078982u, 1333558937u, 3733636790u, 3960011078u, 945143131u, 3343864106u), - SC(1801254589u, 1449097227u, 181948563u, 1034221031u, 1779862110u, 3141289560u, 3383585093u, 2578193674u, 554670851u, 2530857925u, 4076682145u, 2827602863u, 4244507626u, 2938597885u, 3223414171u, 2204001183u), - SC(291814305u, 2937237569u, 1434020428u, 3585179044u, 3677832974u, 2016114805u, 3981784693u, 538800869u, 2673738915u, 999373833u, 1457987857u, 3180983013u, 501300267u, 4103517997u, 997980659u, 1113009463u), - SC(3993610129u, 1037741502u, 330412440u, 2749687355u, 1555232145u, 1196959672u, 530284980u, 340384986u, 2298150586u, 3185141181u, 26985524u, 2219307959u, 2447245692u, 1065988754u, 1248620406u, 2208024308u) -}, -{ - SC(3660855132u, 3816892380u, 3431508003u, 1440179111u, 768988979u, 3652895254u, 2084463131u, 3991218655u, 323118457u, 3675476946u, 2157306354u, 2684850253u, 1543808805u, 744627428u, 1091926767u, 3538062578u), - SC(2810298495u, 3411171710u, 4062828084u, 3003344135u, 3264709694u, 1048068132u, 3549102117u, 1927032841u, 3841604555u, 1360558064u, 2204714588u, 1197341693u, 3768005385u, 2899352192u, 2849083812u, 3793398404u), - SC(3631867959u, 3146872034u, 420513606u, 2446059169u, 2652499910u, 429155541u, 748397809u, 3543114527u, 235482177u, 894763888u, 1086818023u, 3285579564u, 1810274445u, 1142434275u, 140188668u, 4059040723u), - SC(2682453748u, 1595694625u, 17869409u, 4001607469u, 759206176u, 3336900820u, 3693692341u, 2473365492u, 2714988574u, 637563477u, 4105755464u, 3161387095u, 2814461644u, 4283494186u, 3858290792u, 1516784203u), - SC(4062605051u, 1956634460u, 3701616314u, 2342355265u, 1267526896u, 464674235u, 2247549950u, 3633206724u, 296547100u, 2905295542u, 4077085273u, 2746567644u, 1803616500u, 918536622u, 2709233803u, 2413530101u), - SC(1383097263u, 1316928613u, 759541292u, 3793001510u, 257497874u, 3658838865u, 3213596633u, 3650670599u, 63812226u, 1947202098u, 3651967368u, 2399936732u, 2521262969u, 322630211u, 4004516883u, 1422335688u), - SC(2852550033u, 3224936812u, 733055828u, 3325391168u, 1930707186u, 731324754u, 3498518219u, 4117056191u, 2179511600u, 2761523161u, 4282458808u, 3042559735u, 2438675720u, 2532100345u, 3706723018u, 4059342362u), - SC(2048163474u, 1848349034u, 3258863528u, 3644103333u, 1151231486u, 3308192205u, 2814277731u, 4197063636u, 3510455851u, 1315219655u, 2185965649u, 3799505477u, 4254363720u, 3128925961u, 1852465545u, 4138612075u), - SC(960983998u, 3301464188u, 2737893955u, 1522861436u, 4164105020u, 1184099683u, 64022400u, 2368856028u, 326418376u, 2065332946u, 2081529277u, 3466798514u, 208026276u, 417986090u, 3587033208u, 2294843214u), - SC(2712989146u, 349068332u, 3978782854u, 1513755929u, 4281030368u, 4041238337u, 1631550267u, 936378809u, 3831648862u, 1780262732u, 3189639539u, 328937247u, 722753719u, 3671027558u, 215485348u, 294998383u), - SC(170533035u, 3100330628u, 2519007245u, 2729143680u, 1780483799u, 1771308699u, 777046078u, 1252661309u, 944830935u, 3219243484u, 2959537667u, 145170296u, 892161275u, 1151850054u, 2176346749u, 598783080u), - SC(3596882604u, 51304713u, 1277701547u, 3288737023u, 2143659411u, 1229626338u, 2504854740u, 2518260221u, 2909459409u, 3820898741u, 1076396276u, 3330086214u, 2070741501u, 1675949151u, 4169029889u, 2072266145u), - SC(3395707749u, 1912264784u, 839246291u, 1812660322u, 2590197689u, 3115125394u, 280633483u, 1476186344u, 2182942190u, 4022517575u, 1314348304u, 2211853573u, 1730367526u, 3842875309u, 1411362967u, 749836026u), - SC(822183119u, 2084092802u, 2957672615u, 1548122281u, 2555590320u, 4127903458u, 704941703u, 3216796016u, 1310798669u, 1681974379u, 2704001393u, 836064664u, 2498528840u, 2878347924u, 3344415063u, 1714110968u), - SC(3763417450u, 1647484613u, 2916400914u, 1340277384u, 3671023234u, 2962715012u, 2086976330u, 2356641838u, 861453503u, 2497852292u, 3384683911u, 2044029625u, 3423593678u, 602612346u, 1947876325u, 1071593133u), - SC(502143537u, 3800930061u, 289630048u, 2019675509u, 690814111u, 1395759030u, 2095320716u, 1658529388u, 2140950369u, 4113871752u, 2130755443u, 1184235968u, 2624156111u, 1053548247u, 1666584094u, 3436241707u) -}, -{ - SC(2819478132u, 2629026829u, 2945562911u, 1854605392u, 41922071u, 2531530491u, 2316774439u, 3550381961u, 1180787169u, 3914439365u, 3786421842u, 3441223373u, 494782102u, 2858003292u, 1448968751u, 2940369046u), - SC(1228705098u, 2320747717u, 1742025124u, 3358828738u, 1857762103u, 2669617968u, 2684123743u, 2427291148u, 3948024171u, 3841263454u, 3817968782u, 3617000488u, 3457510946u, 3443415072u, 3976288418u, 291039859u), - SC(1118114309u, 1364783097u, 3986370035u, 1058514953u, 3723130907u, 2966082807u, 1592373613u, 4029958112u, 1261460522u, 159904028u, 385928252u, 2962822321u, 213058425u, 39305506u, 3400567258u, 2953928339u), - SC(4004285350u, 3275325131u, 2912133301u, 482119944u, 699333459u, 1353300830u, 498723416u, 2738735797u, 3773472794u, 1167510524u, 1995708610u, 1872986795u, 1771998886u, 460328822u, 2566240531u, 3665251184u), - SC(870908870u, 249845288u, 3674648542u, 3670939624u, 3213883826u, 2765218754u, 3292181727u, 1765634472u, 2846619223u, 156162860u, 2158300764u, 3792761756u, 4248292998u, 1588571137u, 1696144875u, 2915693433u), - SC(1257645965u, 743351844u, 3299328612u, 1606739395u, 2242479072u, 526126122u, 3132670209u, 2327012389u, 1257540758u, 1688790030u, 864103666u, 1782879705u, 2344074317u, 878043196u, 569218289u, 3875319913u), - SC(676712446u, 2310487862u, 3297058723u, 154140360u, 1534807165u, 2207878247u, 4002312458u, 1195155314u, 3973562995u, 203866583u, 1307033594u, 1808951889u, 3485439766u, 2123920858u, 3400721970u, 628518531u), - SC(453432196u, 3506137302u, 962794710u, 2800823697u, 944975983u, 445662356u, 620440622u, 225699982u, 1038708892u, 3484553780u, 4174808994u, 3862318255u, 1961625058u, 2183421173u, 2682639230u, 3890472885u), - SC(3472048934u, 1436162338u, 4281682055u, 1419885595u, 1926695253u, 861477946u, 2586543901u, 2286266784u, 2854911092u, 1779735787u, 2994125983u, 2248840912u, 677288518u, 3593153557u, 3383199489u, 2094768467u), - SC(971218259u, 3653638590u, 3374334294u, 479058129u, 1331477004u, 2497262229u, 892109896u, 3651901580u, 1455849852u, 2738531309u, 14202660u, 1968080740u, 1927308794u, 897128363u, 3654300057u, 1275380700u), - SC(684658124u, 660984744u, 2929312783u, 1473333980u, 1562502960u, 656352357u, 338449257u, 2159155320u, 2425193686u, 930413364u, 2001285554u, 307432757u, 2238003500u, 1858295105u, 481986971u, 1067622012u), - SC(943383548u, 127299943u, 2909652237u, 1257655712u, 4123282405u, 78394323u, 1736026340u, 2126927829u, 296638455u, 1861436609u, 641299684u, 636649068u, 3331138991u, 1014270261u, 257248847u, 1556179874u), - SC(2668740334u, 4261010365u, 3376970497u, 2258271000u, 3369826513u, 906131732u, 12531263u, 2501581679u, 861444520u, 2059219969u, 3536488433u, 3392343056u, 3231250347u, 3425501702u, 4204845226u, 3883035310u), - SC(875006280u, 3061145215u, 799684212u, 4150716124u, 1344915012u, 1442298502u, 887378800u, 2722425542u, 4141895498u, 4068116328u, 601774281u, 3538746538u, 1671758462u, 3066546971u, 1116345758u, 554718074u), - SC(1149406575u, 702696847u, 505403366u, 331269161u, 664926760u, 2151357672u, 2890104906u, 3156886545u, 1199701084u, 1614409973u, 4222014462u, 1336462493u, 3214687968u, 1279434993u, 2285235388u, 2975474024u), - SC(2419658919u, 481424988u, 2207220911u, 2736159805u, 4086711147u, 477511738u, 1428567116u, 3971000648u, 429362137u, 3495313342u, 3653961670u, 4170077754u, 2057308114u, 1445981917u, 97057494u, 3847612010u) -}, -{ - SC(3017729014u, 3423125690u, 1534829496u, 1346803271u, 888659105u, 1661894766u, 4165031912u, 697485157u, 3575889724u, 1795181757u, 1507549874u, 1480154979u, 3565672142u, 830054113u, 1507719534u, 3652903656u), - SC(4123340423u, 2168639254u, 3491407759u, 395600125u, 2056091205u, 1233197217u, 2716612715u, 3263564356u, 2257286689u, 2753339767u, 2228663460u, 3584404544u, 3972978154u, 3637886739u, 3854541466u, 1603898424u), - SC(641806023u, 3776877383u, 3574980110u, 2564901152u, 1378226343u, 738790225u, 4030459977u, 2255719927u, 295765315u, 60094770u, 422069111u, 439158593u, 3956842123u, 1242303994u, 150522972u, 3682386439u), - SC(2385589330u, 2076597417u, 605447848u, 3200763641u, 3106877254u, 3374069827u, 3828392492u, 1315607291u, 3211667999u, 305089333u, 179172787u, 3225149656u, 1080822644u, 3286534940u, 2231515542u, 2699760148u), - SC(3983719183u, 1208009460u, 767048521u, 326825213u, 1087716958u, 3599826498u, 3107818740u, 2785268698u, 1304576537u, 1847155836u, 3250405674u, 2694326935u, 2163030471u, 3253944705u, 1698753082u, 3845065767u), - SC(2823293375u, 2790862099u, 1207038844u, 3886043838u, 3567640686u, 3799791258u, 1638354726u, 1428653770u, 2075289233u, 1582582790u, 213364421u, 2858522524u, 2809903954u, 1742449197u, 324107072u, 1051562955u), - SC(2291926834u, 1805734123u, 3420689573u, 1003089617u, 476535216u, 1334543097u, 2045923069u, 2990972415u, 1822043289u, 2128934150u, 3541372378u, 1912558832u, 2295908612u, 1500502429u, 3539272060u, 2641558214u), - SC(3069594753u, 3051481608u, 2339450545u, 2054924228u, 4282917353u, 65440790u, 2134400604u, 3588265957u, 2569563771u, 741034486u, 740973978u, 93172292u, 1583303041u, 2980574219u, 2969067524u, 1088571815u), - SC(78721532u, 1566330912u, 1219109269u, 3229207312u, 2345730495u, 3209647323u, 2033975193u, 1009666575u, 2794060854u, 4218956981u, 3379703631u, 2400336569u, 100401885u, 3519721431u, 4007729122u, 3851183625u), - SC(2344993313u, 2454241381u, 3071516966u, 4207668067u, 250885582u, 1733938903u, 1658948056u, 2192440210u, 1717829063u, 849763004u, 2334162093u, 3715296533u, 1757279167u, 3270001477u, 2677428083u, 4197601814u), - SC(2911676146u, 4069956071u, 3299890629u, 3133371278u, 3551760603u, 558967408u, 205243474u, 237180706u, 4227661901u, 390685951u, 658498389u, 225847327u, 3028263358u, 3941067795u, 1850521034u, 1584413524u), - SC(304549398u, 3089811378u, 549382137u, 2353383127u, 2278640956u, 781853185u, 1734676013u, 3311472816u, 957105351u, 1291924767u, 2025324585u, 3897237789u, 80455313u, 302089802u, 3496158310u, 4000611245u), - SC(1221283087u, 3865703766u, 1551786763u, 3208862988u, 2964616465u, 1429406173u, 2847895093u, 3047143885u, 3187847794u, 3875229246u, 2044093786u, 2855772466u, 2252977997u, 1253496627u, 1824313803u, 3492626272u), - SC(1435191953u, 2954553263u, 3689501374u, 3761866706u, 3160683386u, 2172174457u, 4033800334u, 2293562561u, 500568896u, 2877151546u, 112648553u, 2760351679u, 1976713840u, 2960166087u, 1364536484u, 4127293522u), - SC(2942286091u, 3570696800u, 2680748212u, 879905933u, 371824626u, 2796545677u, 2544287558u, 1654320774u, 3724452395u, 1875952433u, 1755420330u, 700510406u, 2122483560u, 357724466u, 2579725929u, 4152935597u), - SC(732269412u, 3045632405u, 947036931u, 2403831527u, 2919479301u, 2947112020u, 1653738112u, 2316444303u, 3103978479u, 2856978461u, 308282125u, 1154683958u, 2086296447u, 1288456128u, 528614237u, 2945631134u) -}, -{ - SC(3751554592u, 1759634227u, 4138518211u, 3130599659u, 3881948336u, 669688286u, 3672211577u, 695226401u, 1226786139u, 1855160209u, 905875552u, 2831529665u, 1625185017u, 3130043300u, 3227522138u, 3659203373u), - SC(399372699u, 529779700u, 1206056828u, 1867177702u, 196488961u, 2148657353u, 2522788662u, 2308787051u, 1566407979u, 857878238u, 2852634973u, 2131204123u, 2812808340u, 3651465982u, 1947448513u, 3757182587u), - SC(3732610632u, 1025396308u, 60450219u, 3075208965u, 2460440177u, 301478800u, 2020185415u, 2910424285u, 1627945543u, 473410099u, 4114096970u, 2440686062u, 3031404169u, 2099206907u, 1232790956u, 2248800462u), - SC(2343232878u, 1198836246u, 1270188071u, 2305538045u, 1841160260u, 1049160535u, 2935147928u, 3818293360u, 2128394208u, 692132409u, 3183837651u, 981952986u, 3501941431u, 1239605342u, 1265208179u, 225920797u), - SC(1958540456u, 418545838u, 1645667403u, 4203505141u, 81660142u, 351421726u, 2877676470u, 871152679u, 2804776066u, 431108218u, 927442607u, 3782508732u, 318483929u, 4079394971u, 1143889788u, 4195920424u), - SC(2351179626u, 1598459225u, 3579449038u, 4292231882u, 2911534527u, 3174868713u, 2883217980u, 1046921244u, 3074833211u, 117299980u, 3425406982u, 2813303717u, 879305153u, 3439142119u, 1270010014u, 2633468950u), - SC(3394012837u, 1133386629u, 2931266329u, 2512080059u, 3268046571u, 585832644u, 1151303760u, 4164956195u, 1787214290u, 3523549326u, 4139598868u, 530139359u, 2107355242u, 1401770006u, 4264627539u, 3014221080u), - SC(1988836761u, 3474599222u, 2535855552u, 3118306895u, 1953046625u, 30632894u, 8987922u, 1482010220u, 1585584845u, 441041520u, 3045700482u, 362734762u, 3723600227u, 1056985402u, 2472480517u, 3558297033u), - SC(4137318322u, 915055827u, 1432589840u, 3550795442u, 1919127293u, 1256417138u, 946345068u, 1353195020u, 2948635882u, 3916808200u, 3223857138u, 2259986522u, 636089773u, 2116476405u, 266813303u, 3992924481u), - SC(1294364269u, 2282087282u, 719947200u, 1065389577u, 67185303u, 600695627u, 3423704882u, 507439949u, 1464333499u, 954935833u, 1949391476u, 2146234814u, 640934838u, 2477152026u, 3767255766u, 2397668523u), - SC(1825548026u, 2780595753u, 282065873u, 3347141416u, 3152283414u, 1656153711u, 1047376382u, 3616949007u, 464657631u, 3299783240u, 1162505878u, 3862534742u, 3899846651u, 3980167606u, 2513773976u, 1803555687u), - SC(734708953u, 181663675u, 2018505992u, 1055015000u, 2266993350u, 3679506170u, 1032089726u, 2239152753u, 3271229362u, 257492591u, 519168390u, 890304984u, 594386284u, 933877218u, 2646719799u, 439652468u), - SC(1253204385u, 2215899770u, 848155650u, 1305331452u, 1831981169u, 4101626048u, 253253616u, 718148001u, 3846087699u, 2362703617u, 564971301u, 878503956u, 2792594154u, 3831500219u, 630060686u, 2654848235u), - SC(2082956373u, 965635733u, 1172460454u, 3057130868u, 485386699u, 558270142u, 2819896785u, 247008390u, 1884023798u, 3291747866u, 1725636793u, 1552257124u, 171155452u, 894504521u, 3157754944u, 4135144713u), - SC(3013624247u, 3479051648u, 3976465681u, 139584997u, 690715168u, 2972053528u, 2543659091u, 81834710u, 261064551u, 1476481099u, 2550215537u, 1381589752u, 3557508349u, 3578290922u, 1272133161u, 3008228265u), - SC(3507369103u, 1077600519u, 1522596015u, 3088783267u, 2852999673u, 751358577u, 733140212u, 3467225217u, 100497019u, 50410977u, 68742811u, 3090618848u, 1603912616u, 2272476179u, 1767751118u, 3249696448u) -}, -{ - SC(2950670644u, 1870384244u, 3964091927u, 4110714448u, 298132763u, 3177974896u, 3260855649u, 1258311080u, 2976836646u, 3581267654u, 3094482836u, 80535005u, 2024129606u, 168620678u, 4254285674u, 2577025593u), - SC(1515179601u, 3578614970u, 3088354879u, 797813018u, 1355130048u, 1083957563u, 119796717u, 2021253602u, 1525138732u, 4127381203u, 3062851977u, 4142386071u, 1213064952u, 3609844670u, 1484215992u, 3431673114u), - SC(1401099367u, 3953214819u, 830584870u, 2207781603u, 918659453u, 4293181358u, 4072336467u, 4282551694u, 262435288u, 1941569548u, 147995405u, 1811389750u, 4118444114u, 1252574507u, 578798636u, 1074483177u), - SC(2872591360u, 1058667772u, 16799222u, 688522560u, 3475129040u, 3433794124u, 1076991040u, 1425059515u, 2939587530u, 236447274u, 3960100164u, 1298525395u, 2761371754u, 4025787449u, 2464666072u, 3981743594u), - SC(3976786453u, 1358319886u, 3905641993u, 1405765539u, 2585003073u, 3447572652u, 741448872u, 3444688769u, 971292808u, 1486657617u, 3079335839u, 862424956u, 248802634u, 1703726921u, 2982469234u, 2682500687u), - SC(4273605693u, 2467118193u, 3538801384u, 3862847335u, 1065478730u, 1602785515u, 1071410798u, 2624755760u, 2768741032u, 2700950902u, 558848464u, 3400938789u, 1410632048u, 2094050860u, 1686695852u, 2101955993u), - SC(4124709913u, 3191744141u, 3038636619u, 2944952304u, 2687117769u, 1502766822u, 14738299u, 223780235u, 32298390u, 1195949618u, 1154476371u, 1873391152u, 273358443u, 2362272244u, 509120994u, 606974408u), - SC(3937286725u, 1520668653u, 941545039u, 3056942351u, 574018151u, 2549472282u, 82289937u, 374652507u, 619831005u, 2134744303u, 1462663193u, 2963006112u, 3726585674u, 1797461239u, 1470634776u, 3441417480u), - SC(2845288945u, 3925574221u, 1989126288u, 3105801567u, 210047271u, 1545005898u, 2572648420u, 2278643173u, 2633053858u, 3288168184u, 3566345146u, 165026071u, 191806458u, 4116335861u, 1768316231u, 3169297484u), - SC(253765755u, 2509241970u, 1926513613u, 3735004917u, 4188741775u, 2806800711u, 281300019u, 3635185u, 3462483807u, 2277745510u, 1708651892u, 1413928970u, 56262931u, 531946794u, 2864634184u, 3118504241u), - SC(4194010611u, 4232988065u, 1802432341u, 3448133339u, 3732370320u, 253801846u, 2726367450u, 3905836819u, 1373544282u, 2066678017u, 3439519431u, 3381452691u, 2754663978u, 535580478u, 2512241599u, 2720083475u), - SC(3589933363u, 4047249230u, 2311777188u, 270484672u, 1108190662u, 2080251561u, 1724842405u, 4014518744u, 1593608472u, 2342434397u, 4205240553u, 2166622191u, 3528923u, 1996089122u, 4284726332u, 989608730u), - SC(2475269743u, 4230552139u, 3917936952u, 3098769598u, 3209444661u, 4188126675u, 3974782724u, 3639917274u, 2711234947u, 1439392508u, 1127433801u, 478802541u, 4223040451u, 2268034322u, 2452212595u, 3508939070u), - SC(2413851784u, 190519100u, 3576747926u, 2710481928u, 2148944938u, 3984096005u, 2427227598u, 1001464024u, 2191178977u, 1139441673u, 3841324161u, 308061908u, 3976150834u, 1467800561u, 3226772030u, 1743883019u), - SC(281260179u, 1415659644u, 915707047u, 1662956706u, 911938094u, 3456789397u, 2082200558u, 947098788u, 4036848108u, 2455542339u, 1466205449u, 4158358953u, 586549709u, 850657486u, 61343079u, 2292663847u), - SC(3487862268u, 4116082621u, 1969417576u, 1466595601u, 3136251120u, 3697533272u, 438943523u, 1041892750u, 1141661777u, 435333448u, 3031876514u, 2121342186u, 209290199u, 256519609u, 1400190683u, 4260080502u) -}, -{ - SC(1406628534u, 2978091511u, 343468499u, 973866526u, 757277528u, 1142388839u, 2945536141u, 3759469101u, 3001571847u, 2170606364u, 1017327004u, 3120716036u, 468321128u, 3656061918u, 2331571461u, 1930702552u), - SC(3117811324u, 4230396490u, 526101390u, 3589443580u, 12282838u, 3055128772u, 453582536u, 750425919u, 87216299u, 1999749165u, 2446098001u, 1907762611u, 183870981u, 3643605669u, 4232900175u, 2946539195u), - SC(3903405291u, 1034986659u, 2587588236u, 1880077572u, 1696686560u, 1243434386u, 3746745675u, 2212912696u, 2031851135u, 575946730u, 2663616094u, 2706019532u, 2635197066u, 1942621203u, 3760379195u, 4173271368u), - SC(2892050679u, 1105289247u, 1519565685u, 2426902952u, 65580444u, 3373395323u, 2112756687u, 3658806066u, 2548718870u, 3586646888u, 3350821933u, 1921239811u, 4061525916u, 3520594550u, 1872307168u, 3464547908u), - SC(2889143489u, 489507550u, 788811400u, 1800916293u, 3249681744u, 1400920516u, 3917828215u, 1093821500u, 1905385813u, 2931012984u, 1800788801u, 1697549042u, 3133274419u, 3606456099u, 2156683634u, 3205410986u), - SC(2814687995u, 4053305746u, 484530004u, 410862009u, 246830045u, 3164065541u, 3723774424u, 3388961612u, 3438413619u, 3662326637u, 2178649434u, 3555798301u, 164350275u, 2341607004u, 3896269562u, 1591806179u), - SC(3226183767u, 3881369008u, 700458770u, 376569395u, 2607908019u, 1353553198u, 2636334721u, 1140283021u, 2632309194u, 1710844790u, 3031461719u, 4081969123u, 3326745889u, 4034909949u, 3950856167u, 3153389256u), - SC(2184243175u, 2166726232u, 3921103433u, 872887260u, 623636347u, 95935618u, 2766774027u, 697875047u, 164043041u, 993154257u, 4114304816u, 3500729957u, 409872172u, 3504722710u, 2806324915u, 717798207u), - SC(1913401183u, 1684394893u, 957780895u, 2366199383u, 3846687839u, 2225031745u, 50628017u, 764720583u, 2251658783u, 1601491318u, 3836612294u, 3836982164u, 1834686310u, 4239983357u, 2677791106u, 718595268u), - SC(641418698u, 3008658673u, 1590313857u, 1025261614u, 1545641278u, 883067087u, 405447843u, 251932751u, 890679795u, 1380695500u, 4259157180u, 4219905082u, 665298826u, 4240175069u, 1720908833u, 2268480568u), - SC(1323007329u, 2757671761u, 531677728u, 1863777888u, 1512057206u, 2416428007u, 297355401u, 2843988168u, 3028483811u, 4269951770u, 844221740u, 1060678479u, 2913804270u, 3550002834u, 1490208797u, 2041637686u), - SC(4098631786u, 3088674341u, 2277647863u, 546429701u, 239595915u, 96051385u, 2043858235u, 356783975u, 3081379864u, 1495630942u, 1713035648u, 2797737429u, 4252005067u, 1174473008u, 182861961u, 1284115192u), - SC(1497340893u, 2990980382u, 435071738u, 25048206u, 1369038540u, 2388914024u, 3985375113u, 3187649864u, 1375850783u, 2762762203u, 3714513839u, 1546363407u, 2343675571u, 416152492u, 1797618344u, 3540898582u), - SC(2184924310u, 2347360549u, 640504537u, 1253044800u, 1440674061u, 1666425671u, 3827600864u, 2022304946u, 2918906490u, 263308814u, 3892002350u, 1942380643u, 1520343008u, 1245225248u, 3081248535u, 2098883649u), - SC(2377054091u, 3295547231u, 2240796492u, 1757295037u, 62158041u, 1809272299u, 4005194159u, 1592984938u, 366675588u, 3144502911u, 2973082795u, 4105706826u, 2851896979u, 3262002710u, 3082369242u, 634669574u), - SC(729159370u, 3948971047u, 1511320403u, 3061460707u, 3090283349u, 1868816562u, 3759558902u, 3868199437u, 2438888892u, 1660478281u, 2415784493u, 3546303863u, 3144683831u, 3066258755u, 2228021651u, 3294706852u) -} -}; -#undef SC -#endif diff --git a/libwallet/musig/extrakeys_main_impl.h b/libwallet/musig/extrakeys_main_impl.h deleted file mode 100644 index 2808350d..00000000 --- a/libwallet/musig/extrakeys_main_impl.h +++ /dev/null @@ -1,310 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2020 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_MODULE_EXTRAKEYS_MAIN_H -#define SECP256K1_MODULE_EXTRAKEYS_MAIN_H - -#include "secp256k1.h" -#include "secp256k1_extrakeys.h" -#include "hsort_impl.h" - -static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) { - return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey *) pubkey); -} - -static SECP256K1_INLINE void secp256k1_xonly_pubkey_save(secp256k1_xonly_pubkey *pubkey, secp256k1_ge *ge) { - secp256k1_pubkey_save((secp256k1_pubkey *) pubkey, ge); -} - -int secp256k1_xonly_pubkey_parse(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, const unsigned char *input32) { - secp256k1_ge pk; - secp256k1_fe x; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(pubkey != NULL); - memset(pubkey, 0, sizeof(*pubkey)); - ARG_CHECK(input32 != NULL); - - if (!secp256k1_fe_set_b32(&x, input32)) { - return 0; - } - if (!secp256k1_ge_set_xo_var(&pk, &x, 0)) { - return 0; - } - if (!secp256k1_ge_is_in_correct_subgroup(&pk)) { - return 0; - } - secp256k1_xonly_pubkey_save(pubkey, &pk); - return 1; -} - -int secp256k1_xonly_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output32, const secp256k1_xonly_pubkey *pubkey) { - secp256k1_ge pk; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(output32 != NULL); - memset(output32, 0, 32); - ARG_CHECK(pubkey != NULL); - - if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) { - return 0; - } - secp256k1_fe_get_b32(output32, &pk.x); - return 1; -} - -int secp256k1_xonly_pubkey_cmp(const secp256k1_context* ctx, const secp256k1_xonly_pubkey* pk0, const secp256k1_xonly_pubkey* pk1) { - unsigned char out[2][32]; - const secp256k1_xonly_pubkey* pk[2]; - int i; - - VERIFY_CHECK(ctx != NULL); - pk[0] = pk0; pk[1] = pk1; - for (i = 0; i < 2; i++) { - /* If the public key is NULL or invalid, xonly_pubkey_serialize will - * call the illegal_callback and return 0. In that case we will - * serialize the key as all zeros which is less than any valid public - * key. This results in consistent comparisons even if NULL or invalid - * pubkeys are involved and prevents edge cases such as sorting - * algorithms that use this function and do not terminate as a - * result. */ - if (!secp256k1_xonly_pubkey_serialize(ctx, out[i], pk[i])) { - /* Note that xonly_pubkey_serialize should already set the output to - * zero in that case, but it's not guaranteed by the API, we can't - * test it and writing a VERIFY_CHECK is more complex than - * explicitly memsetting (again). */ - memset(out[i], 0, sizeof(out[i])); - } - } - return secp256k1_memcmp_var(out[0], out[1], sizeof(out[1])); -} - -/** Keeps a group element as is if it has an even Y and otherwise negates it. - * y_parity is set to 0 in the former case and to 1 in the latter case. - * Requires that the coordinates of r are normalized. */ -static int secp256k1_extrakeys_ge_even_y(secp256k1_ge *r) { - int y_parity = 0; - VERIFY_CHECK(!secp256k1_ge_is_infinity(r)); - - if (secp256k1_fe_is_odd(&r->y)) { - secp256k1_fe_negate(&r->y, &r->y, 1); - y_parity = 1; - } - return y_parity; -} - -int secp256k1_xonly_pubkey_from_pubkey(const secp256k1_context* ctx, secp256k1_xonly_pubkey *xonly_pubkey, int *pk_parity, const secp256k1_pubkey *pubkey) { - secp256k1_ge pk; - int tmp; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(xonly_pubkey != NULL); - ARG_CHECK(pubkey != NULL); - - if (!secp256k1_pubkey_load(ctx, &pk, pubkey)) { - return 0; - } - tmp = secp256k1_extrakeys_ge_even_y(&pk); - if (pk_parity != NULL) { - *pk_parity = tmp; - } - secp256k1_xonly_pubkey_save(xonly_pubkey, &pk); - return 1; -} - -int secp256k1_xonly_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) { - secp256k1_ge pk; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(output_pubkey != NULL); - memset(output_pubkey, 0, sizeof(*output_pubkey)); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(internal_pubkey != NULL); - ARG_CHECK(tweak32 != NULL); - - if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey) - || !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) { - return 0; - } - secp256k1_pubkey_save(output_pubkey, &pk); - return 1; -} - -int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const unsigned char *tweaked_pubkey32, int tweaked_pk_parity, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) { - secp256k1_ge pk; - unsigned char pk_expected32[32]; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(internal_pubkey != NULL); - ARG_CHECK(tweaked_pubkey32 != NULL); - ARG_CHECK(tweak32 != NULL); - - if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey) - || !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) { - return 0; - } - secp256k1_fe_normalize_var(&pk.x); - secp256k1_fe_normalize_var(&pk.y); - secp256k1_fe_get_b32(pk_expected32, &pk.x); - - return secp256k1_memcmp_var(&pk_expected32, tweaked_pubkey32, 32) == 0 - && secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity; -} - -/* This struct wraps a const context pointer to satisfy the secp256k1_hsort api - * which expects a non-const cmp_data pointer. */ -typedef struct { - const secp256k1_context *ctx; -} secp256k1_xonly_sort_cmp_data; - -static int secp256k1_xonly_sort_cmp(const void* pk1, const void* pk2, void *cmp_data) { - return secp256k1_xonly_pubkey_cmp(((secp256k1_xonly_sort_cmp_data*)cmp_data)->ctx, - *(secp256k1_xonly_pubkey **)pk1, - *(secp256k1_xonly_pubkey **)pk2); -} - -int secp256k1_xonly_sort(const secp256k1_context* ctx, const secp256k1_xonly_pubkey **pubkeys, size_t n_pubkeys) { - secp256k1_xonly_sort_cmp_data cmp_data; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(pubkeys != NULL); - - cmp_data.ctx = ctx; - secp256k1_hsort(pubkeys, n_pubkeys, sizeof(*pubkeys), secp256k1_xonly_sort_cmp, &cmp_data); - return 1; -} - -static void secp256k1_keypair_save(secp256k1_keypair *keypair, const secp256k1_scalar *sk, secp256k1_ge *pk) { - secp256k1_scalar_get_b32(&keypair->data[0], sk); - secp256k1_pubkey_save((secp256k1_pubkey *)&keypair->data[32], pk); -} - - -static int secp256k1_keypair_seckey_load(const secp256k1_context* ctx, secp256k1_scalar *sk, const secp256k1_keypair *keypair) { - int ret; - - ret = secp256k1_scalar_set_b32_seckey(sk, &keypair->data[0]); - /* We can declassify ret here because sk is only zero if a keypair function - * failed (which zeroes the keypair) and its return value is ignored. */ - secp256k1_declassify(ctx, &ret, sizeof(ret)); - ARG_CHECK(ret); - return ret; -} - -/* Load a keypair into pk and sk (if non-NULL). This function declassifies pk - * and ARG_CHECKs that the keypair is not invalid. It always initializes sk and - * pk with dummy values. */ -static int secp256k1_keypair_load(const secp256k1_context* ctx, secp256k1_scalar *sk, secp256k1_ge *pk, const secp256k1_keypair *keypair) { - int ret; - const secp256k1_pubkey *pubkey = (const secp256k1_pubkey *)&keypair->data[32]; - - /* Need to declassify the pubkey because pubkey_load ARG_CHECKs if it's - * invalid. */ - secp256k1_declassify(ctx, pubkey, sizeof(*pubkey)); - ret = secp256k1_pubkey_load(ctx, pk, pubkey); - if (sk != NULL) { - ret = ret && secp256k1_keypair_seckey_load(ctx, sk, keypair); - } - if (!ret) { - *pk = secp256k1_ge_const_g; - if (sk != NULL) { - *sk = secp256k1_scalar_one; - } - } - return ret; -} - -int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *seckey32) { - secp256k1_scalar sk; - secp256k1_ge pk; - int ret = 0; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(keypair != NULL); - memset(keypair, 0, sizeof(*keypair)); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(seckey32 != NULL); - - ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32); - secp256k1_keypair_save(keypair, &sk, &pk); - secp256k1_memczero(keypair, sizeof(*keypair), !ret); - - secp256k1_scalar_clear(&sk); - return ret; -} - -int secp256k1_keypair_sec(const secp256k1_context* ctx, unsigned char *seckey, const secp256k1_keypair *keypair) { - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(seckey != NULL); - memset(seckey, 0, 32); - ARG_CHECK(keypair != NULL); - - memcpy(seckey, &keypair->data[0], 32); - return 1; -} - -int secp256k1_keypair_pub(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair) { - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(pubkey != NULL); - memset(pubkey, 0, sizeof(*pubkey)); - ARG_CHECK(keypair != NULL); - - memcpy(pubkey->data, &keypair->data[32], sizeof(*pubkey)); - return 1; -} - -int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, int *pk_parity, const secp256k1_keypair *keypair) { - secp256k1_ge pk; - int tmp; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(pubkey != NULL); - memset(pubkey, 0, sizeof(*pubkey)); - ARG_CHECK(keypair != NULL); - - if (!secp256k1_keypair_load(ctx, NULL, &pk, keypair)) { - return 0; - } - tmp = secp256k1_extrakeys_ge_even_y(&pk); - if (pk_parity != NULL) { - *pk_parity = tmp; - } - secp256k1_xonly_pubkey_save(pubkey, &pk); - - return 1; -} - -int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *tweak32) { - secp256k1_ge pk; - secp256k1_scalar sk; - int y_parity; - int ret; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(keypair != NULL); - ARG_CHECK(tweak32 != NULL); - - ret = secp256k1_keypair_load(ctx, &sk, &pk, keypair); - memset(keypair, 0, sizeof(*keypair)); - - y_parity = secp256k1_extrakeys_ge_even_y(&pk); - if (y_parity == 1) { - secp256k1_scalar_negate(&sk, &sk); - } - - ret &= secp256k1_ec_seckey_tweak_add_helper(&sk, tweak32); - ret &= secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32); - - secp256k1_declassify(ctx, &ret, sizeof(ret)); - if (ret) { - secp256k1_keypair_save(keypair, &sk, &pk); - } - - secp256k1_scalar_clear(&sk); - return ret; -} - -#endif diff --git a/libwallet/musig/field.h b/libwallet/musig/field.h deleted file mode 100644 index cf3bf10b..00000000 --- a/libwallet/musig/field.h +++ /dev/null @@ -1,128 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_FIELD_H -#define SECP256K1_FIELD_H - -/** Field element module. - * - * Field elements can be represented in several ways, but code accessing - * it (and implementations) need to take certain properties into account: - * - Each field element can be normalized or not. - * - Each field element has a magnitude, which represents how far away - * its representation is away from normalization. Normalized elements - * always have a magnitude of 1, but a magnitude of 1 doesn't imply - * normality. - */ - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - -#include "util.h" - -#if defined(SECP256K1_WIDEMUL_INT128) -#include "field_5x52.h" -#elif defined(SECP256K1_WIDEMUL_INT64) -#include "field_10x26.h" -#else -#error "Please select wide multiplication implementation" -#endif - -/** Normalize a field element. This brings the field element to a canonical representation, reduces - * its magnitude to 1, and reduces it modulo field size `p`. - */ -static void secp256k1_fe_normalize(secp256k1_fe *r); - -/** Weakly normalize a field element: reduce its magnitude to 1, but don't fully normalize. */ -static void secp256k1_fe_normalize_weak(secp256k1_fe *r); - -/** Normalize a field element, without constant-time guarantee. */ -static void secp256k1_fe_normalize_var(secp256k1_fe *r); - -/** Verify whether a field element represents zero i.e. would normalize to a zero value. */ -static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r); - -/** Verify whether a field element represents zero i.e. would normalize to a zero value, - * without constant-time guarantee. */ -static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r); - -/** Set a field element equal to a small integer. Resulting field element is normalized. */ -static void secp256k1_fe_set_int(secp256k1_fe *r, int a); - -/** Sets a field element equal to zero, initializing all fields. */ -static void secp256k1_fe_clear(secp256k1_fe *a); - -/** Verify whether a field element is zero. Requires the input to be normalized. */ -static int secp256k1_fe_is_zero(const secp256k1_fe *a); - -/** Check the "oddness" of a field element. Requires the input to be normalized. */ -static int secp256k1_fe_is_odd(const secp256k1_fe *a); - -/** Compare two field elements. Requires magnitude-1 inputs. */ -static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b); - -/** Same as secp256k1_fe_equal, but may be variable time. */ -static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b); - -/** Compare two field elements. Requires both inputs to be normalized */ -static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b); - -/** Set a field element equal to 32-byte big endian value. If successful, the resulting field element is normalized. */ -static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a); - -/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ -static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a); - -/** Set a field element equal to the additive inverse of another. Takes a maximum magnitude of the input - * as an argument. The magnitude of the output is one higher. */ -static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m); - -/** Multiplies the passed field element with a small integer constant. Multiplies the magnitude by that - * small integer. */ -static void secp256k1_fe_mul_int(secp256k1_fe *r, int a); - -/** Adds a field element to another. The result has the sum of the inputs' magnitudes as magnitude. */ -static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a); - -/** Sets a field element to be the product of two others. Requires the inputs' magnitudes to be at most 8. - * The output magnitude is 1 (but not guaranteed to be normalized). */ -static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b); - -/** Sets a field element to be the square of another. Requires the input's magnitude to be at most 8. - * The output magnitude is 1 (but not guaranteed to be normalized). */ -static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a); - -/** If a has a square root, it is computed in r and 1 is returned. If a does not - * have a square root, the root of its negation is computed and 0 is returned. - * The input's magnitude can be at most 8. The output magnitude is 1 (but not - * guaranteed to be normalized). The result in r will always be a square - * itself. */ -static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a); - -/** Checks whether a field element is a quadratic residue. */ -static int secp256k1_fe_is_quad_var(const secp256k1_fe *a); - -/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be - * at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */ -static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a); - -/** Potentially faster version of secp256k1_fe_inv, without constant-time guarantee. */ -static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a); - -/** Convert a field element to the storage type. */ -static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a); - -/** Convert a field element back from the storage type. */ -static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a); - -/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ -static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag); - -/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ -static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); - -#endif /* SECP256K1_FIELD_H */ diff --git a/libwallet/musig/field_10x26.h b/libwallet/musig/field_10x26.h deleted file mode 100644 index 9eb65607..00000000 --- a/libwallet/musig/field_10x26.h +++ /dev/null @@ -1,50 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_FIELD_REPR_H -#define SECP256K1_FIELD_REPR_H - -#include - -typedef struct { - /* X = sum(i=0..9, n[i]*2^(i*26)) mod p - * where p = 2^256 - 0x1000003D1 - */ - uint32_t n[10]; -#ifdef VERIFY - int magnitude; - int normalized; -#endif -} secp256k1_fe; - -/* Unpacks a constant into a overlapping multi-limbed FE element. */ -#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ - (d0) & 0x3FFFFFFUL, \ - (((uint32_t)d0) >> 26) | (((uint32_t)(d1) & 0xFFFFFUL) << 6), \ - (((uint32_t)d1) >> 20) | (((uint32_t)(d2) & 0x3FFFUL) << 12), \ - (((uint32_t)d2) >> 14) | (((uint32_t)(d3) & 0xFFUL) << 18), \ - (((uint32_t)d3) >> 8) | (((uint32_t)(d4) & 0x3UL) << 24), \ - (((uint32_t)d4) >> 2) & 0x3FFFFFFUL, \ - (((uint32_t)d4) >> 28) | (((uint32_t)(d5) & 0x3FFFFFUL) << 4), \ - (((uint32_t)d5) >> 22) | (((uint32_t)(d6) & 0xFFFFUL) << 10), \ - (((uint32_t)d6) >> 16) | (((uint32_t)(d7) & 0x3FFUL) << 16), \ - (((uint32_t)d7) >> 10) \ -} - -#ifdef VERIFY -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} -#else -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} -#endif - -typedef struct { - uint32_t n[8]; -} secp256k1_fe_storage; - -#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }} -#define SECP256K1_FE_STORAGE_CONST_GET(d) d.n[7], d.n[6], d.n[5], d.n[4],d.n[3], d.n[2], d.n[1], d.n[0] - -#endif /* SECP256K1_FIELD_REPR_H */ diff --git a/libwallet/musig/field_10x26_impl.h b/libwallet/musig/field_10x26_impl.h deleted file mode 100644 index 7a38c117..00000000 --- a/libwallet/musig/field_10x26_impl.h +++ /dev/null @@ -1,1256 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_FIELD_REPR_IMPL_H -#define SECP256K1_FIELD_REPR_IMPL_H - -#include "util.h" -#include "field.h" -#include "modinv32_impl.h" - -#ifdef VERIFY -static void secp256k1_fe_verify(const secp256k1_fe *a) { - const uint32_t *d = a->n; - int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; - r &= (d[0] <= 0x3FFFFFFUL * m); - r &= (d[1] <= 0x3FFFFFFUL * m); - r &= (d[2] <= 0x3FFFFFFUL * m); - r &= (d[3] <= 0x3FFFFFFUL * m); - r &= (d[4] <= 0x3FFFFFFUL * m); - r &= (d[5] <= 0x3FFFFFFUL * m); - r &= (d[6] <= 0x3FFFFFFUL * m); - r &= (d[7] <= 0x3FFFFFFUL * m); - r &= (d[8] <= 0x3FFFFFFUL * m); - r &= (d[9] <= 0x03FFFFFUL * m); - r &= (a->magnitude >= 0); - r &= (a->magnitude <= 32); - if (a->normalized) { - r &= (a->magnitude <= 1); - if (r && (d[9] == 0x03FFFFFUL)) { - uint32_t mid = d[8] & d[7] & d[6] & d[5] & d[4] & d[3] & d[2]; - if (mid == 0x3FFFFFFUL) { - r &= ((d[1] + 0x40UL + ((d[0] + 0x3D1UL) >> 26)) <= 0x3FFFFFFUL); - } - } - } - VERIFY_CHECK(r == 1); -} -#endif - -static void secp256k1_fe_normalize(secp256k1_fe *r) { - uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], - t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; - - /* Reduce t9 at the start so there will be at most a single carry from the first pass */ - uint32_t m; - uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; - - /* The first pass ensures the magnitude is 1, ... */ - t0 += x * 0x3D1UL; t1 += (x << 6); - t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; - t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; - t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2; - t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3; - t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4; - t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5; - t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6; - t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7; - t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8; - - /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ - VERIFY_CHECK(t9 >> 23 == 0); - - /* At most a single final reduction is needed; check if the value is >= the field characteristic */ - x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL) - & ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); - - /* Apply the final reduction (for constant-time behaviour, we do it always) */ - t0 += x * 0x3D1UL; t1 += (x << 6); - t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; - t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; - t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; - t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; - t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; - t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; - t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; - t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; - t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; - - /* If t9 didn't carry to bit 22 already, then it should have after any final reduction */ - VERIFY_CHECK(t9 >> 22 == x); - - /* Mask off the possible multiple of 2^256 from the final reduction */ - t9 &= 0x03FFFFFUL; - - r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif -} - -static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { - uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], - t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; - - /* Reduce t9 at the start so there will be at most a single carry from the first pass */ - uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; - - /* The first pass ensures the magnitude is 1, ... */ - t0 += x * 0x3D1UL; t1 += (x << 6); - t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; - t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; - t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; - t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; - t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; - t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; - t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; - t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; - t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; - - /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ - VERIFY_CHECK(t9 >> 23 == 0); - - r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; - -#ifdef VERIFY - r->magnitude = 1; - secp256k1_fe_verify(r); -#endif -} - -static void secp256k1_fe_normalize_var(secp256k1_fe *r) { - uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], - t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; - - /* Reduce t9 at the start so there will be at most a single carry from the first pass */ - uint32_t m; - uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; - - /* The first pass ensures the magnitude is 1, ... */ - t0 += x * 0x3D1UL; t1 += (x << 6); - t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; - t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; - t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2; - t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3; - t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4; - t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5; - t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6; - t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7; - t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8; - - /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ - VERIFY_CHECK(t9 >> 23 == 0); - - /* At most a single final reduction is needed; check if the value is >= the field characteristic */ - x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL) - & ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); - - if (x) { - t0 += 0x3D1UL; t1 += (x << 6); - t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; - t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; - t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; - t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; - t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; - t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; - t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; - t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; - t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; - - /* If t9 didn't carry to bit 22 already, then it should have after any final reduction */ - VERIFY_CHECK(t9 >> 22 == x); - - /* Mask off the possible multiple of 2^256 from the final reduction */ - t9 &= 0x03FFFFFUL; - } - - r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif -} - -static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) { - uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], - t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; - - /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ - uint32_t z0, z1; - - /* Reduce t9 at the start so there will be at most a single carry from the first pass */ - uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; - - /* The first pass ensures the magnitude is 1, ... */ - t0 += x * 0x3D1UL; t1 += (x << 6); - t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; z0 = t0; z1 = t0 ^ 0x3D0UL; - t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL; - t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2; - t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3; - t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4; - t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5; - t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6; - t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7; - t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8; - z0 |= t9; z1 &= t9 ^ 0x3C00000UL; - - /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ - VERIFY_CHECK(t9 >> 23 == 0); - - return (z0 == 0) | (z1 == 0x3FFFFFFUL); -} - -static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) { - uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; - uint32_t z0, z1; - uint32_t x; - - t0 = r->n[0]; - t9 = r->n[9]; - - /* Reduce t9 at the start so there will be at most a single carry from the first pass */ - x = t9 >> 22; - - /* The first pass ensures the magnitude is 1, ... */ - t0 += x * 0x3D1UL; - - /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ - z0 = t0 & 0x3FFFFFFUL; - z1 = z0 ^ 0x3D0UL; - - /* Fast return path should catch the majority of cases */ - if ((z0 != 0UL) & (z1 != 0x3FFFFFFUL)) { - return 0; - } - - t1 = r->n[1]; - t2 = r->n[2]; - t3 = r->n[3]; - t4 = r->n[4]; - t5 = r->n[5]; - t6 = r->n[6]; - t7 = r->n[7]; - t8 = r->n[8]; - - t9 &= 0x03FFFFFUL; - t1 += (x << 6); - - t1 += (t0 >> 26); - t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL; - t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2; - t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3; - t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4; - t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5; - t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6; - t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7; - t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8; - z0 |= t9; z1 &= t9 ^ 0x3C00000UL; - - /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ - VERIFY_CHECK(t9 >> 23 == 0); - - return (z0 == 0) | (z1 == 0x3FFFFFFUL); -} - -SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { - r->n[0] = a; - r->n[1] = r->n[2] = r->n[3] = r->n[4] = r->n[5] = r->n[6] = r->n[7] = r->n[8] = r->n[9] = 0; -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif -} - -SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { - const uint32_t *t = a->n; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif - return (t[0] | t[1] | t[2] | t[3] | t[4] | t[5] | t[6] | t[7] | t[8] | t[9]) == 0; -} - -SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif - return a->n[0] & 1; -} - -SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { - int i; -#ifdef VERIFY - a->magnitude = 0; - a->normalized = 1; -#endif - for (i=0; i<10; i++) { - a->n[i] = 0; - } -} - -static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { - int i; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - VERIFY_CHECK(b->normalized); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); -#endif - for (i = 9; i >= 0; i--) { - if (a->n[i] > b->n[i]) { - return 1; - } - if (a->n[i] < b->n[i]) { - return -1; - } - } - return 0; -} - -static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { - int ret; - r->n[0] = (uint32_t)a[31] | ((uint32_t)a[30] << 8) | ((uint32_t)a[29] << 16) | ((uint32_t)(a[28] & 0x3) << 24); - r->n[1] = (uint32_t)((a[28] >> 2) & 0x3f) | ((uint32_t)a[27] << 6) | ((uint32_t)a[26] << 14) | ((uint32_t)(a[25] & 0xf) << 22); - r->n[2] = (uint32_t)((a[25] >> 4) & 0xf) | ((uint32_t)a[24] << 4) | ((uint32_t)a[23] << 12) | ((uint32_t)(a[22] & 0x3f) << 20); - r->n[3] = (uint32_t)((a[22] >> 6) & 0x3) | ((uint32_t)a[21] << 2) | ((uint32_t)a[20] << 10) | ((uint32_t)a[19] << 18); - r->n[4] = (uint32_t)a[18] | ((uint32_t)a[17] << 8) | ((uint32_t)a[16] << 16) | ((uint32_t)(a[15] & 0x3) << 24); - r->n[5] = (uint32_t)((a[15] >> 2) & 0x3f) | ((uint32_t)a[14] << 6) | ((uint32_t)a[13] << 14) | ((uint32_t)(a[12] & 0xf) << 22); - r->n[6] = (uint32_t)((a[12] >> 4) & 0xf) | ((uint32_t)a[11] << 4) | ((uint32_t)a[10] << 12) | ((uint32_t)(a[9] & 0x3f) << 20); - r->n[7] = (uint32_t)((a[9] >> 6) & 0x3) | ((uint32_t)a[8] << 2) | ((uint32_t)a[7] << 10) | ((uint32_t)a[6] << 18); - r->n[8] = (uint32_t)a[5] | ((uint32_t)a[4] << 8) | ((uint32_t)a[3] << 16) | ((uint32_t)(a[2] & 0x3) << 24); - r->n[9] = (uint32_t)((a[2] >> 2) & 0x3f) | ((uint32_t)a[1] << 6) | ((uint32_t)a[0] << 14); - - ret = !((r->n[9] == 0x3FFFFFUL) & ((r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL) & ((r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); -#ifdef VERIFY - r->magnitude = 1; - if (ret) { - r->normalized = 1; - secp256k1_fe_verify(r); - } else { - r->normalized = 0; - } -#endif - return ret; -} - -/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ -static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif - r[0] = (a->n[9] >> 14) & 0xff; - r[1] = (a->n[9] >> 6) & 0xff; - r[2] = ((a->n[9] & 0x3F) << 2) | ((a->n[8] >> 24) & 0x3); - r[3] = (a->n[8] >> 16) & 0xff; - r[4] = (a->n[8] >> 8) & 0xff; - r[5] = a->n[8] & 0xff; - r[6] = (a->n[7] >> 18) & 0xff; - r[7] = (a->n[7] >> 10) & 0xff; - r[8] = (a->n[7] >> 2) & 0xff; - r[9] = ((a->n[7] & 0x3) << 6) | ((a->n[6] >> 20) & 0x3f); - r[10] = (a->n[6] >> 12) & 0xff; - r[11] = (a->n[6] >> 4) & 0xff; - r[12] = ((a->n[6] & 0xf) << 4) | ((a->n[5] >> 22) & 0xf); - r[13] = (a->n[5] >> 14) & 0xff; - r[14] = (a->n[5] >> 6) & 0xff; - r[15] = ((a->n[5] & 0x3f) << 2) | ((a->n[4] >> 24) & 0x3); - r[16] = (a->n[4] >> 16) & 0xff; - r[17] = (a->n[4] >> 8) & 0xff; - r[18] = a->n[4] & 0xff; - r[19] = (a->n[3] >> 18) & 0xff; - r[20] = (a->n[3] >> 10) & 0xff; - r[21] = (a->n[3] >> 2) & 0xff; - r[22] = ((a->n[3] & 0x3) << 6) | ((a->n[2] >> 20) & 0x3f); - r[23] = (a->n[2] >> 12) & 0xff; - r[24] = (a->n[2] >> 4) & 0xff; - r[25] = ((a->n[2] & 0xf) << 4) | ((a->n[1] >> 22) & 0xf); - r[26] = (a->n[1] >> 14) & 0xff; - r[27] = (a->n[1] >> 6) & 0xff; - r[28] = ((a->n[1] & 0x3f) << 2) | ((a->n[0] >> 24) & 0x3); - r[29] = (a->n[0] >> 16) & 0xff; - r[30] = (a->n[0] >> 8) & 0xff; - r[31] = a->n[0] & 0xff; -} - -SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= m); - secp256k1_fe_verify(a); -#endif - r->n[0] = 0x3FFFC2FUL * 2 * (m + 1) - a->n[0]; - r->n[1] = 0x3FFFFBFUL * 2 * (m + 1) - a->n[1]; - r->n[2] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[2]; - r->n[3] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[3]; - r->n[4] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[4]; - r->n[5] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[5]; - r->n[6] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[6]; - r->n[7] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[7]; - r->n[8] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[8]; - r->n[9] = 0x03FFFFFUL * 2 * (m + 1) - a->n[9]; -#ifdef VERIFY - r->magnitude = m + 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif -} - -SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { - r->n[0] *= a; - r->n[1] *= a; - r->n[2] *= a; - r->n[3] *= a; - r->n[4] *= a; - r->n[5] *= a; - r->n[6] *= a; - r->n[7] *= a; - r->n[8] *= a; - r->n[9] *= a; -#ifdef VERIFY - r->magnitude *= a; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif -} - -SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - secp256k1_fe_verify(a); -#endif - r->n[0] += a->n[0]; - r->n[1] += a->n[1]; - r->n[2] += a->n[2]; - r->n[3] += a->n[3]; - r->n[4] += a->n[4]; - r->n[5] += a->n[5]; - r->n[6] += a->n[6]; - r->n[7] += a->n[7]; - r->n[8] += a->n[8]; - r->n[9] += a->n[9]; -#ifdef VERIFY - r->magnitude += a->magnitude; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif -} - -#if defined(USE_EXTERNAL_ASM) - -/* External assembler implementation */ -void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b); -void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a); - -#else - -#ifdef VERIFY -#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) -#else -#define VERIFY_BITS(x, n) do { } while(0) -#endif - -SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b) { - uint64_t c, d; - uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8; - uint32_t t9, t1, t0, t2, t3, t4, t5, t6, t7; - const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL; - - VERIFY_BITS(a[0], 30); - VERIFY_BITS(a[1], 30); - VERIFY_BITS(a[2], 30); - VERIFY_BITS(a[3], 30); - VERIFY_BITS(a[4], 30); - VERIFY_BITS(a[5], 30); - VERIFY_BITS(a[6], 30); - VERIFY_BITS(a[7], 30); - VERIFY_BITS(a[8], 30); - VERIFY_BITS(a[9], 26); - VERIFY_BITS(b[0], 30); - VERIFY_BITS(b[1], 30); - VERIFY_BITS(b[2], 30); - VERIFY_BITS(b[3], 30); - VERIFY_BITS(b[4], 30); - VERIFY_BITS(b[5], 30); - VERIFY_BITS(b[6], 30); - VERIFY_BITS(b[7], 30); - VERIFY_BITS(b[8], 30); - VERIFY_BITS(b[9], 26); - - /** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n. - * for 0 <= x <= 9, px is a shorthand for sum(a[i]*b[x-i], i=0..x). - * for 9 <= x <= 18, px is a shorthand for sum(a[i]*b[x-i], i=(x-9)..9) - * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. - */ - - d = (uint64_t)a[0] * b[9] - + (uint64_t)a[1] * b[8] - + (uint64_t)a[2] * b[7] - + (uint64_t)a[3] * b[6] - + (uint64_t)a[4] * b[5] - + (uint64_t)a[5] * b[4] - + (uint64_t)a[6] * b[3] - + (uint64_t)a[7] * b[2] - + (uint64_t)a[8] * b[1] - + (uint64_t)a[9] * b[0]; - /* VERIFY_BITS(d, 64); */ - /* [d 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ - t9 = d & M; d >>= 26; - VERIFY_BITS(t9, 26); - VERIFY_BITS(d, 38); - /* [d t9 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ - - c = (uint64_t)a[0] * b[0]; - VERIFY_BITS(c, 60); - /* [d t9 0 0 0 0 0 0 0 0 c] = [p9 0 0 0 0 0 0 0 0 p0] */ - d += (uint64_t)a[1] * b[9] - + (uint64_t)a[2] * b[8] - + (uint64_t)a[3] * b[7] - + (uint64_t)a[4] * b[6] - + (uint64_t)a[5] * b[5] - + (uint64_t)a[6] * b[4] - + (uint64_t)a[7] * b[3] - + (uint64_t)a[8] * b[2] - + (uint64_t)a[9] * b[1]; - VERIFY_BITS(d, 63); - /* [d t9 0 0 0 0 0 0 0 0 c] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ - u0 = d & M; d >>= 26; c += u0 * R0; - VERIFY_BITS(u0, 26); - VERIFY_BITS(d, 37); - VERIFY_BITS(c, 61); - /* [d u0 t9 0 0 0 0 0 0 0 0 c-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ - t0 = c & M; c >>= 26; c += u0 * R1; - VERIFY_BITS(t0, 26); - VERIFY_BITS(c, 37); - /* [d u0 t9 0 0 0 0 0 0 0 c-u0*R1 t0-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ - /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ - - c += (uint64_t)a[0] * b[1] - + (uint64_t)a[1] * b[0]; - VERIFY_BITS(c, 62); - /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 p1 p0] */ - d += (uint64_t)a[2] * b[9] - + (uint64_t)a[3] * b[8] - + (uint64_t)a[4] * b[7] - + (uint64_t)a[5] * b[6] - + (uint64_t)a[6] * b[5] - + (uint64_t)a[7] * b[4] - + (uint64_t)a[8] * b[3] - + (uint64_t)a[9] * b[2]; - VERIFY_BITS(d, 63); - /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ - u1 = d & M; d >>= 26; c += u1 * R0; - VERIFY_BITS(u1, 26); - VERIFY_BITS(d, 37); - VERIFY_BITS(c, 63); - /* [d u1 0 t9 0 0 0 0 0 0 0 c-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ - t1 = c & M; c >>= 26; c += u1 * R1; - VERIFY_BITS(t1, 26); - VERIFY_BITS(c, 38); - /* [d u1 0 t9 0 0 0 0 0 0 c-u1*R1 t1-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ - /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ - - c += (uint64_t)a[0] * b[2] - + (uint64_t)a[1] * b[1] - + (uint64_t)a[2] * b[0]; - VERIFY_BITS(c, 62); - /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ - d += (uint64_t)a[3] * b[9] - + (uint64_t)a[4] * b[8] - + (uint64_t)a[5] * b[7] - + (uint64_t)a[6] * b[6] - + (uint64_t)a[7] * b[5] - + (uint64_t)a[8] * b[4] - + (uint64_t)a[9] * b[3]; - VERIFY_BITS(d, 63); - /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ - u2 = d & M; d >>= 26; c += u2 * R0; - VERIFY_BITS(u2, 26); - VERIFY_BITS(d, 37); - VERIFY_BITS(c, 63); - /* [d u2 0 0 t9 0 0 0 0 0 0 c-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ - t2 = c & M; c >>= 26; c += u2 * R1; - VERIFY_BITS(t2, 26); - VERIFY_BITS(c, 38); - /* [d u2 0 0 t9 0 0 0 0 0 c-u2*R1 t2-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ - /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ - - c += (uint64_t)a[0] * b[3] - + (uint64_t)a[1] * b[2] - + (uint64_t)a[2] * b[1] - + (uint64_t)a[3] * b[0]; - VERIFY_BITS(c, 63); - /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ - d += (uint64_t)a[4] * b[9] - + (uint64_t)a[5] * b[8] - + (uint64_t)a[6] * b[7] - + (uint64_t)a[7] * b[6] - + (uint64_t)a[8] * b[5] - + (uint64_t)a[9] * b[4]; - VERIFY_BITS(d, 63); - /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ - u3 = d & M; d >>= 26; c += u3 * R0; - VERIFY_BITS(u3, 26); - VERIFY_BITS(d, 37); - /* VERIFY_BITS(c, 64); */ - /* [d u3 0 0 0 t9 0 0 0 0 0 c-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ - t3 = c & M; c >>= 26; c += u3 * R1; - VERIFY_BITS(t3, 26); - VERIFY_BITS(c, 39); - /* [d u3 0 0 0 t9 0 0 0 0 c-u3*R1 t3-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ - /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ - - c += (uint64_t)a[0] * b[4] - + (uint64_t)a[1] * b[3] - + (uint64_t)a[2] * b[2] - + (uint64_t)a[3] * b[1] - + (uint64_t)a[4] * b[0]; - VERIFY_BITS(c, 63); - /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ - d += (uint64_t)a[5] * b[9] - + (uint64_t)a[6] * b[8] - + (uint64_t)a[7] * b[7] - + (uint64_t)a[8] * b[6] - + (uint64_t)a[9] * b[5]; - VERIFY_BITS(d, 62); - /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ - u4 = d & M; d >>= 26; c += u4 * R0; - VERIFY_BITS(u4, 26); - VERIFY_BITS(d, 36); - /* VERIFY_BITS(c, 64); */ - /* [d u4 0 0 0 0 t9 0 0 0 0 c-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ - t4 = c & M; c >>= 26; c += u4 * R1; - VERIFY_BITS(t4, 26); - VERIFY_BITS(c, 39); - /* [d u4 0 0 0 0 t9 0 0 0 c-u4*R1 t4-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ - - c += (uint64_t)a[0] * b[5] - + (uint64_t)a[1] * b[4] - + (uint64_t)a[2] * b[3] - + (uint64_t)a[3] * b[2] - + (uint64_t)a[4] * b[1] - + (uint64_t)a[5] * b[0]; - VERIFY_BITS(c, 63); - /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ - d += (uint64_t)a[6] * b[9] - + (uint64_t)a[7] * b[8] - + (uint64_t)a[8] * b[7] - + (uint64_t)a[9] * b[6]; - VERIFY_BITS(d, 62); - /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ - u5 = d & M; d >>= 26; c += u5 * R0; - VERIFY_BITS(u5, 26); - VERIFY_BITS(d, 36); - /* VERIFY_BITS(c, 64); */ - /* [d u5 0 0 0 0 0 t9 0 0 0 c-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ - t5 = c & M; c >>= 26; c += u5 * R1; - VERIFY_BITS(t5, 26); - VERIFY_BITS(c, 39); - /* [d u5 0 0 0 0 0 t9 0 0 c-u5*R1 t5-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ - - c += (uint64_t)a[0] * b[6] - + (uint64_t)a[1] * b[5] - + (uint64_t)a[2] * b[4] - + (uint64_t)a[3] * b[3] - + (uint64_t)a[4] * b[2] - + (uint64_t)a[5] * b[1] - + (uint64_t)a[6] * b[0]; - VERIFY_BITS(c, 63); - /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ - d += (uint64_t)a[7] * b[9] - + (uint64_t)a[8] * b[8] - + (uint64_t)a[9] * b[7]; - VERIFY_BITS(d, 61); - /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ - u6 = d & M; d >>= 26; c += u6 * R0; - VERIFY_BITS(u6, 26); - VERIFY_BITS(d, 35); - /* VERIFY_BITS(c, 64); */ - /* [d u6 0 0 0 0 0 0 t9 0 0 c-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ - t6 = c & M; c >>= 26; c += u6 * R1; - VERIFY_BITS(t6, 26); - VERIFY_BITS(c, 39); - /* [d u6 0 0 0 0 0 0 t9 0 c-u6*R1 t6-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ - - c += (uint64_t)a[0] * b[7] - + (uint64_t)a[1] * b[6] - + (uint64_t)a[2] * b[5] - + (uint64_t)a[3] * b[4] - + (uint64_t)a[4] * b[3] - + (uint64_t)a[5] * b[2] - + (uint64_t)a[6] * b[1] - + (uint64_t)a[7] * b[0]; - /* VERIFY_BITS(c, 64); */ - VERIFY_CHECK(c <= 0x8000007C00000007ULL); - /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ - d += (uint64_t)a[8] * b[9] - + (uint64_t)a[9] * b[8]; - VERIFY_BITS(d, 58); - /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ - u7 = d & M; d >>= 26; c += u7 * R0; - VERIFY_BITS(u7, 26); - VERIFY_BITS(d, 32); - /* VERIFY_BITS(c, 64); */ - VERIFY_CHECK(c <= 0x800001703FFFC2F7ULL); - /* [d u7 0 0 0 0 0 0 0 t9 0 c-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ - t7 = c & M; c >>= 26; c += u7 * R1; - VERIFY_BITS(t7, 26); - VERIFY_BITS(c, 38); - /* [d u7 0 0 0 0 0 0 0 t9 c-u7*R1 t7-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ - - c += (uint64_t)a[0] * b[8] - + (uint64_t)a[1] * b[7] - + (uint64_t)a[2] * b[6] - + (uint64_t)a[3] * b[5] - + (uint64_t)a[4] * b[4] - + (uint64_t)a[5] * b[3] - + (uint64_t)a[6] * b[2] - + (uint64_t)a[7] * b[1] - + (uint64_t)a[8] * b[0]; - /* VERIFY_BITS(c, 64); */ - VERIFY_CHECK(c <= 0x9000007B80000008ULL); - /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - d += (uint64_t)a[9] * b[9]; - VERIFY_BITS(d, 57); - /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - u8 = d & M; d >>= 26; c += u8 * R0; - VERIFY_BITS(u8, 26); - VERIFY_BITS(d, 31); - /* VERIFY_BITS(c, 64); */ - VERIFY_CHECK(c <= 0x9000016FBFFFC2F8ULL); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - - r[3] = t3; - VERIFY_BITS(r[3], 26); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[4] = t4; - VERIFY_BITS(r[4], 26); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[5] = t5; - VERIFY_BITS(r[5], 26); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[6] = t6; - VERIFY_BITS(r[6], 26); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[7] = t7; - VERIFY_BITS(r[7], 26); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - - r[8] = c & M; c >>= 26; c += u8 * R1; - VERIFY_BITS(r[8], 26); - VERIFY_BITS(c, 39); - /* [d u8 0 0 0 0 0 0 0 0 t9+c-u8*R1 r8-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 0 0 0 0 t9+c r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += d * R0 + t9; - VERIFY_BITS(c, 45); - /* [d 0 0 0 0 0 0 0 0 0 c-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[9] = c & (M >> 4); c >>= 22; c += d * (R1 << 4); - VERIFY_BITS(r[9], 22); - VERIFY_BITS(c, 46); - /* [d 0 0 0 0 0 0 0 0 r9+((c-d*R1<<4)<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - - d = c * (R0 >> 4) + t0; - VERIFY_BITS(d, 56); - /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[0] = d & M; d >>= 26; - VERIFY_BITS(r[0], 26); - VERIFY_BITS(d, 30); - /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - d += c * (R1 >> 4) + t1; - VERIFY_BITS(d, 53); - VERIFY_CHECK(d <= 0x10000003FFFFBFULL); - /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - /* [r9 r8 r7 r6 r5 r4 r3 t2 d r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[1] = d & M; d >>= 26; - VERIFY_BITS(r[1], 26); - VERIFY_BITS(d, 27); - VERIFY_CHECK(d <= 0x4000000ULL); - /* [r9 r8 r7 r6 r5 r4 r3 t2+d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - d += t2; - VERIFY_BITS(d, 27); - /* [r9 r8 r7 r6 r5 r4 r3 d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[2] = d; - VERIFY_BITS(r[2], 27); - /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ -} - -SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a) { - uint64_t c, d; - uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8; - uint32_t t9, t0, t1, t2, t3, t4, t5, t6, t7; - const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL; - - VERIFY_BITS(a[0], 30); - VERIFY_BITS(a[1], 30); - VERIFY_BITS(a[2], 30); - VERIFY_BITS(a[3], 30); - VERIFY_BITS(a[4], 30); - VERIFY_BITS(a[5], 30); - VERIFY_BITS(a[6], 30); - VERIFY_BITS(a[7], 30); - VERIFY_BITS(a[8], 30); - VERIFY_BITS(a[9], 26); - - /** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n. - * px is a shorthand for sum(a[i]*a[x-i], i=0..x). - * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. - */ - - d = (uint64_t)(a[0]*2) * a[9] - + (uint64_t)(a[1]*2) * a[8] - + (uint64_t)(a[2]*2) * a[7] - + (uint64_t)(a[3]*2) * a[6] - + (uint64_t)(a[4]*2) * a[5]; - /* VERIFY_BITS(d, 64); */ - /* [d 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ - t9 = d & M; d >>= 26; - VERIFY_BITS(t9, 26); - VERIFY_BITS(d, 38); - /* [d t9 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ - - c = (uint64_t)a[0] * a[0]; - VERIFY_BITS(c, 60); - /* [d t9 0 0 0 0 0 0 0 0 c] = [p9 0 0 0 0 0 0 0 0 p0] */ - d += (uint64_t)(a[1]*2) * a[9] - + (uint64_t)(a[2]*2) * a[8] - + (uint64_t)(a[3]*2) * a[7] - + (uint64_t)(a[4]*2) * a[6] - + (uint64_t)a[5] * a[5]; - VERIFY_BITS(d, 63); - /* [d t9 0 0 0 0 0 0 0 0 c] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ - u0 = d & M; d >>= 26; c += u0 * R0; - VERIFY_BITS(u0, 26); - VERIFY_BITS(d, 37); - VERIFY_BITS(c, 61); - /* [d u0 t9 0 0 0 0 0 0 0 0 c-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ - t0 = c & M; c >>= 26; c += u0 * R1; - VERIFY_BITS(t0, 26); - VERIFY_BITS(c, 37); - /* [d u0 t9 0 0 0 0 0 0 0 c-u0*R1 t0-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ - /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ - - c += (uint64_t)(a[0]*2) * a[1]; - VERIFY_BITS(c, 62); - /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 p1 p0] */ - d += (uint64_t)(a[2]*2) * a[9] - + (uint64_t)(a[3]*2) * a[8] - + (uint64_t)(a[4]*2) * a[7] - + (uint64_t)(a[5]*2) * a[6]; - VERIFY_BITS(d, 63); - /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ - u1 = d & M; d >>= 26; c += u1 * R0; - VERIFY_BITS(u1, 26); - VERIFY_BITS(d, 37); - VERIFY_BITS(c, 63); - /* [d u1 0 t9 0 0 0 0 0 0 0 c-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ - t1 = c & M; c >>= 26; c += u1 * R1; - VERIFY_BITS(t1, 26); - VERIFY_BITS(c, 38); - /* [d u1 0 t9 0 0 0 0 0 0 c-u1*R1 t1-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ - /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ - - c += (uint64_t)(a[0]*2) * a[2] - + (uint64_t)a[1] * a[1]; - VERIFY_BITS(c, 62); - /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ - d += (uint64_t)(a[3]*2) * a[9] - + (uint64_t)(a[4]*2) * a[8] - + (uint64_t)(a[5]*2) * a[7] - + (uint64_t)a[6] * a[6]; - VERIFY_BITS(d, 63); - /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ - u2 = d & M; d >>= 26; c += u2 * R0; - VERIFY_BITS(u2, 26); - VERIFY_BITS(d, 37); - VERIFY_BITS(c, 63); - /* [d u2 0 0 t9 0 0 0 0 0 0 c-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ - t2 = c & M; c >>= 26; c += u2 * R1; - VERIFY_BITS(t2, 26); - VERIFY_BITS(c, 38); - /* [d u2 0 0 t9 0 0 0 0 0 c-u2*R1 t2-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ - /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ - - c += (uint64_t)(a[0]*2) * a[3] - + (uint64_t)(a[1]*2) * a[2]; - VERIFY_BITS(c, 63); - /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ - d += (uint64_t)(a[4]*2) * a[9] - + (uint64_t)(a[5]*2) * a[8] - + (uint64_t)(a[6]*2) * a[7]; - VERIFY_BITS(d, 63); - /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ - u3 = d & M; d >>= 26; c += u3 * R0; - VERIFY_BITS(u3, 26); - VERIFY_BITS(d, 37); - /* VERIFY_BITS(c, 64); */ - /* [d u3 0 0 0 t9 0 0 0 0 0 c-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ - t3 = c & M; c >>= 26; c += u3 * R1; - VERIFY_BITS(t3, 26); - VERIFY_BITS(c, 39); - /* [d u3 0 0 0 t9 0 0 0 0 c-u3*R1 t3-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ - /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ - - c += (uint64_t)(a[0]*2) * a[4] - + (uint64_t)(a[1]*2) * a[3] - + (uint64_t)a[2] * a[2]; - VERIFY_BITS(c, 63); - /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ - d += (uint64_t)(a[5]*2) * a[9] - + (uint64_t)(a[6]*2) * a[8] - + (uint64_t)a[7] * a[7]; - VERIFY_BITS(d, 62); - /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ - u4 = d & M; d >>= 26; c += u4 * R0; - VERIFY_BITS(u4, 26); - VERIFY_BITS(d, 36); - /* VERIFY_BITS(c, 64); */ - /* [d u4 0 0 0 0 t9 0 0 0 0 c-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ - t4 = c & M; c >>= 26; c += u4 * R1; - VERIFY_BITS(t4, 26); - VERIFY_BITS(c, 39); - /* [d u4 0 0 0 0 t9 0 0 0 c-u4*R1 t4-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ - - c += (uint64_t)(a[0]*2) * a[5] - + (uint64_t)(a[1]*2) * a[4] - + (uint64_t)(a[2]*2) * a[3]; - VERIFY_BITS(c, 63); - /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ - d += (uint64_t)(a[6]*2) * a[9] - + (uint64_t)(a[7]*2) * a[8]; - VERIFY_BITS(d, 62); - /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ - u5 = d & M; d >>= 26; c += u5 * R0; - VERIFY_BITS(u5, 26); - VERIFY_BITS(d, 36); - /* VERIFY_BITS(c, 64); */ - /* [d u5 0 0 0 0 0 t9 0 0 0 c-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ - t5 = c & M; c >>= 26; c += u5 * R1; - VERIFY_BITS(t5, 26); - VERIFY_BITS(c, 39); - /* [d u5 0 0 0 0 0 t9 0 0 c-u5*R1 t5-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ - - c += (uint64_t)(a[0]*2) * a[6] - + (uint64_t)(a[1]*2) * a[5] - + (uint64_t)(a[2]*2) * a[4] - + (uint64_t)a[3] * a[3]; - VERIFY_BITS(c, 63); - /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ - d += (uint64_t)(a[7]*2) * a[9] - + (uint64_t)a[8] * a[8]; - VERIFY_BITS(d, 61); - /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ - u6 = d & M; d >>= 26; c += u6 * R0; - VERIFY_BITS(u6, 26); - VERIFY_BITS(d, 35); - /* VERIFY_BITS(c, 64); */ - /* [d u6 0 0 0 0 0 0 t9 0 0 c-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ - t6 = c & M; c >>= 26; c += u6 * R1; - VERIFY_BITS(t6, 26); - VERIFY_BITS(c, 39); - /* [d u6 0 0 0 0 0 0 t9 0 c-u6*R1 t6-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ - - c += (uint64_t)(a[0]*2) * a[7] - + (uint64_t)(a[1]*2) * a[6] - + (uint64_t)(a[2]*2) * a[5] - + (uint64_t)(a[3]*2) * a[4]; - /* VERIFY_BITS(c, 64); */ - VERIFY_CHECK(c <= 0x8000007C00000007ULL); - /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ - d += (uint64_t)(a[8]*2) * a[9]; - VERIFY_BITS(d, 58); - /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ - u7 = d & M; d >>= 26; c += u7 * R0; - VERIFY_BITS(u7, 26); - VERIFY_BITS(d, 32); - /* VERIFY_BITS(c, 64); */ - VERIFY_CHECK(c <= 0x800001703FFFC2F7ULL); - /* [d u7 0 0 0 0 0 0 0 t9 0 c-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ - t7 = c & M; c >>= 26; c += u7 * R1; - VERIFY_BITS(t7, 26); - VERIFY_BITS(c, 38); - /* [d u7 0 0 0 0 0 0 0 t9 c-u7*R1 t7-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ - - c += (uint64_t)(a[0]*2) * a[8] - + (uint64_t)(a[1]*2) * a[7] - + (uint64_t)(a[2]*2) * a[6] - + (uint64_t)(a[3]*2) * a[5] - + (uint64_t)a[4] * a[4]; - /* VERIFY_BITS(c, 64); */ - VERIFY_CHECK(c <= 0x9000007B80000008ULL); - /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - d += (uint64_t)a[9] * a[9]; - VERIFY_BITS(d, 57); - /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - u8 = d & M; d >>= 26; c += u8 * R0; - VERIFY_BITS(u8, 26); - VERIFY_BITS(d, 31); - /* VERIFY_BITS(c, 64); */ - VERIFY_CHECK(c <= 0x9000016FBFFFC2F8ULL); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - - r[3] = t3; - VERIFY_BITS(r[3], 26); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[4] = t4; - VERIFY_BITS(r[4], 26); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[5] = t5; - VERIFY_BITS(r[5], 26); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[6] = t6; - VERIFY_BITS(r[6], 26); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[7] = t7; - VERIFY_BITS(r[7], 26); - /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - - r[8] = c & M; c >>= 26; c += u8 * R1; - VERIFY_BITS(r[8], 26); - VERIFY_BITS(c, 39); - /* [d u8 0 0 0 0 0 0 0 0 t9+c-u8*R1 r8-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 0 0 0 0 t9+c r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += d * R0 + t9; - VERIFY_BITS(c, 45); - /* [d 0 0 0 0 0 0 0 0 0 c-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[9] = c & (M >> 4); c >>= 22; c += d * (R1 << 4); - VERIFY_BITS(r[9], 22); - VERIFY_BITS(c, 46); - /* [d 0 0 0 0 0 0 0 0 r9+((c-d*R1<<4)<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - - d = c * (R0 >> 4) + t0; - VERIFY_BITS(d, 56); - /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[0] = d & M; d >>= 26; - VERIFY_BITS(r[0], 26); - VERIFY_BITS(d, 30); - /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - d += c * (R1 >> 4) + t1; - VERIFY_BITS(d, 53); - VERIFY_CHECK(d <= 0x10000003FFFFBFULL); - /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - /* [r9 r8 r7 r6 r5 r4 r3 t2 d r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[1] = d & M; d >>= 26; - VERIFY_BITS(r[1], 26); - VERIFY_BITS(d, 27); - VERIFY_CHECK(d <= 0x4000000ULL); - /* [r9 r8 r7 r6 r5 r4 r3 t2+d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - d += t2; - VERIFY_BITS(d, 27); - /* [r9 r8 r7 r6 r5 r4 r3 d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[2] = d; - VERIFY_BITS(r[2], 27); - /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ -} -#endif - -static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - VERIFY_CHECK(b->magnitude <= 8); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); - VERIFY_CHECK(r != b); - VERIFY_CHECK(a != b); -#endif - secp256k1_fe_mul_inner(r->n, a->n, b->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif -} - -static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - secp256k1_fe_verify(a); -#endif - secp256k1_fe_sqr_inner(r->n, a->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif -} - -static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { - uint32_t mask0, mask1; - VG_CHECK_VERIFY(r->n, sizeof(r->n)); - mask0 = flag + ~((uint32_t)0); - mask1 = ~mask0; - r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); - r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); - r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); - r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); - r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); - r->n[5] = (r->n[5] & mask0) | (a->n[5] & mask1); - r->n[6] = (r->n[6] & mask0) | (a->n[6] & mask1); - r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); - r->n[8] = (r->n[8] & mask0) | (a->n[8] & mask1); - r->n[9] = (r->n[9] & mask0) | (a->n[9] & mask1); -#ifdef VERIFY - if (flag) { - r->magnitude = a->magnitude; - r->normalized = a->normalized; - } -#endif -} - -static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { - uint32_t mask0, mask1; - VG_CHECK_VERIFY(r->n, sizeof(r->n)); - mask0 = flag + ~((uint32_t)0); - mask1 = ~mask0; - r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); - r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); - r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); - r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); - r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); - r->n[5] = (r->n[5] & mask0) | (a->n[5] & mask1); - r->n[6] = (r->n[6] & mask0) | (a->n[6] & mask1); - r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); -} - -static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); -#endif - r->n[0] = a->n[0] | a->n[1] << 26; - r->n[1] = a->n[1] >> 6 | a->n[2] << 20; - r->n[2] = a->n[2] >> 12 | a->n[3] << 14; - r->n[3] = a->n[3] >> 18 | a->n[4] << 8; - r->n[4] = a->n[4] >> 24 | a->n[5] << 2 | a->n[6] << 28; - r->n[5] = a->n[6] >> 4 | a->n[7] << 22; - r->n[6] = a->n[7] >> 10 | a->n[8] << 16; - r->n[7] = a->n[8] >> 16 | a->n[9] << 10; -} - -static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { - r->n[0] = a->n[0] & 0x3FFFFFFUL; - r->n[1] = a->n[0] >> 26 | ((a->n[1] << 6) & 0x3FFFFFFUL); - r->n[2] = a->n[1] >> 20 | ((a->n[2] << 12) & 0x3FFFFFFUL); - r->n[3] = a->n[2] >> 14 | ((a->n[3] << 18) & 0x3FFFFFFUL); - r->n[4] = a->n[3] >> 8 | ((a->n[4] << 24) & 0x3FFFFFFUL); - r->n[5] = (a->n[4] >> 2) & 0x3FFFFFFUL; - r->n[6] = a->n[4] >> 28 | ((a->n[5] << 4) & 0x3FFFFFFUL); - r->n[7] = a->n[5] >> 22 | ((a->n[6] << 10) & 0x3FFFFFFUL); - r->n[8] = a->n[6] >> 16 | ((a->n[7] << 16) & 0x3FFFFFFUL); - r->n[9] = a->n[7] >> 10; -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; -#endif -} - -static void secp256k1_fe_from_signed30(secp256k1_fe *r, const secp256k1_modinv32_signed30 *a) { - const uint32_t M26 = UINT32_MAX >> 6; - const uint32_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4], - a5 = a->v[5], a6 = a->v[6], a7 = a->v[7], a8 = a->v[8]; - - /* The output from secp256k1_modinv32{_var} should be normalized to range [0,modulus), and - * have limbs in [0,2^30). The modulus is < 2^256, so the top limb must be below 2^(256-30*8). - */ - VERIFY_CHECK(a0 >> 30 == 0); - VERIFY_CHECK(a1 >> 30 == 0); - VERIFY_CHECK(a2 >> 30 == 0); - VERIFY_CHECK(a3 >> 30 == 0); - VERIFY_CHECK(a4 >> 30 == 0); - VERIFY_CHECK(a5 >> 30 == 0); - VERIFY_CHECK(a6 >> 30 == 0); - VERIFY_CHECK(a7 >> 30 == 0); - VERIFY_CHECK(a8 >> 16 == 0); - - r->n[0] = a0 & M26; - r->n[1] = (a0 >> 26 | a1 << 4) & M26; - r->n[2] = (a1 >> 22 | a2 << 8) & M26; - r->n[3] = (a2 >> 18 | a3 << 12) & M26; - r->n[4] = (a3 >> 14 | a4 << 16) & M26; - r->n[5] = (a4 >> 10 | a5 << 20) & M26; - r->n[6] = (a5 >> 6 | a6 << 24) & M26; - r->n[7] = (a6 >> 2 ) & M26; - r->n[8] = (a6 >> 28 | a7 << 2) & M26; - r->n[9] = (a7 >> 24 | a8 << 6); - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif -} - -static void secp256k1_fe_to_signed30(secp256k1_modinv32_signed30 *r, const secp256k1_fe *a) { - const uint32_t M30 = UINT32_MAX >> 2; - const uint64_t a0 = a->n[0], a1 = a->n[1], a2 = a->n[2], a3 = a->n[3], a4 = a->n[4], - a5 = a->n[5], a6 = a->n[6], a7 = a->n[7], a8 = a->n[8], a9 = a->n[9]; - -#ifdef VERIFY - VERIFY_CHECK(a->normalized); -#endif - - r->v[0] = (a0 | a1 << 26) & M30; - r->v[1] = (a1 >> 4 | a2 << 22) & M30; - r->v[2] = (a2 >> 8 | a3 << 18) & M30; - r->v[3] = (a3 >> 12 | a4 << 14) & M30; - r->v[4] = (a4 >> 16 | a5 << 10) & M30; - r->v[5] = (a5 >> 20 | a6 << 6) & M30; - r->v[6] = (a6 >> 24 | a7 << 2 - | a8 << 28) & M30; - r->v[7] = (a8 >> 2 | a9 << 24) & M30; - r->v[8] = a9 >> 6; -} - -static const secp256k1_modinv32_modinfo secp256k1_const_modinfo_fe = { - {{-0x3D1, -4, 0, 0, 0, 0, 0, 0, 65536}}, - 0x2DDACACFL -}; - -static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *x) { - secp256k1_fe tmp; - secp256k1_modinv32_signed30 s; - - tmp = *x; - secp256k1_fe_normalize(&tmp); - secp256k1_fe_to_signed30(&s, &tmp); - secp256k1_modinv32(&s, &secp256k1_const_modinfo_fe); - secp256k1_fe_from_signed30(r, &s); - - VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp)); -} - -static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { - secp256k1_fe tmp; - secp256k1_modinv32_signed30 s; - - tmp = *x; - secp256k1_fe_normalize_var(&tmp); - secp256k1_fe_to_signed30(&s, &tmp); - secp256k1_modinv32_var(&s, &secp256k1_const_modinfo_fe); - secp256k1_fe_from_signed30(r, &s); - - VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp)); -} - -#endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/libwallet/musig/field_5x52.h b/libwallet/musig/field_5x52.h deleted file mode 100644 index 50ee3f9e..00000000 --- a/libwallet/musig/field_5x52.h +++ /dev/null @@ -1,55 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_FIELD_REPR_H -#define SECP256K1_FIELD_REPR_H - -#include - -typedef struct { - /* X = sum(i=0..4, n[i]*2^(i*52)) mod p - * where p = 2^256 - 0x1000003D1 - */ - uint64_t n[5]; -#ifdef VERIFY - int magnitude; - int normalized; -#endif -} secp256k1_fe; - -/* Unpacks a constant into a overlapping multi-limbed FE element. */ -#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ - (d0) | (((uint64_t)(d1) & 0xFFFFFUL) << 32), \ - ((uint64_t)(d1) >> 20) | (((uint64_t)(d2)) << 12) | (((uint64_t)(d3) & 0xFFUL) << 44), \ - ((uint64_t)(d3) >> 8) | (((uint64_t)(d4) & 0xFFFFFFFUL) << 24), \ - ((uint64_t)(d4) >> 28) | (((uint64_t)(d5)) << 4) | (((uint64_t)(d6) & 0xFFFFUL) << 36), \ - ((uint64_t)(d6) >> 16) | (((uint64_t)(d7)) << 16) \ -} - -#ifdef VERIFY -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} -#else -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} -#endif - -typedef struct { - uint64_t n[4]; -} secp256k1_fe_storage; - -#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ \ - (d0) | (((uint64_t)(d1)) << 32), \ - (d2) | (((uint64_t)(d3)) << 32), \ - (d4) | (((uint64_t)(d5)) << 32), \ - (d6) | (((uint64_t)(d7)) << 32) \ -}} - -#define SECP256K1_FE_STORAGE_CONST_GET(d) \ - (uint32_t)(d.n[3] >> 32), (uint32_t)d.n[3], \ - (uint32_t)(d.n[2] >> 32), (uint32_t)d.n[2], \ - (uint32_t)(d.n[1] >> 32), (uint32_t)d.n[1], \ - (uint32_t)(d.n[0] >> 32), (uint32_t)d.n[0] - -#endif /* SECP256K1_FIELD_REPR_H */ diff --git a/libwallet/musig/field_5x52_asm_impl.h b/libwallet/musig/field_5x52_asm_impl.h deleted file mode 100644 index a2118044..00000000 --- a/libwallet/musig/field_5x52_asm_impl.h +++ /dev/null @@ -1,502 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013-2014 Diederik Huys, Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -/** - * Changelog: - * - March 2013, Diederik Huys: original version - * - November 2014, Pieter Wuille: updated to use Peter Dettman's parallel multiplication algorithm - * - December 2014, Pieter Wuille: converted from YASM to GCC inline assembly - */ - -#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H -#define SECP256K1_FIELD_INNER5X52_IMPL_H - -SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { -/** - * Registers: rdx:rax = multiplication accumulator - * r9:r8 = c - * r15:rcx = d - * r10-r14 = a0-a4 - * rbx = b - * rdi = r - * rsi = a / t? - */ - uint64_t tmp1, tmp2, tmp3; -__asm__ __volatile__( - "movq 0(%%rsi),%%r10\n" - "movq 8(%%rsi),%%r11\n" - "movq 16(%%rsi),%%r12\n" - "movq 24(%%rsi),%%r13\n" - "movq 32(%%rsi),%%r14\n" - - /* d += a3 * b0 */ - "movq 0(%%rbx),%%rax\n" - "mulq %%r13\n" - "movq %%rax,%%rcx\n" - "movq %%rdx,%%r15\n" - /* d += a2 * b1 */ - "movq 8(%%rbx),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a1 * b2 */ - "movq 16(%%rbx),%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d = a0 * b3 */ - "movq 24(%%rbx),%%rax\n" - "mulq %%r10\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* c = a4 * b4 */ - "movq 32(%%rbx),%%rax\n" - "mulq %%r14\n" - "movq %%rax,%%r8\n" - "movq %%rdx,%%r9\n" - /* d += (c & M) * R */ - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* c >>= 52 (%%r8 only) */ - "shrdq $52,%%r9,%%r8\n" - /* t3 (tmp1) = d & M */ - "movq %%rcx,%%rsi\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rsi\n" - "movq %%rsi,%q1\n" - /* d >>= 52 */ - "shrdq $52,%%r15,%%rcx\n" - "xorq %%r15,%%r15\n" - /* d += a4 * b0 */ - "movq 0(%%rbx),%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a3 * b1 */ - "movq 8(%%rbx),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a2 * b2 */ - "movq 16(%%rbx),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a1 * b3 */ - "movq 24(%%rbx),%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a0 * b4 */ - "movq 32(%%rbx),%%rax\n" - "mulq %%r10\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += c * R */ - "movq %%r8,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* t4 = d & M (%%rsi) */ - "movq %%rcx,%%rsi\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rsi\n" - /* d >>= 52 */ - "shrdq $52,%%r15,%%rcx\n" - "xorq %%r15,%%r15\n" - /* tx = t4 >> 48 (tmp3) */ - "movq %%rsi,%%rax\n" - "shrq $48,%%rax\n" - "movq %%rax,%q3\n" - /* t4 &= (M >> 4) (tmp2) */ - "movq $0xffffffffffff,%%rax\n" - "andq %%rax,%%rsi\n" - "movq %%rsi,%q2\n" - /* c = a0 * b0 */ - "movq 0(%%rbx),%%rax\n" - "mulq %%r10\n" - "movq %%rax,%%r8\n" - "movq %%rdx,%%r9\n" - /* d += a4 * b1 */ - "movq 8(%%rbx),%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a3 * b2 */ - "movq 16(%%rbx),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a2 * b3 */ - "movq 24(%%rbx),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a1 * b4 */ - "movq 32(%%rbx),%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* u0 = d & M (%%rsi) */ - "movq %%rcx,%%rsi\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rsi\n" - /* d >>= 52 */ - "shrdq $52,%%r15,%%rcx\n" - "xorq %%r15,%%r15\n" - /* u0 = (u0 << 4) | tx (%%rsi) */ - "shlq $4,%%rsi\n" - "movq %q3,%%rax\n" - "orq %%rax,%%rsi\n" - /* c += u0 * (R >> 4) */ - "movq $0x1000003d1,%%rax\n" - "mulq %%rsi\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* r[0] = c & M */ - "movq %%r8,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq %%rax,0(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* c += a1 * b0 */ - "movq 0(%%rbx),%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* c += a0 * b1 */ - "movq 8(%%rbx),%%rax\n" - "mulq %%r10\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d += a4 * b2 */ - "movq 16(%%rbx),%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a3 * b3 */ - "movq 24(%%rbx),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a2 * b4 */ - "movq 32(%%rbx),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* c += (d & M) * R */ - "movq %%rcx,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d >>= 52 */ - "shrdq $52,%%r15,%%rcx\n" - "xorq %%r15,%%r15\n" - /* r[1] = c & M */ - "movq %%r8,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq %%rax,8(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* c += a2 * b0 */ - "movq 0(%%rbx),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* c += a1 * b1 */ - "movq 8(%%rbx),%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* c += a0 * b2 (last use of %%r10 = a0) */ - "movq 16(%%rbx),%%rax\n" - "mulq %%r10\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* fetch t3 (%%r10, overwrites a0), t4 (%%rsi) */ - "movq %q2,%%rsi\n" - "movq %q1,%%r10\n" - /* d += a4 * b3 */ - "movq 24(%%rbx),%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a3 * b4 */ - "movq 32(%%rbx),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* c += (d & M) * R */ - "movq %%rcx,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d >>= 52 (%%rcx only) */ - "shrdq $52,%%r15,%%rcx\n" - /* r[2] = c & M */ - "movq %%r8,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq %%rax,16(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* c += t3 */ - "addq %%r10,%%r8\n" - /* c += d * R */ - "movq %%rcx,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* r[3] = c & M */ - "movq %%r8,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq %%rax,24(%%rdi)\n" - /* c >>= 52 (%%r8 only) */ - "shrdq $52,%%r9,%%r8\n" - /* c += t4 (%%r8 only) */ - "addq %%rsi,%%r8\n" - /* r[4] = c */ - "movq %%r8,32(%%rdi)\n" -: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) -: "b"(b), "D"(r) -: "%rax", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" -); -} - -SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t *a) { -/** - * Registers: rdx:rax = multiplication accumulator - * r9:r8 = c - * rcx:rbx = d - * r10-r14 = a0-a4 - * r15 = M (0xfffffffffffff) - * rdi = r - * rsi = a / t? - */ - uint64_t tmp1, tmp2, tmp3; -__asm__ __volatile__( - "movq 0(%%rsi),%%r10\n" - "movq 8(%%rsi),%%r11\n" - "movq 16(%%rsi),%%r12\n" - "movq 24(%%rsi),%%r13\n" - "movq 32(%%rsi),%%r14\n" - "movq $0xfffffffffffff,%%r15\n" - - /* d = (a0*2) * a3 */ - "leaq (%%r10,%%r10,1),%%rax\n" - "mulq %%r13\n" - "movq %%rax,%%rbx\n" - "movq %%rdx,%%rcx\n" - /* d += (a1*2) * a2 */ - "leaq (%%r11,%%r11,1),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* c = a4 * a4 */ - "movq %%r14,%%rax\n" - "mulq %%r14\n" - "movq %%rax,%%r8\n" - "movq %%rdx,%%r9\n" - /* d += (c & M) * R */ - "andq %%r15,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* c >>= 52 (%%r8 only) */ - "shrdq $52,%%r9,%%r8\n" - /* t3 (tmp1) = d & M */ - "movq %%rbx,%%rsi\n" - "andq %%r15,%%rsi\n" - "movq %%rsi,%q1\n" - /* d >>= 52 */ - "shrdq $52,%%rcx,%%rbx\n" - "xorq %%rcx,%%rcx\n" - /* a4 *= 2 */ - "addq %%r14,%%r14\n" - /* d += a0 * a4 */ - "movq %%r10,%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* d+= (a1*2) * a3 */ - "leaq (%%r11,%%r11,1),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* d += a2 * a2 */ - "movq %%r12,%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* d += c * R */ - "movq %%r8,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* t4 = d & M (%%rsi) */ - "movq %%rbx,%%rsi\n" - "andq %%r15,%%rsi\n" - /* d >>= 52 */ - "shrdq $52,%%rcx,%%rbx\n" - "xorq %%rcx,%%rcx\n" - /* tx = t4 >> 48 (tmp3) */ - "movq %%rsi,%%rax\n" - "shrq $48,%%rax\n" - "movq %%rax,%q3\n" - /* t4 &= (M >> 4) (tmp2) */ - "movq $0xffffffffffff,%%rax\n" - "andq %%rax,%%rsi\n" - "movq %%rsi,%q2\n" - /* c = a0 * a0 */ - "movq %%r10,%%rax\n" - "mulq %%r10\n" - "movq %%rax,%%r8\n" - "movq %%rdx,%%r9\n" - /* d += a1 * a4 */ - "movq %%r11,%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* d += (a2*2) * a3 */ - "leaq (%%r12,%%r12,1),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* u0 = d & M (%%rsi) */ - "movq %%rbx,%%rsi\n" - "andq %%r15,%%rsi\n" - /* d >>= 52 */ - "shrdq $52,%%rcx,%%rbx\n" - "xorq %%rcx,%%rcx\n" - /* u0 = (u0 << 4) | tx (%%rsi) */ - "shlq $4,%%rsi\n" - "movq %q3,%%rax\n" - "orq %%rax,%%rsi\n" - /* c += u0 * (R >> 4) */ - "movq $0x1000003d1,%%rax\n" - "mulq %%rsi\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* r[0] = c & M */ - "movq %%r8,%%rax\n" - "andq %%r15,%%rax\n" - "movq %%rax,0(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* a0 *= 2 */ - "addq %%r10,%%r10\n" - /* c += a0 * a1 */ - "movq %%r10,%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d += a2 * a4 */ - "movq %%r12,%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* d += a3 * a3 */ - "movq %%r13,%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* c += (d & M) * R */ - "movq %%rbx,%%rax\n" - "andq %%r15,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d >>= 52 */ - "shrdq $52,%%rcx,%%rbx\n" - "xorq %%rcx,%%rcx\n" - /* r[1] = c & M */ - "movq %%r8,%%rax\n" - "andq %%r15,%%rax\n" - "movq %%rax,8(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* c += a0 * a2 (last use of %%r10) */ - "movq %%r10,%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* fetch t3 (%%r10, overwrites a0),t4 (%%rsi) */ - "movq %q2,%%rsi\n" - "movq %q1,%%r10\n" - /* c += a1 * a1 */ - "movq %%r11,%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d += a3 * a4 */ - "movq %%r13,%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* c += (d & M) * R */ - "movq %%rbx,%%rax\n" - "andq %%r15,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d >>= 52 (%%rbx only) */ - "shrdq $52,%%rcx,%%rbx\n" - /* r[2] = c & M */ - "movq %%r8,%%rax\n" - "andq %%r15,%%rax\n" - "movq %%rax,16(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* c += t3 */ - "addq %%r10,%%r8\n" - /* c += d * R */ - "movq %%rbx,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* r[3] = c & M */ - "movq %%r8,%%rax\n" - "andq %%r15,%%rax\n" - "movq %%rax,24(%%rdi)\n" - /* c >>= 52 (%%r8 only) */ - "shrdq $52,%%r9,%%r8\n" - /* c += t4 (%%r8 only) */ - "addq %%rsi,%%r8\n" - /* r[4] = c */ - "movq %%r8,32(%%rdi)\n" -: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) -: "D"(r) -: "%rax", "%rbx", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" -); -} - -#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */ diff --git a/libwallet/musig/field_5x52_impl.h b/libwallet/musig/field_5x52_impl.h deleted file mode 100644 index 60ded927..00000000 --- a/libwallet/musig/field_5x52_impl.h +++ /dev/null @@ -1,578 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_FIELD_REPR_IMPL_H -#define SECP256K1_FIELD_REPR_IMPL_H - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - -#include "util.h" -#include "field.h" -#include "modinv64_impl.h" - -#if defined(USE_ASM_X86_64) -#include "field_5x52_asm_impl.h" -#else -#include "field_5x52_int128_impl.h" -#endif - -/** Implements arithmetic modulo FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F, - * represented as 5 uint64_t's in base 2^52. The values are allowed to contain >52 each. In particular, - * each FieldElem has a 'magnitude' associated with it. Internally, a magnitude M means each element - * is at most M*(2^53-1), except the most significant one, which is limited to M*(2^49-1). All operations - * accept any input with magnitude at most M, and have different rules for propagating magnitude to their - * output. - */ - -#ifdef VERIFY -static void secp256k1_fe_verify(const secp256k1_fe *a) { - const uint64_t *d = a->n; - int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; - /* secp256k1 'p' value defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ - r &= (d[0] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[1] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[2] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[3] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[4] <= 0x0FFFFFFFFFFFFULL * m); - r &= (a->magnitude >= 0); - r &= (a->magnitude <= 2048); - if (a->normalized) { - r &= (a->magnitude <= 1); - if (r && (d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) { - r &= (d[0] < 0xFFFFEFFFFFC2FULL); - } - } - VERIFY_CHECK(r == 1); -} -#endif - -static void secp256k1_fe_normalize(secp256k1_fe *r) { - uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; - - /* Reduce t4 at the start so there will be at most a single carry from the first pass */ - uint64_t m; - uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; - - /* The first pass ensures the magnitude is 1, ... */ - t0 += x * 0x1000003D1ULL; - t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; - t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1; - t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2; - t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3; - - /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ - VERIFY_CHECK(t4 >> 49 == 0); - - /* At most a single final reduction is needed; check if the value is >= the field characteristic */ - x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL) - & (t0 >= 0xFFFFEFFFFFC2FULL)); - - /* Apply the final reduction (for constant-time behaviour, we do it always) */ - t0 += x * 0x1000003D1ULL; - t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; - t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; - t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; - t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; - - /* If t4 didn't carry to bit 48 already, then it should have after any final reduction */ - VERIFY_CHECK(t4 >> 48 == x); - - /* Mask off the possible multiple of 2^256 from the final reduction */ - t4 &= 0x0FFFFFFFFFFFFULL; - - r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif -} - -static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { - uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; - - /* Reduce t4 at the start so there will be at most a single carry from the first pass */ - uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; - - /* The first pass ensures the magnitude is 1, ... */ - t0 += x * 0x1000003D1ULL; - t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; - t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; - t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; - t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; - - /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ - VERIFY_CHECK(t4 >> 49 == 0); - - r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - -#ifdef VERIFY - r->magnitude = 1; - secp256k1_fe_verify(r); -#endif -} - -static void secp256k1_fe_normalize_var(secp256k1_fe *r) { - uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; - - /* Reduce t4 at the start so there will be at most a single carry from the first pass */ - uint64_t m; - uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; - - /* The first pass ensures the magnitude is 1, ... */ - t0 += x * 0x1000003D1ULL; - t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; - t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1; - t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2; - t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3; - - /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ - VERIFY_CHECK(t4 >> 49 == 0); - - /* At most a single final reduction is needed; check if the value is >= the field characteristic */ - x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL) - & (t0 >= 0xFFFFEFFFFFC2FULL)); - - if (x) { - t0 += 0x1000003D1ULL; - t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; - t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; - t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; - t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; - - /* If t4 didn't carry to bit 48 already, then it should have after any final reduction */ - VERIFY_CHECK(t4 >> 48 == x); - - /* Mask off the possible multiple of 2^256 from the final reduction */ - t4 &= 0x0FFFFFFFFFFFFULL; - } - - r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif -} - -static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) { - uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; - - /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ - uint64_t z0, z1; - - /* Reduce t4 at the start so there will be at most a single carry from the first pass */ - uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; - - /* The first pass ensures the magnitude is 1, ... */ - t0 += x * 0x1000003D1ULL; - t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; z0 = t0; z1 = t0 ^ 0x1000003D0ULL; - t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1; - t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2; - t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3; - z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL; - - /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ - VERIFY_CHECK(t4 >> 49 == 0); - - return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); -} - -static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) { - uint64_t t0, t1, t2, t3, t4; - uint64_t z0, z1; - uint64_t x; - - t0 = r->n[0]; - t4 = r->n[4]; - - /* Reduce t4 at the start so there will be at most a single carry from the first pass */ - x = t4 >> 48; - - /* The first pass ensures the magnitude is 1, ... */ - t0 += x * 0x1000003D1ULL; - - /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ - z0 = t0 & 0xFFFFFFFFFFFFFULL; - z1 = z0 ^ 0x1000003D0ULL; - - /* Fast return path should catch the majority of cases */ - if ((z0 != 0ULL) & (z1 != 0xFFFFFFFFFFFFFULL)) { - return 0; - } - - t1 = r->n[1]; - t2 = r->n[2]; - t3 = r->n[3]; - - t4 &= 0x0FFFFFFFFFFFFULL; - - t1 += (t0 >> 52); - t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1; - t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2; - t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3; - z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL; - - /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ - VERIFY_CHECK(t4 >> 49 == 0); - - return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); -} - -SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { - r->n[0] = a; - r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif -} - -SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { - const uint64_t *t = a->n; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif - return (t[0] | t[1] | t[2] | t[3] | t[4]) == 0; -} - -SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif - return a->n[0] & 1; -} - -SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { - int i; -#ifdef VERIFY - a->magnitude = 0; - a->normalized = 1; -#endif - for (i=0; i<5; i++) { - a->n[i] = 0; - } -} - -static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { - int i; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - VERIFY_CHECK(b->normalized); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); -#endif - for (i = 4; i >= 0; i--) { - if (a->n[i] > b->n[i]) { - return 1; - } - if (a->n[i] < b->n[i]) { - return -1; - } - } - return 0; -} - -static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { - int ret; - r->n[0] = (uint64_t)a[31] - | ((uint64_t)a[30] << 8) - | ((uint64_t)a[29] << 16) - | ((uint64_t)a[28] << 24) - | ((uint64_t)a[27] << 32) - | ((uint64_t)a[26] << 40) - | ((uint64_t)(a[25] & 0xF) << 48); - r->n[1] = (uint64_t)((a[25] >> 4) & 0xF) - | ((uint64_t)a[24] << 4) - | ((uint64_t)a[23] << 12) - | ((uint64_t)a[22] << 20) - | ((uint64_t)a[21] << 28) - | ((uint64_t)a[20] << 36) - | ((uint64_t)a[19] << 44); - r->n[2] = (uint64_t)a[18] - | ((uint64_t)a[17] << 8) - | ((uint64_t)a[16] << 16) - | ((uint64_t)a[15] << 24) - | ((uint64_t)a[14] << 32) - | ((uint64_t)a[13] << 40) - | ((uint64_t)(a[12] & 0xF) << 48); - r->n[3] = (uint64_t)((a[12] >> 4) & 0xF) - | ((uint64_t)a[11] << 4) - | ((uint64_t)a[10] << 12) - | ((uint64_t)a[9] << 20) - | ((uint64_t)a[8] << 28) - | ((uint64_t)a[7] << 36) - | ((uint64_t)a[6] << 44); - r->n[4] = (uint64_t)a[5] - | ((uint64_t)a[4] << 8) - | ((uint64_t)a[3] << 16) - | ((uint64_t)a[2] << 24) - | ((uint64_t)a[1] << 32) - | ((uint64_t)a[0] << 40); - ret = !((r->n[4] == 0x0FFFFFFFFFFFFULL) & ((r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL) & (r->n[0] >= 0xFFFFEFFFFFC2FULL)); -#ifdef VERIFY - r->magnitude = 1; - if (ret) { - r->normalized = 1; - secp256k1_fe_verify(r); - } else { - r->normalized = 0; - } -#endif - return ret; -} - -/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ -static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif - r[0] = (a->n[4] >> 40) & 0xFF; - r[1] = (a->n[4] >> 32) & 0xFF; - r[2] = (a->n[4] >> 24) & 0xFF; - r[3] = (a->n[4] >> 16) & 0xFF; - r[4] = (a->n[4] >> 8) & 0xFF; - r[5] = a->n[4] & 0xFF; - r[6] = (a->n[3] >> 44) & 0xFF; - r[7] = (a->n[3] >> 36) & 0xFF; - r[8] = (a->n[3] >> 28) & 0xFF; - r[9] = (a->n[3] >> 20) & 0xFF; - r[10] = (a->n[3] >> 12) & 0xFF; - r[11] = (a->n[3] >> 4) & 0xFF; - r[12] = ((a->n[2] >> 48) & 0xF) | ((a->n[3] & 0xF) << 4); - r[13] = (a->n[2] >> 40) & 0xFF; - r[14] = (a->n[2] >> 32) & 0xFF; - r[15] = (a->n[2] >> 24) & 0xFF; - r[16] = (a->n[2] >> 16) & 0xFF; - r[17] = (a->n[2] >> 8) & 0xFF; - r[18] = a->n[2] & 0xFF; - r[19] = (a->n[1] >> 44) & 0xFF; - r[20] = (a->n[1] >> 36) & 0xFF; - r[21] = (a->n[1] >> 28) & 0xFF; - r[22] = (a->n[1] >> 20) & 0xFF; - r[23] = (a->n[1] >> 12) & 0xFF; - r[24] = (a->n[1] >> 4) & 0xFF; - r[25] = ((a->n[0] >> 48) & 0xF) | ((a->n[1] & 0xF) << 4); - r[26] = (a->n[0] >> 40) & 0xFF; - r[27] = (a->n[0] >> 32) & 0xFF; - r[28] = (a->n[0] >> 24) & 0xFF; - r[29] = (a->n[0] >> 16) & 0xFF; - r[30] = (a->n[0] >> 8) & 0xFF; - r[31] = a->n[0] & 0xFF; -} - -SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= m); - secp256k1_fe_verify(a); -#endif - r->n[0] = 0xFFFFEFFFFFC2FULL * 2 * (m + 1) - a->n[0]; - r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[1]; - r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[2]; - r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[3]; - r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * (m + 1) - a->n[4]; -#ifdef VERIFY - r->magnitude = m + 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif -} - -SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { - r->n[0] *= a; - r->n[1] *= a; - r->n[2] *= a; - r->n[3] *= a; - r->n[4] *= a; -#ifdef VERIFY - r->magnitude *= a; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif -} - -SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - secp256k1_fe_verify(a); -#endif - r->n[0] += a->n[0]; - r->n[1] += a->n[1]; - r->n[2] += a->n[2]; - r->n[3] += a->n[3]; - r->n[4] += a->n[4]; -#ifdef VERIFY - r->magnitude += a->magnitude; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif -} - -static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - VERIFY_CHECK(b->magnitude <= 8); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); - VERIFY_CHECK(r != b); - VERIFY_CHECK(a != b); -#endif - secp256k1_fe_mul_inner(r->n, a->n, b->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif -} - -static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - secp256k1_fe_verify(a); -#endif - secp256k1_fe_sqr_inner(r->n, a->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif -} - -static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { - uint64_t mask0, mask1; - VG_CHECK_VERIFY(r->n, sizeof(r->n)); - mask0 = flag + ~((uint64_t)0); - mask1 = ~mask0; - r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); - r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); - r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); - r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); - r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); -#ifdef VERIFY - if (flag) { - r->magnitude = a->magnitude; - r->normalized = a->normalized; - } -#endif -} - -static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { - uint64_t mask0, mask1; - VG_CHECK_VERIFY(r->n, sizeof(r->n)); - mask0 = flag + ~((uint64_t)0); - mask1 = ~mask0; - r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); - r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); - r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); - r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); -} - -static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); -#endif - r->n[0] = a->n[0] | a->n[1] << 52; - r->n[1] = a->n[1] >> 12 | a->n[2] << 40; - r->n[2] = a->n[2] >> 24 | a->n[3] << 28; - r->n[3] = a->n[3] >> 36 | a->n[4] << 16; -} - -static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { - r->n[0] = a->n[0] & 0xFFFFFFFFFFFFFULL; - r->n[1] = a->n[0] >> 52 | ((a->n[1] << 12) & 0xFFFFFFFFFFFFFULL); - r->n[2] = a->n[1] >> 40 | ((a->n[2] << 24) & 0xFFFFFFFFFFFFFULL); - r->n[3] = a->n[2] >> 28 | ((a->n[3] << 36) & 0xFFFFFFFFFFFFFULL); - r->n[4] = a->n[3] >> 16; -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; -#endif -} - -static void secp256k1_fe_from_signed62(secp256k1_fe *r, const secp256k1_modinv64_signed62 *a) { - const uint64_t M52 = UINT64_MAX >> 12; - const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4]; - - /* The output from secp256k1_modinv64{_var} should be normalized to range [0,modulus), and - * have limbs in [0,2^62). The modulus is < 2^256, so the top limb must be below 2^(256-62*4). - */ - VERIFY_CHECK(a0 >> 62 == 0); - VERIFY_CHECK(a1 >> 62 == 0); - VERIFY_CHECK(a2 >> 62 == 0); - VERIFY_CHECK(a3 >> 62 == 0); - VERIFY_CHECK(a4 >> 8 == 0); - - r->n[0] = a0 & M52; - r->n[1] = (a0 >> 52 | a1 << 10) & M52; - r->n[2] = (a1 >> 42 | a2 << 20) & M52; - r->n[3] = (a2 >> 32 | a3 << 30) & M52; - r->n[4] = (a3 >> 22 | a4 << 40); - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif -} - -static void secp256k1_fe_to_signed62(secp256k1_modinv64_signed62 *r, const secp256k1_fe *a) { - const uint64_t M62 = UINT64_MAX >> 2; - const uint64_t a0 = a->n[0], a1 = a->n[1], a2 = a->n[2], a3 = a->n[3], a4 = a->n[4]; - -#ifdef VERIFY - VERIFY_CHECK(a->normalized); -#endif - - r->v[0] = (a0 | a1 << 52) & M62; - r->v[1] = (a1 >> 10 | a2 << 42) & M62; - r->v[2] = (a2 >> 20 | a3 << 32) & M62; - r->v[3] = (a3 >> 30 | a4 << 22) & M62; - r->v[4] = a4 >> 40; -} - -static const secp256k1_modinv64_modinfo secp256k1_const_modinfo_fe = { - {{-0x1000003D1LL, 0, 0, 0, 256}}, - 0x27C7F6E22DDACACFLL -}; - -static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *x) { - secp256k1_fe tmp; - secp256k1_modinv64_signed62 s; - - tmp = *x; - secp256k1_fe_normalize(&tmp); - secp256k1_fe_to_signed62(&s, &tmp); - secp256k1_modinv64(&s, &secp256k1_const_modinfo_fe); - secp256k1_fe_from_signed62(r, &s); - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp)); -#endif -} - -static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { - secp256k1_fe tmp; - secp256k1_modinv64_signed62 s; - - tmp = *x; - secp256k1_fe_normalize_var(&tmp); - secp256k1_fe_to_signed62(&s, &tmp); - secp256k1_modinv64_var(&s, &secp256k1_const_modinfo_fe); - secp256k1_fe_from_signed62(r, &s); - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp)); -#endif -} - -#endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/libwallet/musig/field_5x52_int128_impl.h b/libwallet/musig/field_5x52_int128_impl.h deleted file mode 100644 index 314002ee..00000000 --- a/libwallet/musig/field_5x52_int128_impl.h +++ /dev/null @@ -1,279 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H -#define SECP256K1_FIELD_INNER5X52_IMPL_H - -#include - -#ifdef VERIFY -#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) -#else -#define VERIFY_BITS(x, n) do { } while(0) -#endif - -SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { - uint128_t c, d; - uint64_t t3, t4, tx, u0; - uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4]; - const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL; - - VERIFY_BITS(a[0], 56); - VERIFY_BITS(a[1], 56); - VERIFY_BITS(a[2], 56); - VERIFY_BITS(a[3], 56); - VERIFY_BITS(a[4], 52); - VERIFY_BITS(b[0], 56); - VERIFY_BITS(b[1], 56); - VERIFY_BITS(b[2], 56); - VERIFY_BITS(b[3], 56); - VERIFY_BITS(b[4], 52); - VERIFY_CHECK(r != b); - VERIFY_CHECK(a != b); - - /* [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n. - * for 0 <= x <= 4, px is a shorthand for sum(a[i]*b[x-i], i=0..x). - * for 4 <= x <= 8, px is a shorthand for sum(a[i]*b[x-i], i=(x-4)..4) - * Note that [x 0 0 0 0 0] = [x*R]. - */ - - d = (uint128_t)a0 * b[3] - + (uint128_t)a1 * b[2] - + (uint128_t)a2 * b[1] - + (uint128_t)a3 * b[0]; - VERIFY_BITS(d, 114); - /* [d 0 0 0] = [p3 0 0 0] */ - c = (uint128_t)a4 * b[4]; - VERIFY_BITS(c, 112); - /* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ - d += (c & M) * R; c >>= 52; - VERIFY_BITS(d, 115); - VERIFY_BITS(c, 60); - /* [c 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ - t3 = d & M; d >>= 52; - VERIFY_BITS(t3, 52); - VERIFY_BITS(d, 63); - /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ - - d += (uint128_t)a0 * b[4] - + (uint128_t)a1 * b[3] - + (uint128_t)a2 * b[2] - + (uint128_t)a3 * b[1] - + (uint128_t)a4 * b[0]; - VERIFY_BITS(d, 115); - /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - d += c * R; - VERIFY_BITS(d, 116); - /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - t4 = d & M; d >>= 52; - VERIFY_BITS(t4, 52); - VERIFY_BITS(d, 64); - /* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - tx = (t4 >> 48); t4 &= (M >> 4); - VERIFY_BITS(tx, 4); - VERIFY_BITS(t4, 48); - /* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - - c = (uint128_t)a0 * b[0]; - VERIFY_BITS(c, 112); - /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */ - d += (uint128_t)a1 * b[4] - + (uint128_t)a2 * b[3] - + (uint128_t)a3 * b[2] - + (uint128_t)a4 * b[1]; - VERIFY_BITS(d, 115); - /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - u0 = d & M; d >>= 52; - VERIFY_BITS(u0, 52); - VERIFY_BITS(d, 63); - /* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - /* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - u0 = (u0 << 4) | tx; - VERIFY_BITS(u0, 56); - /* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - c += (uint128_t)u0 * (R >> 4); - VERIFY_BITS(c, 115); - /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - r[0] = c & M; c >>= 52; - VERIFY_BITS(r[0], 52); - VERIFY_BITS(c, 61); - /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */ - - c += (uint128_t)a0 * b[1] - + (uint128_t)a1 * b[0]; - VERIFY_BITS(c, 114); - /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */ - d += (uint128_t)a2 * b[4] - + (uint128_t)a3 * b[3] - + (uint128_t)a4 * b[2]; - VERIFY_BITS(d, 114); - /* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - c += (d & M) * R; d >>= 52; - VERIFY_BITS(c, 115); - VERIFY_BITS(d, 62); - /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - r[1] = c & M; c >>= 52; - VERIFY_BITS(r[1], 52); - VERIFY_BITS(c, 63); - /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - - c += (uint128_t)a0 * b[2] - + (uint128_t)a1 * b[1] - + (uint128_t)a2 * b[0]; - VERIFY_BITS(c, 114); - /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */ - d += (uint128_t)a3 * b[4] - + (uint128_t)a4 * b[3]; - VERIFY_BITS(d, 114); - /* [d 0 0 t4 t3 c t1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += (d & M) * R; d >>= 52; - VERIFY_BITS(c, 115); - VERIFY_BITS(d, 62); - /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - - /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[2] = c & M; c >>= 52; - VERIFY_BITS(r[2], 52); - VERIFY_BITS(c, 63); - /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += d * R + t3; - VERIFY_BITS(c, 100); - /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[3] = c & M; c >>= 52; - VERIFY_BITS(r[3], 52); - VERIFY_BITS(c, 48); - /* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += t4; - VERIFY_BITS(c, 49); - /* [c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[4] = c; - VERIFY_BITS(r[4], 49); - /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ -} - -SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t *a) { - uint128_t c, d; - uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4]; - int64_t t3, t4, tx, u0; - const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL; - - VERIFY_BITS(a[0], 56); - VERIFY_BITS(a[1], 56); - VERIFY_BITS(a[2], 56); - VERIFY_BITS(a[3], 56); - VERIFY_BITS(a[4], 52); - - /** [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n. - * px is a shorthand for sum(a[i]*a[x-i], i=0..x). - * Note that [x 0 0 0 0 0] = [x*R]. - */ - - d = (uint128_t)(a0*2) * a3 - + (uint128_t)(a1*2) * a2; - VERIFY_BITS(d, 114); - /* [d 0 0 0] = [p3 0 0 0] */ - c = (uint128_t)a4 * a4; - VERIFY_BITS(c, 112); - /* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ - d += (c & M) * R; c >>= 52; - VERIFY_BITS(d, 115); - VERIFY_BITS(c, 60); - /* [c 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ - t3 = d & M; d >>= 52; - VERIFY_BITS(t3, 52); - VERIFY_BITS(d, 63); - /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ - - a4 *= 2; - d += (uint128_t)a0 * a4 - + (uint128_t)(a1*2) * a3 - + (uint128_t)a2 * a2; - VERIFY_BITS(d, 115); - /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - d += c * R; - VERIFY_BITS(d, 116); - /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - t4 = d & M; d >>= 52; - VERIFY_BITS(t4, 52); - VERIFY_BITS(d, 64); - /* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - tx = (t4 >> 48); t4 &= (M >> 4); - VERIFY_BITS(tx, 4); - VERIFY_BITS(t4, 48); - /* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - - c = (uint128_t)a0 * a0; - VERIFY_BITS(c, 112); - /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */ - d += (uint128_t)a1 * a4 - + (uint128_t)(a2*2) * a3; - VERIFY_BITS(d, 114); - /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - u0 = d & M; d >>= 52; - VERIFY_BITS(u0, 52); - VERIFY_BITS(d, 62); - /* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - /* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - u0 = (u0 << 4) | tx; - VERIFY_BITS(u0, 56); - /* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - c += (uint128_t)u0 * (R >> 4); - VERIFY_BITS(c, 113); - /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - r[0] = c & M; c >>= 52; - VERIFY_BITS(r[0], 52); - VERIFY_BITS(c, 61); - /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */ - - a0 *= 2; - c += (uint128_t)a0 * a1; - VERIFY_BITS(c, 114); - /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */ - d += (uint128_t)a2 * a4 - + (uint128_t)a3 * a3; - VERIFY_BITS(d, 114); - /* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - c += (d & M) * R; d >>= 52; - VERIFY_BITS(c, 115); - VERIFY_BITS(d, 62); - /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - r[1] = c & M; c >>= 52; - VERIFY_BITS(r[1], 52); - VERIFY_BITS(c, 63); - /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - - c += (uint128_t)a0 * a2 - + (uint128_t)a1 * a1; - VERIFY_BITS(c, 114); - /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */ - d += (uint128_t)a3 * a4; - VERIFY_BITS(d, 114); - /* [d 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += (d & M) * R; d >>= 52; - VERIFY_BITS(c, 115); - VERIFY_BITS(d, 62); - /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[2] = c & M; c >>= 52; - VERIFY_BITS(r[2], 52); - VERIFY_BITS(c, 63); - /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - - c += d * R + t3; - VERIFY_BITS(c, 100); - /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[3] = c & M; c >>= 52; - VERIFY_BITS(r[3], 52); - VERIFY_BITS(c, 48); - /* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += t4; - VERIFY_BITS(c, 49); - /* [c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[4] = c; - VERIFY_BITS(r[4], 49); - /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ -} - -#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */ diff --git a/libwallet/musig/field_impl.h b/libwallet/musig/field_impl.h deleted file mode 100644 index eb8b8e20..00000000 --- a/libwallet/musig/field_impl.h +++ /dev/null @@ -1,145 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_FIELD_IMPL_H -#define SECP256K1_FIELD_IMPL_H - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - -#include "util.h" - -#if defined(SECP256K1_WIDEMUL_INT128) -#include "field_5x52_impl.h" -#elif defined(SECP256K1_WIDEMUL_INT64) -#include "field_10x26_impl.h" -#else -#error "Please select wide multiplication implementation" -#endif - -SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { - secp256k1_fe na; - secp256k1_fe_negate(&na, a, 1); - secp256k1_fe_add(&na, b); - return secp256k1_fe_normalizes_to_zero(&na); -} - -SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b) { - secp256k1_fe na; - secp256k1_fe_negate(&na, a, 1); - secp256k1_fe_add(&na, b); - return secp256k1_fe_normalizes_to_zero_var(&na); -} - -static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { - /** Given that p is congruent to 3 mod 4, we can compute the square root of - * a mod p as the (p+1)/4'th power of a. - * - * As (p+1)/4 is an even number, it will have the same result for a and for - * (-a). Only one of these two numbers actually has a square root however, - * so we test at the end by squaring and comparing to the input. - * Also because (p+1)/4 is an even number, the computed square root is - * itself always a square (a ** ((p+1)/4) is the square of a ** ((p+1)/8)). - */ - secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; - int j; - - VERIFY_CHECK(r != a); - - /** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in - * { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block: - * 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] - */ - - secp256k1_fe_sqr(&x2, a); - secp256k1_fe_mul(&x2, &x2, a); - - secp256k1_fe_sqr(&x3, &x2); - secp256k1_fe_mul(&x3, &x3, a); - - x6 = x3; - for (j=0; j<3; j++) { - secp256k1_fe_sqr(&x6, &x6); - } - secp256k1_fe_mul(&x6, &x6, &x3); - - x9 = x6; - for (j=0; j<3; j++) { - secp256k1_fe_sqr(&x9, &x9); - } - secp256k1_fe_mul(&x9, &x9, &x3); - - x11 = x9; - for (j=0; j<2; j++) { - secp256k1_fe_sqr(&x11, &x11); - } - secp256k1_fe_mul(&x11, &x11, &x2); - - x22 = x11; - for (j=0; j<11; j++) { - secp256k1_fe_sqr(&x22, &x22); - } - secp256k1_fe_mul(&x22, &x22, &x11); - - x44 = x22; - for (j=0; j<22; j++) { - secp256k1_fe_sqr(&x44, &x44); - } - secp256k1_fe_mul(&x44, &x44, &x22); - - x88 = x44; - for (j=0; j<44; j++) { - secp256k1_fe_sqr(&x88, &x88); - } - secp256k1_fe_mul(&x88, &x88, &x44); - - x176 = x88; - for (j=0; j<88; j++) { - secp256k1_fe_sqr(&x176, &x176); - } - secp256k1_fe_mul(&x176, &x176, &x88); - - x220 = x176; - for (j=0; j<44; j++) { - secp256k1_fe_sqr(&x220, &x220); - } - secp256k1_fe_mul(&x220, &x220, &x44); - - x223 = x220; - for (j=0; j<3; j++) { - secp256k1_fe_sqr(&x223, &x223); - } - secp256k1_fe_mul(&x223, &x223, &x3); - - /* The final result is then assembled using a sliding window over the blocks. */ - - t1 = x223; - for (j=0; j<23; j++) { - secp256k1_fe_sqr(&t1, &t1); - } - secp256k1_fe_mul(&t1, &t1, &x22); - for (j=0; j<6; j++) { - secp256k1_fe_sqr(&t1, &t1); - } - secp256k1_fe_mul(&t1, &t1, &x2); - secp256k1_fe_sqr(&t1, &t1); - secp256k1_fe_sqr(r, &t1); - - /* Check that a square root was actually calculated */ - - secp256k1_fe_sqr(&t1, r); - return secp256k1_fe_equal(&t1, a); -} - -static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) { - secp256k1_fe r; - return secp256k1_fe_sqrt(&r, a); -} - -static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); - -#endif /* SECP256K1_FIELD_IMPL_H */ diff --git a/libwallet/musig/group.h b/libwallet/musig/group.h deleted file mode 100644 index 2442e0cb..00000000 --- a/libwallet/musig/group.h +++ /dev/null @@ -1,153 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_GROUP_H -#define SECP256K1_GROUP_H - -#include "field.h" - -/** A group element of the secp256k1 curve, in affine coordinates. */ -typedef struct { - secp256k1_fe x; - secp256k1_fe y; - int infinity; /* whether this represents the point at infinity */ -} secp256k1_ge; - -#define SECP256K1_GE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), 0} -#define SECP256K1_GE_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} - -/** A group element of the secp256k1 curve, in jacobian coordinates. */ -typedef struct { - secp256k1_fe x; /* actual X: x/z^2 */ - secp256k1_fe y; /* actual Y: y/z^3 */ - secp256k1_fe z; - int infinity; /* whether this represents the point at infinity */ -} secp256k1_gej; - -#define SECP256K1_GEJ_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), 0} -#define SECP256K1_GEJ_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} - -typedef struct { - secp256k1_fe_storage x; - secp256k1_fe_storage y; -} secp256k1_ge_storage; - -#define SECP256K1_GE_STORAGE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_STORAGE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_STORAGE_CONST((i),(j),(k),(l),(m),(n),(o),(p))} - -#define SECP256K1_GE_STORAGE_CONST_GET(t) SECP256K1_FE_STORAGE_CONST_GET(t.x), SECP256K1_FE_STORAGE_CONST_GET(t.y) - -/** Set a group element equal to the point with given X and Y coordinates */ -static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y); - -/** Set a group element (affine) equal to the point with the given X coordinate - * and a Y coordinate that is a quadratic residue modulo p. The return value - * is true iff a coordinate with the given X coordinate exists. - */ -static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x); - -/** Set a group element (affine) equal to the point with the given X coordinate, and given oddness - * for Y. Return value indicates whether the result is valid. */ -static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd); - -/** Check whether a group element is the point at infinity. */ -static int secp256k1_ge_is_infinity(const secp256k1_ge *a); - -/** Check whether a group element is valid (i.e., on the curve). */ -static int secp256k1_ge_is_valid_var(const secp256k1_ge *a); - -/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */ -static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a); - -/** Set a group element equal to another which is given in jacobian coordinates. Constant time. */ -static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a); - -/** Set a group element equal to another which is given in jacobian coordinates. */ -static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a); - -/** Set a batch of group elements equal to the inputs given in jacobian coordinates */ -static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len); - -/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to - * the same global z "denominator". zr must contain the known z-ratios such - * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. The x and y - * coordinates of the result are stored in r, the common z coordinate is - * stored in globalz. */ -static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr); - -/** Set a group element (affine) equal to the point at infinity. */ -static void secp256k1_ge_set_infinity(secp256k1_ge *r); - -/** Set a group element (jacobian) equal to the point at infinity. */ -static void secp256k1_gej_set_infinity(secp256k1_gej *r); - -/** Set a group element (jacobian) equal to another which is given in affine coordinates. */ -static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a); - -/** Compare the X coordinate of a group element (jacobian). */ -static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a); - -/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */ -static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a); - -/** Check whether a group element is the point at infinity. */ -static int secp256k1_gej_is_infinity(const secp256k1_gej *a); - -/** Check whether a group element's y coordinate is a quadratic residue. */ -static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a); - -/** Set r equal to the double of a. Constant time. */ -static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a); - -/** Set r equal to the double of a. If rzr is not-NULL this sets *rzr such that r->z == a->z * *rzr (where infinity means an implicit z = 0). */ -static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); - -/** Set r equal to the sum of a and b. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */ -static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr); - -/** Set r equal to the sum of a and b (with b given in affine coordinates, and not infinity). */ -static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b); - -/** Set r equal to the sum of a and b (with b given in affine coordinates). This is more efficient - than secp256k1_gej_add_var. It is identical to secp256k1_gej_add_ge but without constant-time - guarantee, and b is allowed to be infinity. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */ -static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr); - -/** Set r equal to the sum of a and b (with the inverse of b's Z coordinate passed as bzinv). */ -static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv); - -/** Set r to be equal to lambda times a, where lambda is chosen in a way such that this is very fast. */ -static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a); - -/** Clear a secp256k1_gej to prevent leaking sensitive information. */ -static void secp256k1_gej_clear(secp256k1_gej *r); - -/** Clear a secp256k1_ge to prevent leaking sensitive information. */ -static void secp256k1_ge_clear(secp256k1_ge *r); - -/** Convert a group element to the storage type. */ -static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a); - -/** Convert a group element back from the storage type. */ -static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a); - -/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ -static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag); - -/** Rescale a jacobian point by b which must be non-zero. Constant-time. */ -static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); - -/** Determine if a point (which is assumed to be on the curve) is in the correct (sub)group of the curve. - * - * In normal mode, the used group is secp256k1, which has cofactor=1 meaning that every point on the curve is in the - * group, and this function returns always true. - * - * When compiling in exhaustive test mode, a slightly different curve equation is used, leading to a group with a - * (very) small subgroup, and that subgroup is what is used for all cryptographic operations. In that mode, this - * function checks whether a point that is on the curve is in fact also in that subgroup. - */ -static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge); - -#endif /* SECP256K1_GROUP_H */ diff --git a/libwallet/musig/group_impl.h b/libwallet/musig/group_impl.h deleted file mode 100644 index aa7a0fba..00000000 --- a/libwallet/musig/group_impl.h +++ /dev/null @@ -1,692 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_GROUP_IMPL_H -#define SECP256K1_GROUP_IMPL_H - -#include "field.h" -#include "group.h" - -/* These exhaustive group test orders and generators are chosen such that: - * - The field size is equal to that of secp256k1, so field code is the same. - * - The curve equation is of the form y^2=x^3+B for some constant B. - * - The subgroup has a generator 2*P, where P.x=1. - * - The subgroup has size less than 1000 to permit exhaustive testing. - * - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y). - * - * These parameters are generated using sage/gen_exhaustive_groups.sage. - */ -#if defined(EXHAUSTIVE_TEST_ORDER) -# if EXHAUSTIVE_TEST_ORDER == 13 -static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( - 0xc3459c3d, 0x35326167, 0xcd86cce8, 0x07a2417f, - 0x5b8bd567, 0xde8538ee, 0x0d507b0c, 0xd128f5bb, - 0x8e467fec, 0xcd30000a, 0x6cc1184e, 0x25d382c2, - 0xa2f4494e, 0x2fbe9abc, 0x8b64abac, 0xd005fb24 -); -static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST( - 0x3d3486b2, 0x159a9ca5, 0xc75638be, 0xb23a69bc, - 0x946a45ab, 0x24801247, 0xb4ed2b8e, 0x26b6a417 -); -# elif EXHAUSTIVE_TEST_ORDER == 199 -static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( - 0x226e653f, 0xc8df7744, 0x9bacbf12, 0x7d1dcbf9, - 0x87f05b2a, 0xe7edbd28, 0x1f564575, 0xc48dcf18, - 0xa13872c2, 0xe933bb17, 0x5d9ffd5b, 0xb5b6e10c, - 0x57fe3c00, 0xbaaaa15a, 0xe003ec3e, 0x9c269bae -); -static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST( - 0x2cca28fa, 0xfc614b80, 0x2a3db42b, 0x00ba00b1, - 0xbea8d943, 0xdace9ab2, 0x9536daea, 0x0074defb -); -# else -# error No known generator for the specified exhaustive test group order. -# endif -#else -/** Generator for secp256k1, value 'g' defined in - * "Standards for Efficient Cryptography" (SEC2) 2.7.1. - */ -static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( - 0x79BE667EUL, 0xF9DCBBACUL, 0x55A06295UL, 0xCE870B07UL, - 0x029BFCDBUL, 0x2DCE28D9UL, 0x59F2815BUL, 0x16F81798UL, - 0x483ADA77UL, 0x26A3C465UL, 0x5DA4FBFCUL, 0x0E1108A8UL, - 0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL -); - -static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 7); -#endif - -static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) { - secp256k1_fe zi2; - secp256k1_fe zi3; - secp256k1_fe_sqr(&zi2, zi); - secp256k1_fe_mul(&zi3, &zi2, zi); - secp256k1_fe_mul(&r->x, &a->x, &zi2); - secp256k1_fe_mul(&r->y, &a->y, &zi3); - r->infinity = a->infinity; -} - -static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y) { - r->infinity = 0; - r->x = *x; - r->y = *y; -} - -static int secp256k1_ge_is_infinity(const secp256k1_ge *a) { - return a->infinity; -} - -static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a) { - *r = *a; - secp256k1_fe_normalize_weak(&r->y); - secp256k1_fe_negate(&r->y, &r->y, 1); -} - -static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a) { - secp256k1_fe z2, z3; - r->infinity = a->infinity; - secp256k1_fe_inv(&a->z, &a->z); - secp256k1_fe_sqr(&z2, &a->z); - secp256k1_fe_mul(&z3, &a->z, &z2); - secp256k1_fe_mul(&a->x, &a->x, &z2); - secp256k1_fe_mul(&a->y, &a->y, &z3); - secp256k1_fe_set_int(&a->z, 1); - r->x = a->x; - r->y = a->y; -} - -static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { - secp256k1_fe z2, z3; - if (a->infinity) { - secp256k1_ge_set_infinity(r); - return; - } - secp256k1_fe_inv_var(&a->z, &a->z); - secp256k1_fe_sqr(&z2, &a->z); - secp256k1_fe_mul(&z3, &a->z, &z2); - secp256k1_fe_mul(&a->x, &a->x, &z2); - secp256k1_fe_mul(&a->y, &a->y, &z3); - secp256k1_fe_set_int(&a->z, 1); - secp256k1_ge_set_xy(r, &a->x, &a->y); -} - -static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len) { - secp256k1_fe u; - size_t i; - size_t last_i = SIZE_MAX; - - for (i = 0; i < len; i++) { - if (a[i].infinity) { - secp256k1_ge_set_infinity(&r[i]); - } else { - /* Use destination's x coordinates as scratch space */ - if (last_i == SIZE_MAX) { - r[i].x = a[i].z; - } else { - secp256k1_fe_mul(&r[i].x, &r[last_i].x, &a[i].z); - } - last_i = i; - } - } - if (last_i == SIZE_MAX) { - return; - } - secp256k1_fe_inv_var(&u, &r[last_i].x); - - i = last_i; - while (i > 0) { - i--; - if (!a[i].infinity) { - secp256k1_fe_mul(&r[last_i].x, &r[i].x, &u); - secp256k1_fe_mul(&u, &u, &a[last_i].z); - last_i = i; - } - } - VERIFY_CHECK(!a[last_i].infinity); - r[last_i].x = u; - - for (i = 0; i < len; i++) { - if (!a[i].infinity) { - secp256k1_ge_set_gej_zinv(&r[i], &a[i], &r[i].x); - } - } -} - -static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr) { - size_t i = len - 1; - secp256k1_fe zs; - - if (len > 0) { - /* The z of the final point gives us the "global Z" for the table. */ - r[i].x = a[i].x; - r[i].y = a[i].y; - /* Ensure all y values are in weak normal form for fast negation of points */ - secp256k1_fe_normalize_weak(&r[i].y); - *globalz = a[i].z; - r[i].infinity = 0; - zs = zr[i]; - - /* Work our way backwards, using the z-ratios to scale the x/y values. */ - while (i > 0) { - if (i != len - 1) { - secp256k1_fe_mul(&zs, &zs, &zr[i]); - } - i--; - secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zs); - } - } -} - -static void secp256k1_gej_set_infinity(secp256k1_gej *r) { - r->infinity = 1; - secp256k1_fe_clear(&r->x); - secp256k1_fe_clear(&r->y); - secp256k1_fe_clear(&r->z); -} - -static void secp256k1_ge_set_infinity(secp256k1_ge *r) { - r->infinity = 1; - secp256k1_fe_clear(&r->x); - secp256k1_fe_clear(&r->y); -} - -static void secp256k1_gej_clear(secp256k1_gej *r) { - r->infinity = 0; - secp256k1_fe_clear(&r->x); - secp256k1_fe_clear(&r->y); - secp256k1_fe_clear(&r->z); -} - -static void secp256k1_ge_clear(secp256k1_ge *r) { - r->infinity = 0; - secp256k1_fe_clear(&r->x); - secp256k1_fe_clear(&r->y); -} - -static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) { - secp256k1_fe x2, x3; - r->x = *x; - secp256k1_fe_sqr(&x2, x); - secp256k1_fe_mul(&x3, x, &x2); - r->infinity = 0; - secp256k1_fe_add(&x3, &secp256k1_fe_const_b); - return secp256k1_fe_sqrt(&r->y, &x3); -} - -static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { - if (!secp256k1_ge_set_xquad(r, x)) { - return 0; - } - secp256k1_fe_normalize_var(&r->y); - if (secp256k1_fe_is_odd(&r->y) != odd) { - secp256k1_fe_negate(&r->y, &r->y, 1); - } - return 1; - -} - -static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a) { - r->infinity = a->infinity; - r->x = a->x; - r->y = a->y; - secp256k1_fe_set_int(&r->z, 1); -} - -static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a) { - secp256k1_fe r, r2; - VERIFY_CHECK(!a->infinity); - secp256k1_fe_sqr(&r, &a->z); secp256k1_fe_mul(&r, &r, x); - r2 = a->x; secp256k1_fe_normalize_weak(&r2); - return secp256k1_fe_equal_var(&r, &r2); -} - -static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a) { - r->infinity = a->infinity; - r->x = a->x; - r->y = a->y; - r->z = a->z; - secp256k1_fe_normalize_weak(&r->y); - secp256k1_fe_negate(&r->y, &r->y, 1); -} - -static int secp256k1_gej_is_infinity(const secp256k1_gej *a) { - return a->infinity; -} - -static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { - secp256k1_fe y2, x3; - if (a->infinity) { - return 0; - } - /* y^2 = x^3 + 7 */ - secp256k1_fe_sqr(&y2, &a->y); - secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); - secp256k1_fe_add(&x3, &secp256k1_fe_const_b); - secp256k1_fe_normalize_weak(&x3); - return secp256k1_fe_equal_var(&y2, &x3); -} - -static SECP256K1_INLINE void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a) { - /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate. - * - * Note that there is an implementation described at - * https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - * which trades a multiply for a square, but in practice this is actually slower, - * mainly because it requires more normalizations. - */ - secp256k1_fe t1,t2,t3,t4; - - r->infinity = a->infinity; - - secp256k1_fe_mul(&r->z, &a->z, &a->y); - secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */ - secp256k1_fe_sqr(&t1, &a->x); - secp256k1_fe_mul_int(&t1, 3); /* T1 = 3*X^2 (3) */ - secp256k1_fe_sqr(&t2, &t1); /* T2 = 9*X^4 (1) */ - secp256k1_fe_sqr(&t3, &a->y); - secp256k1_fe_mul_int(&t3, 2); /* T3 = 2*Y^2 (2) */ - secp256k1_fe_sqr(&t4, &t3); - secp256k1_fe_mul_int(&t4, 2); /* T4 = 8*Y^4 (2) */ - secp256k1_fe_mul(&t3, &t3, &a->x); /* T3 = 2*X*Y^2 (1) */ - r->x = t3; - secp256k1_fe_mul_int(&r->x, 4); /* X' = 8*X*Y^2 (4) */ - secp256k1_fe_negate(&r->x, &r->x, 4); /* X' = -8*X*Y^2 (5) */ - secp256k1_fe_add(&r->x, &t2); /* X' = 9*X^4 - 8*X*Y^2 (6) */ - secp256k1_fe_negate(&t2, &t2, 1); /* T2 = -9*X^4 (2) */ - secp256k1_fe_mul_int(&t3, 6); /* T3 = 12*X*Y^2 (6) */ - secp256k1_fe_add(&t3, &t2); /* T3 = 12*X*Y^2 - 9*X^4 (8) */ - secp256k1_fe_mul(&r->y, &t1, &t3); /* Y' = 36*X^3*Y^2 - 27*X^6 (1) */ - secp256k1_fe_negate(&t2, &t4, 2); /* T2 = -8*Y^4 (3) */ - secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */ -} - -static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { - /** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity, - * Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have - * y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p. - * - * Having said this, if this function receives a point on a sextic twist, e.g. by - * a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6, - * since -6 does have a cube root mod p. For this point, this function will not set - * the infinity flag even though the point doubles to infinity, and the result - * point will be gibberish (z = 0 but infinity = 0). - */ - if (a->infinity) { - secp256k1_gej_set_infinity(r); - if (rzr != NULL) { - secp256k1_fe_set_int(rzr, 1); - } - return; - } - - if (rzr != NULL) { - *rzr = a->y; - secp256k1_fe_normalize_weak(rzr); - secp256k1_fe_mul_int(rzr, 2); - } - - secp256k1_gej_double(r, a); -} - -static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) { - /* Operations: 12 mul, 4 sqr, 2 normalize, 12 mul_int/add/negate */ - secp256k1_fe z22, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; - - if (a->infinity) { - VERIFY_CHECK(rzr == NULL); - *r = *b; - return; - } - - if (b->infinity) { - if (rzr != NULL) { - secp256k1_fe_set_int(rzr, 1); - } - *r = *a; - return; - } - - r->infinity = 0; - secp256k1_fe_sqr(&z22, &b->z); - secp256k1_fe_sqr(&z12, &a->z); - secp256k1_fe_mul(&u1, &a->x, &z22); - secp256k1_fe_mul(&u2, &b->x, &z12); - secp256k1_fe_mul(&s1, &a->y, &z22); secp256k1_fe_mul(&s1, &s1, &b->z); - secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &a->z); - secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); - secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); - if (secp256k1_fe_normalizes_to_zero_var(&h)) { - if (secp256k1_fe_normalizes_to_zero_var(&i)) { - secp256k1_gej_double_var(r, a, rzr); - } else { - if (rzr != NULL) { - secp256k1_fe_set_int(rzr, 0); - } - secp256k1_gej_set_infinity(r); - } - return; - } - secp256k1_fe_sqr(&i2, &i); - secp256k1_fe_sqr(&h2, &h); - secp256k1_fe_mul(&h3, &h, &h2); - secp256k1_fe_mul(&h, &h, &b->z); - if (rzr != NULL) { - *rzr = h; - } - secp256k1_fe_mul(&r->z, &a->z, &h); - secp256k1_fe_mul(&t, &u1, &h2); - r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); - secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); - secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); - secp256k1_fe_add(&r->y, &h3); -} - -static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr) { - /* 8 mul, 3 sqr, 4 normalize, 12 mul_int/add/negate */ - secp256k1_fe z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; - if (a->infinity) { - VERIFY_CHECK(rzr == NULL); - secp256k1_gej_set_ge(r, b); - return; - } - if (b->infinity) { - if (rzr != NULL) { - secp256k1_fe_set_int(rzr, 1); - } - *r = *a; - return; - } - r->infinity = 0; - - secp256k1_fe_sqr(&z12, &a->z); - u1 = a->x; secp256k1_fe_normalize_weak(&u1); - secp256k1_fe_mul(&u2, &b->x, &z12); - s1 = a->y; secp256k1_fe_normalize_weak(&s1); - secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &a->z); - secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); - secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); - if (secp256k1_fe_normalizes_to_zero_var(&h)) { - if (secp256k1_fe_normalizes_to_zero_var(&i)) { - secp256k1_gej_double_var(r, a, rzr); - } else { - if (rzr != NULL) { - secp256k1_fe_set_int(rzr, 0); - } - secp256k1_gej_set_infinity(r); - } - return; - } - secp256k1_fe_sqr(&i2, &i); - secp256k1_fe_sqr(&h2, &h); - secp256k1_fe_mul(&h3, &h, &h2); - if (rzr != NULL) { - *rzr = h; - } - secp256k1_fe_mul(&r->z, &a->z, &h); - secp256k1_fe_mul(&t, &u1, &h2); - r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); - secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); - secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); - secp256k1_fe_add(&r->y, &h3); -} - -static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv) { - /* 9 mul, 3 sqr, 4 normalize, 12 mul_int/add/negate */ - secp256k1_fe az, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; - - if (b->infinity) { - *r = *a; - return; - } - if (a->infinity) { - secp256k1_fe bzinv2, bzinv3; - r->infinity = b->infinity; - secp256k1_fe_sqr(&bzinv2, bzinv); - secp256k1_fe_mul(&bzinv3, &bzinv2, bzinv); - secp256k1_fe_mul(&r->x, &b->x, &bzinv2); - secp256k1_fe_mul(&r->y, &b->y, &bzinv3); - secp256k1_fe_set_int(&r->z, 1); - return; - } - r->infinity = 0; - - /** We need to calculate (rx,ry,rz) = (ax,ay,az) + (bx,by,1/bzinv). Due to - * secp256k1's isomorphism we can multiply the Z coordinates on both sides - * by bzinv, and get: (rx,ry,rz*bzinv) = (ax,ay,az*bzinv) + (bx,by,1). - * This means that (rx,ry,rz) can be calculated as - * (ax,ay,az*bzinv) + (bx,by,1), when not applying the bzinv factor to rz. - * The variable az below holds the modified Z coordinate for a, which is used - * for the computation of rx and ry, but not for rz. - */ - secp256k1_fe_mul(&az, &a->z, bzinv); - - secp256k1_fe_sqr(&z12, &az); - u1 = a->x; secp256k1_fe_normalize_weak(&u1); - secp256k1_fe_mul(&u2, &b->x, &z12); - s1 = a->y; secp256k1_fe_normalize_weak(&s1); - secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &az); - secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); - secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); - if (secp256k1_fe_normalizes_to_zero_var(&h)) { - if (secp256k1_fe_normalizes_to_zero_var(&i)) { - secp256k1_gej_double_var(r, a, NULL); - } else { - secp256k1_gej_set_infinity(r); - } - return; - } - secp256k1_fe_sqr(&i2, &i); - secp256k1_fe_sqr(&h2, &h); - secp256k1_fe_mul(&h3, &h, &h2); - r->z = a->z; secp256k1_fe_mul(&r->z, &r->z, &h); - secp256k1_fe_mul(&t, &u1, &h2); - r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); - secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); - secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); - secp256k1_fe_add(&r->y, &h3); -} - - -static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b) { - /* Operations: 7 mul, 5 sqr, 4 normalize, 21 mul_int/add/negate/cmov */ - static const secp256k1_fe fe_1 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); - secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr; - secp256k1_fe m_alt, rr_alt; - int infinity, degenerate; - VERIFY_CHECK(!b->infinity); - VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); - - /** In: - * Eric Brier and Marc Joye, Weierstrass Elliptic Curves and Side-Channel Attacks. - * In D. Naccache and P. Paillier, Eds., Public Key Cryptography, vol. 2274 of Lecture Notes in Computer Science, pages 335-345. Springer-Verlag, 2002. - * we find as solution for a unified addition/doubling formula: - * lambda = ((x1 + x2)^2 - x1 * x2 + a) / (y1 + y2), with a = 0 for secp256k1's curve equation. - * x3 = lambda^2 - (x1 + x2) - * 2*y3 = lambda * (x1 + x2 - 2 * x3) - (y1 + y2). - * - * Substituting x_i = Xi / Zi^2 and yi = Yi / Zi^3, for i=1,2,3, gives: - * U1 = X1*Z2^2, U2 = X2*Z1^2 - * S1 = Y1*Z2^3, S2 = Y2*Z1^3 - * Z = Z1*Z2 - * T = U1+U2 - * M = S1+S2 - * Q = T*M^2 - * R = T^2-U1*U2 - * X3 = 4*(R^2-Q) - * Y3 = 4*(R*(3*Q-2*R^2)-M^4) - * Z3 = 2*M*Z - * (Note that the paper uses xi = Xi / Zi and yi = Yi / Zi instead.) - * - * This formula has the benefit of being the same for both addition - * of distinct points and doubling. However, it breaks down in the - * case that either point is infinity, or that y1 = -y2. We handle - * these cases in the following ways: - * - * - If b is infinity we simply bail by means of a VERIFY_CHECK. - * - * - If a is infinity, we detect this, and at the end of the - * computation replace the result (which will be meaningless, - * but we compute to be constant-time) with b.x : b.y : 1. - * - * - If a = -b, we have y1 = -y2, which is a degenerate case. - * But here the answer is infinity, so we simply set the - * infinity flag of the result, overriding the computed values - * without even needing to cmov. - * - * - If y1 = -y2 but x1 != x2, which does occur thanks to certain - * properties of our curve (specifically, 1 has nontrivial cube - * roots in our field, and the curve equation has no x coefficient) - * then the answer is not infinity but also not given by the above - * equation. In this case, we cmov in place an alternate expression - * for lambda. Specifically (y1 - y2)/(x1 - x2). Where both these - * expressions for lambda are defined, they are equal, and can be - * obtained from each other by multiplication by (y1 + y2)/(y1 + y2) - * then substitution of x^3 + 7 for y^2 (using the curve equation). - * For all pairs of nonzero points (a, b) at least one is defined, - * so this covers everything. - */ - - secp256k1_fe_sqr(&zz, &a->z); /* z = Z1^2 */ - u1 = a->x; secp256k1_fe_normalize_weak(&u1); /* u1 = U1 = X1*Z2^2 (1) */ - secp256k1_fe_mul(&u2, &b->x, &zz); /* u2 = U2 = X2*Z1^2 (1) */ - s1 = a->y; secp256k1_fe_normalize_weak(&s1); /* s1 = S1 = Y1*Z2^3 (1) */ - secp256k1_fe_mul(&s2, &b->y, &zz); /* s2 = Y2*Z1^2 (1) */ - secp256k1_fe_mul(&s2, &s2, &a->z); /* s2 = S2 = Y2*Z1^3 (1) */ - t = u1; secp256k1_fe_add(&t, &u2); /* t = T = U1+U2 (2) */ - m = s1; secp256k1_fe_add(&m, &s2); /* m = M = S1+S2 (2) */ - secp256k1_fe_sqr(&rr, &t); /* rr = T^2 (1) */ - secp256k1_fe_negate(&m_alt, &u2, 1); /* Malt = -X2*Z1^2 */ - secp256k1_fe_mul(&tt, &u1, &m_alt); /* tt = -U1*U2 (2) */ - secp256k1_fe_add(&rr, &tt); /* rr = R = T^2-U1*U2 (3) */ - /** If lambda = R/M = 0/0 we have a problem (except in the "trivial" - * case that Z = z1z2 = 0, and this is special-cased later on). */ - degenerate = secp256k1_fe_normalizes_to_zero(&m) & - secp256k1_fe_normalizes_to_zero(&rr); - /* This only occurs when y1 == -y2 and x1^3 == x2^3, but x1 != x2. - * This means either x1 == beta*x2 or beta*x1 == x2, where beta is - * a nontrivial cube root of one. In either case, an alternate - * non-indeterminate expression for lambda is (y1 - y2)/(x1 - x2), - * so we set R/M equal to this. */ - rr_alt = s1; - secp256k1_fe_mul_int(&rr_alt, 2); /* rr = Y1*Z2^3 - Y2*Z1^3 (2) */ - secp256k1_fe_add(&m_alt, &u1); /* Malt = X1*Z2^2 - X2*Z1^2 */ - - secp256k1_fe_cmov(&rr_alt, &rr, !degenerate); - secp256k1_fe_cmov(&m_alt, &m, !degenerate); - /* Now Ralt / Malt = lambda and is guaranteed not to be 0/0. - * From here on out Ralt and Malt represent the numerator - * and denominator of lambda; R and M represent the explicit - * expressions x1^2 + x2^2 + x1x2 and y1 + y2. */ - secp256k1_fe_sqr(&n, &m_alt); /* n = Malt^2 (1) */ - secp256k1_fe_mul(&q, &n, &t); /* q = Q = T*Malt^2 (1) */ - /* These two lines use the observation that either M == Malt or M == 0, - * so M^3 * Malt is either Malt^4 (which is computed by squaring), or - * zero (which is "computed" by cmov). So the cost is one squaring - * versus two multiplications. */ - secp256k1_fe_sqr(&n, &n); - secp256k1_fe_cmov(&n, &m, degenerate); /* n = M^3 * Malt (2) */ - secp256k1_fe_sqr(&t, &rr_alt); /* t = Ralt^2 (1) */ - secp256k1_fe_mul(&r->z, &a->z, &m_alt); /* r->z = Malt*Z (1) */ - infinity = secp256k1_fe_normalizes_to_zero(&r->z) & ~a->infinity; - secp256k1_fe_mul_int(&r->z, 2); /* r->z = Z3 = 2*Malt*Z (2) */ - secp256k1_fe_negate(&q, &q, 1); /* q = -Q (2) */ - secp256k1_fe_add(&t, &q); /* t = Ralt^2-Q (3) */ - secp256k1_fe_normalize_weak(&t); - r->x = t; /* r->x = Ralt^2-Q (1) */ - secp256k1_fe_mul_int(&t, 2); /* t = 2*x3 (2) */ - secp256k1_fe_add(&t, &q); /* t = 2*x3 - Q: (4) */ - secp256k1_fe_mul(&t, &t, &rr_alt); /* t = Ralt*(2*x3 - Q) (1) */ - secp256k1_fe_add(&t, &n); /* t = Ralt*(2*x3 - Q) + M^3*Malt (3) */ - secp256k1_fe_negate(&r->y, &t, 3); /* r->y = Ralt*(Q - 2x3) - M^3*Malt (4) */ - secp256k1_fe_normalize_weak(&r->y); - secp256k1_fe_mul_int(&r->x, 4); /* r->x = X3 = 4*(Ralt^2-Q) */ - secp256k1_fe_mul_int(&r->y, 4); /* r->y = Y3 = 4*Ralt*(Q - 2x3) - 4*M^3*Malt (4) */ - - /** In case a->infinity == 1, replace r with (b->x, b->y, 1). */ - secp256k1_fe_cmov(&r->x, &b->x, a->infinity); - secp256k1_fe_cmov(&r->y, &b->y, a->infinity); - secp256k1_fe_cmov(&r->z, &fe_1, a->infinity); - r->infinity = infinity; -} - -static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *s) { - /* Operations: 4 mul, 1 sqr */ - secp256k1_fe zz; - VERIFY_CHECK(!secp256k1_fe_is_zero(s)); - secp256k1_fe_sqr(&zz, s); - secp256k1_fe_mul(&r->x, &r->x, &zz); /* r->x *= s^2 */ - secp256k1_fe_mul(&r->y, &r->y, &zz); - secp256k1_fe_mul(&r->y, &r->y, s); /* r->y *= s^3 */ - secp256k1_fe_mul(&r->z, &r->z, s); /* r->z *= s */ -} - -static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a) { - secp256k1_fe x, y; - VERIFY_CHECK(!a->infinity); - x = a->x; - secp256k1_fe_normalize(&x); - y = a->y; - secp256k1_fe_normalize(&y); - secp256k1_fe_to_storage(&r->x, &x); - secp256k1_fe_to_storage(&r->y, &y); -} - -static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a) { - secp256k1_fe_from_storage(&r->x, &a->x); - secp256k1_fe_from_storage(&r->y, &a->y); - r->infinity = 0; -} - -static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag) { - secp256k1_fe_storage_cmov(&r->x, &a->x, flag); - secp256k1_fe_storage_cmov(&r->y, &a->y, flag); -} - -static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) { - static const secp256k1_fe beta = SECP256K1_FE_CONST( - 0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul, - 0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul - ); - *r = *a; - secp256k1_fe_mul(&r->x, &r->x, &beta); -} - -static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { - secp256k1_fe yz; - - if (a->infinity) { - return 0; - } - - /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as - * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z - is */ - secp256k1_fe_mul(&yz, &a->y, &a->z); - return secp256k1_fe_is_quad_var(&yz); -} - -static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) { -#ifdef EXHAUSTIVE_TEST_ORDER - secp256k1_gej out; - int i; - - /* A very simple EC multiplication ladder that avoids a dependency on ecmult. */ - secp256k1_gej_set_infinity(&out); - for (i = 0; i < 32; ++i) { - secp256k1_gej_double_var(&out, &out, NULL); - if ((((uint32_t)EXHAUSTIVE_TEST_ORDER) >> (31 - i)) & 1) { - secp256k1_gej_add_ge_var(&out, &out, ge, NULL); - } - } - return secp256k1_gej_is_infinity(&out); -#else - (void)ge; - /* The real secp256k1 group has cofactor 1, so the subgroup is the entire curve. */ - return 1; -#endif -} - -#endif /* SECP256K1_GROUP_IMPL_H */ diff --git a/libwallet/musig/hash.h b/libwallet/musig/hash.h deleted file mode 100644 index 0947a096..00000000 --- a/libwallet/musig/hash.h +++ /dev/null @@ -1,41 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_HASH_H -#define SECP256K1_HASH_H - -#include -#include - -typedef struct { - uint32_t s[8]; - uint32_t buf[16]; /* In big endian */ - size_t bytes; -} secp256k1_sha256; - -static void secp256k1_sha256_initialize(secp256k1_sha256 *hash); -static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t size); -static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32); - -typedef struct { - secp256k1_sha256 inner, outer; -} secp256k1_hmac_sha256; - -static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t size); -static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size); -static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32); - -typedef struct { - unsigned char v[32]; - unsigned char k[32]; - int retry; -} secp256k1_rfc6979_hmac_sha256; - -static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen); -static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen); -static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng); - -#endif /* SECP256K1_HASH_H */ diff --git a/libwallet/musig/hash_impl.h b/libwallet/musig/hash_impl.h deleted file mode 100644 index f8cd3a16..00000000 --- a/libwallet/musig/hash_impl.h +++ /dev/null @@ -1,297 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_HASH_IMPL_H -#define SECP256K1_HASH_IMPL_H - -#include "hash.h" -#include "util.h" - -#include -#include -#include - -#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) -#define Maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y)))) -#define Sigma0(x) (((x) >> 2 | (x) << 30) ^ ((x) >> 13 | (x) << 19) ^ ((x) >> 22 | (x) << 10)) -#define Sigma1(x) (((x) >> 6 | (x) << 26) ^ ((x) >> 11 | (x) << 21) ^ ((x) >> 25 | (x) << 7)) -#define sigma0(x) (((x) >> 7 | (x) << 25) ^ ((x) >> 18 | (x) << 14) ^ ((x) >> 3)) -#define sigma1(x) (((x) >> 17 | (x) << 15) ^ ((x) >> 19 | (x) << 13) ^ ((x) >> 10)) - -#define Round(a,b,c,d,e,f,g,h,k,w) do { \ - uint32_t t1 = (h) + Sigma1(e) + Ch((e), (f), (g)) + (k) + (w); \ - uint32_t t2 = Sigma0(a) + Maj((a), (b), (c)); \ - (d) += t1; \ - (h) = t1 + t2; \ -} while(0) - -#if defined(SECP256K1_BIG_ENDIAN) -#define BE32(x) (x) -#elif defined(SECP256K1_LITTLE_ENDIAN) -#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#endif - -static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) { - hash->s[0] = 0x6a09e667ul; - hash->s[1] = 0xbb67ae85ul; - hash->s[2] = 0x3c6ef372ul; - hash->s[3] = 0xa54ff53aul; - hash->s[4] = 0x510e527ful; - hash->s[5] = 0x9b05688cul; - hash->s[6] = 0x1f83d9abul; - hash->s[7] = 0x5be0cd19ul; - hash->bytes = 0; -} - -/** Perform one SHA-256 transformation, processing 16 big endian 32-bit words. */ -static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { - uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; - uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; - - Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = BE32(chunk[0])); - Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = BE32(chunk[1])); - Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = BE32(chunk[2])); - Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = BE32(chunk[3])); - Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = BE32(chunk[4])); - Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = BE32(chunk[5])); - Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = BE32(chunk[6])); - Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = BE32(chunk[7])); - Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = BE32(chunk[8])); - Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = BE32(chunk[9])); - Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = BE32(chunk[10])); - Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = BE32(chunk[11])); - Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = BE32(chunk[12])); - Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = BE32(chunk[13])); - Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = BE32(chunk[14])); - Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = BE32(chunk[15])); - - Round(a, b, c, d, e, f, g, h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1)); - Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2)); - Round(g, h, a, b, c, d, e, f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3)); - Round(f, g, h, a, b, c, d, e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4)); - Round(e, f, g, h, a, b, c, d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5)); - Round(d, e, f, g, h, a, b, c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6)); - Round(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7)); - Round(b, c, d, e, f, g, h, a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8)); - Round(a, b, c, d, e, f, g, h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9)); - Round(h, a, b, c, d, e, f, g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10)); - Round(g, h, a, b, c, d, e, f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11)); - Round(f, g, h, a, b, c, d, e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12)); - Round(e, f, g, h, a, b, c, d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13)); - Round(d, e, f, g, h, a, b, c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14)); - Round(c, d, e, f, g, h, a, b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15)); - Round(b, c, d, e, f, g, h, a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0)); - - Round(a, b, c, d, e, f, g, h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1)); - Round(h, a, b, c, d, e, f, g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2)); - Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3)); - Round(f, g, h, a, b, c, d, e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4)); - Round(e, f, g, h, a, b, c, d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5)); - Round(d, e, f, g, h, a, b, c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6)); - Round(c, d, e, f, g, h, a, b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7)); - Round(b, c, d, e, f, g, h, a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8)); - Round(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9)); - Round(h, a, b, c, d, e, f, g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10)); - Round(g, h, a, b, c, d, e, f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11)); - Round(f, g, h, a, b, c, d, e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12)); - Round(e, f, g, h, a, b, c, d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13)); - Round(d, e, f, g, h, a, b, c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14)); - Round(c, d, e, f, g, h, a, b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15)); - Round(b, c, d, e, f, g, h, a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0)); - - Round(a, b, c, d, e, f, g, h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1)); - Round(h, a, b, c, d, e, f, g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2)); - Round(g, h, a, b, c, d, e, f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3)); - Round(f, g, h, a, b, c, d, e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4)); - Round(e, f, g, h, a, b, c, d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5)); - Round(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6)); - Round(c, d, e, f, g, h, a, b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7)); - Round(b, c, d, e, f, g, h, a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8)); - Round(a, b, c, d, e, f, g, h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9)); - Round(h, a, b, c, d, e, f, g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10)); - Round(g, h, a, b, c, d, e, f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11)); - Round(f, g, h, a, b, c, d, e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12)); - Round(e, f, g, h, a, b, c, d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13)); - Round(d, e, f, g, h, a, b, c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14)); - Round(c, d, e, f, g, h, a, b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15)); - Round(b, c, d, e, f, g, h, a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0)); - - s[0] += a; - s[1] += b; - s[2] += c; - s[3] += d; - s[4] += e; - s[5] += f; - s[6] += g; - s[7] += h; -} - -static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t len) { - size_t bufsize = hash->bytes & 0x3F; - hash->bytes += len; - VERIFY_CHECK(hash->bytes >= len); - while (len >= 64 - bufsize) { - /* Fill the buffer, and process it. */ - size_t chunk_len = 64 - bufsize; - memcpy(((unsigned char*)hash->buf) + bufsize, data, chunk_len); - data += chunk_len; - len -= chunk_len; - secp256k1_sha256_transform(hash->s, hash->buf); - bufsize = 0; - } - if (len) { - /* Fill the buffer with what remains. */ - memcpy(((unsigned char*)hash->buf) + bufsize, data, len); - } -} - -static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32) { - static const unsigned char pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint32_t sizedesc[2]; - uint32_t out[8]; - int i = 0; - sizedesc[0] = BE32(hash->bytes >> 29); - sizedesc[1] = BE32(hash->bytes << 3); - secp256k1_sha256_write(hash, pad, 1 + ((119 - (hash->bytes % 64)) % 64)); - secp256k1_sha256_write(hash, (const unsigned char*)sizedesc, 8); - for (i = 0; i < 8; i++) { - out[i] = BE32(hash->s[i]); - hash->s[i] = 0; - } - memcpy(out32, (const unsigned char*)out, 32); -} - -/* Initializes a sha256 struct and writes the 64 byte string - * SHA256(tag)||SHA256(tag) into it. */ -static void secp256k1_sha256_initialize_tagged(secp256k1_sha256 *hash, const unsigned char *tag, size_t taglen) { - unsigned char buf[32]; - secp256k1_sha256_initialize(hash); - secp256k1_sha256_write(hash, tag, taglen); - secp256k1_sha256_finalize(hash, buf); - - secp256k1_sha256_initialize(hash); - secp256k1_sha256_write(hash, buf, 32); - secp256k1_sha256_write(hash, buf, 32); -} - -static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) { - size_t n; - unsigned char rkey[64]; - if (keylen <= sizeof(rkey)) { - memcpy(rkey, key, keylen); - memset(rkey + keylen, 0, sizeof(rkey) - keylen); - } else { - secp256k1_sha256 sha256; - secp256k1_sha256_initialize(&sha256); - secp256k1_sha256_write(&sha256, key, keylen); - secp256k1_sha256_finalize(&sha256, rkey); - memset(rkey + 32, 0, 32); - } - - secp256k1_sha256_initialize(&hash->outer); - for (n = 0; n < sizeof(rkey); n++) { - rkey[n] ^= 0x5c; - } - secp256k1_sha256_write(&hash->outer, rkey, sizeof(rkey)); - - secp256k1_sha256_initialize(&hash->inner); - for (n = 0; n < sizeof(rkey); n++) { - rkey[n] ^= 0x5c ^ 0x36; - } - secp256k1_sha256_write(&hash->inner, rkey, sizeof(rkey)); - memset(rkey, 0, sizeof(rkey)); -} - -static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size) { - secp256k1_sha256_write(&hash->inner, data, size); -} - -static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32) { - unsigned char temp[32]; - secp256k1_sha256_finalize(&hash->inner, temp); - secp256k1_sha256_write(&hash->outer, temp, 32); - memset(temp, 0, 32); - secp256k1_sha256_finalize(&hash->outer, out32); -} - - -static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen) { - secp256k1_hmac_sha256 hmac; - static const unsigned char zero[1] = {0x00}; - static const unsigned char one[1] = {0x01}; - - memset(rng->v, 0x01, 32); /* RFC6979 3.2.b. */ - memset(rng->k, 0x00, 32); /* RFC6979 3.2.c. */ - - /* RFC6979 3.2.d. */ - secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); - secp256k1_hmac_sha256_write(&hmac, rng->v, 32); - secp256k1_hmac_sha256_write(&hmac, zero, 1); - secp256k1_hmac_sha256_write(&hmac, key, keylen); - secp256k1_hmac_sha256_finalize(&hmac, rng->k); - secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); - secp256k1_hmac_sha256_write(&hmac, rng->v, 32); - secp256k1_hmac_sha256_finalize(&hmac, rng->v); - - /* RFC6979 3.2.f. */ - secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); - secp256k1_hmac_sha256_write(&hmac, rng->v, 32); - secp256k1_hmac_sha256_write(&hmac, one, 1); - secp256k1_hmac_sha256_write(&hmac, key, keylen); - secp256k1_hmac_sha256_finalize(&hmac, rng->k); - secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); - secp256k1_hmac_sha256_write(&hmac, rng->v, 32); - secp256k1_hmac_sha256_finalize(&hmac, rng->v); - rng->retry = 0; -} - -static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen) { - /* RFC6979 3.2.h. */ - static const unsigned char zero[1] = {0x00}; - if (rng->retry) { - secp256k1_hmac_sha256 hmac; - secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); - secp256k1_hmac_sha256_write(&hmac, rng->v, 32); - secp256k1_hmac_sha256_write(&hmac, zero, 1); - secp256k1_hmac_sha256_finalize(&hmac, rng->k); - secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); - secp256k1_hmac_sha256_write(&hmac, rng->v, 32); - secp256k1_hmac_sha256_finalize(&hmac, rng->v); - } - - while (outlen > 0) { - secp256k1_hmac_sha256 hmac; - int now = outlen; - secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); - secp256k1_hmac_sha256_write(&hmac, rng->v, 32); - secp256k1_hmac_sha256_finalize(&hmac, rng->v); - if (now > 32) { - now = 32; - } - memcpy(out, rng->v, now); - out += now; - outlen -= now; - } - - rng->retry = 1; -} - -static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng) { - memset(rng->k, 0, 32); - memset(rng->v, 0, 32); - rng->retry = 0; -} - -#undef BE32 -#undef Round -#undef sigma1 -#undef sigma0 -#undef Sigma1 -#undef Sigma0 -#undef Maj -#undef Ch - -#endif /* SECP256K1_HASH_IMPL_H */ diff --git a/libwallet/musig/hsort.h b/libwallet/musig/hsort.h deleted file mode 100644 index 0f57227c..00000000 --- a/libwallet/musig/hsort.h +++ /dev/null @@ -1,22 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2021 Russell O'Connor, Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_HSORT_H_ -#define SECP256K1_HSORT_H_ - -#include -#include - -/* In-place, iterative heapsort with an interface matching glibc's qsort_r. This - * is preferred over standard library implementations because they generally - * make no guarantee about being fast for malicious inputs. - * - * See the qsort_r manpage for a description of the interface. - */ -static void secp256k1_hsort(void *ptr, size_t count, size_t size, - int (*cmp)(const void *, const void *, void *), - void *cmp_data); -#endif diff --git a/libwallet/musig/hsort_impl.h b/libwallet/musig/hsort_impl.h deleted file mode 100644 index fcc4b10b..00000000 --- a/libwallet/musig/hsort_impl.h +++ /dev/null @@ -1,116 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2021 Russell O'Connor, Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_HSORT_IMPL_H_ -#define SECP256K1_HSORT_IMPL_H_ - -#include "hsort.h" - -/* An array is a heap when, for all non-zero indexes i, the element at index i - * compares as less than or equal to the element at index parent(i) = (i-1)/2. - */ - -static SECP256K1_INLINE size_t child1(size_t i) { - VERIFY_CHECK(i <= (SIZE_MAX - 1)/2); - return 2*i + 1; -} - -static SECP256K1_INLINE size_t child2(size_t i) { - VERIFY_CHECK(i <= SIZE_MAX/2 - 1); - return child1(i)+1; -} - -static SECP256K1_INLINE void swap64(unsigned char *a, size_t i, size_t j, size_t stride) { - unsigned char tmp[64]; - VERIFY_CHECK(stride <= 64); - memcpy(tmp, a + i*stride, stride); - memmove(a + i*stride, a + j*stride, stride); - memcpy(a + j*stride, tmp, stride); -} - -static SECP256K1_INLINE void swap(unsigned char *a, size_t i, size_t j, size_t stride) { - while (64 < stride) { - swap64(a + (stride - 64), i, j, 64); - stride -= 64; - } - swap64(a, i, j, stride); -} - -static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_size, size_t stride, - int (*cmp)(const void *, const void *, void *), void *cmp_data) { - while (i < heap_size/2) { - VERIFY_CHECK(i <= SIZE_MAX/2 - 1); - /* Proof: - * i < heap_size/2 - * i + 1 <= heap_size/2 - * 2*i + 2 <= heap_size <= SIZE_MAX - * 2*i <= SIZE_MAX - 2 - */ - - VERIFY_CHECK(child1(i) < heap_size); - /* Proof: - * i < heap_size/2 - * i + 1 <= heap_size/2 - * 2*i + 2 <= heap_size - * 2*i + 1 < heap_size - * child1(i) < heap_size - */ - - /* Let [x] be notation for the contents at a[x*stride]. - * - * If [child1(i)] > [i] and [child2(i)] > [i], - * swap [i] with the larger child to ensure the new parent is larger - * than both children. When [child1(i)] == [child2(i)], swap [i] with - * [child2(i)]. - * Else if [child1(i)] > [i], swap [i] with [child1(i)]. - * Else if [child2(i)] > [i], swap [i] with [child2(i)]. - */ - if (child2(i) < heap_size - && 0 <= cmp(a + child2(i)*stride, a + child1(i)*stride, cmp_data)) { - if (0 < cmp(a + child2(i)*stride, a + i*stride, cmp_data)) { - swap(a, i, child2(i), stride); - i = child2(i); - } else { - /* At this point we have [child2(i)] >= [child1(i)] and we have - * [child2(i)] <= [i], and thus [child1(i)] <= [i] which means - * that the next comparison can be skipped. */ - return; - } - } else if (0 < cmp(a + child1(i)*stride, a + i*stride, cmp_data)) { - swap(a, i, child1(i), stride); - i = child1(i); - } else { - return; - } - } - /* heap_size/2 <= i - * heap_size/2 < i + 1 - * heap_size < 2*i + 2 - * heap_size <= 2*i + 1 - * heap_size <= child1(i) - * Thus child1(i) and child2(i) are now out of bounds and we are at a leaf. - */ -} - -/* In-place heap sort. */ -static void secp256k1_hsort(void *ptr, size_t count, size_t size, - int (*cmp)(const void *, const void *, void *), - void *cmp_data ) { - size_t i; - - for(i = count/2; 0 < i; --i) { - heap_down(ptr, i-1, count, size, cmp, cmp_data); - } - for(i = count; 1 < i; --i) { - /* Extract the largest value from the heap */ - swap(ptr, 0, i-1, size); - - /* Repair the heap condition */ - heap_down(ptr, 0, i-1, size, cmp, cmp_data); - } -} - -#endif diff --git a/libwallet/musig/impl.go b/libwallet/musig/impl.go new file mode 100644 index 00000000..ab08f126 --- /dev/null +++ b/libwallet/musig/impl.go @@ -0,0 +1,194 @@ +package musig + +// This file contains Muun specific function that interact with the MuSig2 +// contexts. Code specific to differentiate MuSig versions should not exist in +// this file. + +import ( + "crypto/rand" + "errors" + + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +// RandomSessionId returns a safe random session id. Session IDs must not be +// repeated otherwise private keys are compromised. +func RandomSessionId() [32]byte { + var buf [32]byte + _, err := rand.Read(buf[:]) + if err != nil { + panic("couldn't read random bytes") + } + + return buf +} + +func VerifySignature( + musigVersion MusigVersion, + data []byte, + publicKey []byte, + signature []byte, +) (bool, error) { + + pubKey, err := ParsePubKey(musigVersion, publicKey) + if err != nil { + return false, err + } + + sig, err := schnorr.ParseSignature(signature) + if err != nil { + return false, err + } + + return sig.Verify(data, pubKey), nil +} + +// Computes the first part of the 2-2 signature. +// Returns a valid partial signature. +func ComputeMuunPartialSignature( + musigVersion MusigVersion, + data []byte, + userPublicKeyBytes []byte, + muunPrivateKeyBytes []byte, + rawUserPublicNonce []byte, + muunSessionId []byte, + tweak *MuSig2Tweaks, +) ([]byte, error) { + + muunPrivateKey := secp256k1.PrivKeyFromBytes(muunPrivateKeyBytes) + muunPublicKey := muunPrivateKey.PubKey() + muunPublicKeyBytes := muunPublicKey.SerializeCompressed() + + signerPublicKeys, err := MuSig2ParsePubKeys(musigVersion, [][]byte{ + userPublicKeyBytes, + muunPublicKeyBytes, + }) + if err != nil { + return nil, err + } + + // As we'd like the local nonce we send over to be generated + // deterministically, we'll provide a random sessionId as the primary + // randomness source. + muunNonce, err := MuSig2GenerateNonce(musigVersion, muunSessionId, muunPublicKeyBytes) + if err != nil { + return nil, err + } + + // Create a signing context and session with the given private key and + // list of all known signer public keys. + _, session, err := MuSig2CreateContext( + musigVersion, + muunPrivateKey, + signerPublicKeys, + tweak, + muunNonce, + ) + if err != nil { + return nil, err + } + + // Add all nonces we might've learned so far. + haveAllNonces := false + if haveAllNonces, err = session.RegisterPubNonce( + [66]byte(rawUserPublicNonce)); err != nil { + return nil, err + } + if !haveAllNonces { + return nil, errors.New("some nonces are missing") + } + + sig, err := MuSig2Sign(session, ([32]byte)(data)) + if err != nil { + return nil, err + } + + ret, err := SerializePartialSignature(sig) + if err != nil { + return nil, err + } + + return ret[:], nil +} + +// Computes the last part of the 2-2 signature. +// Final signature is ensured to be valid. +func ComputeUserPartialSignature( + musigVersion MusigVersion, + data []byte, + userPrivateKeyBytes []byte, + muunPublicKeyBytes []byte, + muunPartialSigBytes []byte, + muunPublicNonceBytes []byte, + userSessionId []byte, + tweak *MuSig2Tweaks, +) ([]byte, error) { + + userPrivateKey := secp256k1.PrivKeyFromBytes(userPrivateKeyBytes) + userPublicKey := userPrivateKey.PubKey() + userPublicKeyBytes := userPublicKey.SerializeCompressed() + + pubKeys := [][]byte{ + userPublicKeyBytes, + muunPublicKeyBytes, + } + signerPublicKeys, err := MuSig2ParsePubKeys(musigVersion, pubKeys) + if err != nil { + return nil, err + } + + // As we'd like the local nonce we send over to be generated + // deterministically, we'll provide a random sessionId as the primary + // randomness source. + userNonce, err := MuSig2GenerateNonce(musigVersion, userSessionId, userPublicKeyBytes) + if err != nil { + return nil, err + } + + // Create a signing context and session with the given private key and + // list of all known signer public keys. + _, session, err := MuSig2CreateContext( + musigVersion, + userPrivateKey, + signerPublicKeys, + tweak, + userNonce, + ) + if err != nil { + return nil, err + } + + // Add all nonces we might've learned so far. + haveAllNonces := false + if haveAllNonces, err = session.RegisterPubNonce( + [66]byte(muunPublicNonceBytes)); err != nil { + return nil, err + } + if !haveAllNonces { + return nil, errors.New("some nonces are missing") + } + + _, err = MuSig2Sign(session, ([32]byte)(data)) + if err != nil { + return nil, err + } + + muunSig, err := DeserializePartialSignature(muunPartialSigBytes) + if err != nil { + return nil, err + } + + haveAllSigs, err := MuSig2CombineSig(session, muunSig) + if err != nil { + return nil, err + } + if !haveAllSigs { + return nil, errors.New("some signatures are still missing") + } + + // FinalSig() also validates the signature + sig := session.FinalSig() + + return sig.Serialize()[:], nil +} diff --git a/libwallet/musig/import.sh b/libwallet/musig/import.sh deleted file mode 100644 index 758d9781..00000000 --- a/libwallet/musig/import.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash - -set -xe - -if [[ -z "$1" ]]; then - echo "Usage: $0 " - exit 1 -fi - -if [[ $(dirname "$0") != "." ]]; then - echo "Not on musig dir" - exit 1 -fi - -lib_path="$1" - -( - cd "$lib_path"; - ./autogen.sh; - ./configure --with-asm=no; - make; - make check -) - -find . -name "*.c" -and -not -name "umbrella.c" -delete -find . -name "*.h" -and -not -name "umbrella.h" -delete -rm -f secp256k1.k - -cp -r "$lib_path"/include/*.h . -cp -r "$lib_path"/src/*.c . -cp -r "$lib_path"/src/*.h . - -function include_module() { - cp -r "$lib_path"/src/modules/"$1"/*.h . - # Modules are composed of all .h files. Some contain several headers, but - # all have a file named main_impl.h with all the logic. To avoid modules - # overwritting each other files, we rename it to module_main_impl.h - mv main_impl.h "$1_main_impl.h" -} - -include_module extrakeys -include_module schnorrsig -include_module musig - -# Delete unit tests, benchmarks and unused headers. -rm \ - tests.c \ - tests_exhaustive.c \ - tests_exhaustive_impl.h \ - tests_impl.h \ - valgrind_ctime_test.c \ - bench_*.c \ - bench.h \ - secp256k1_ecdh.h \ - secp256k1_ecdsa_adaptor.h \ - secp256k1_ecdsa_s2c.h \ - secp256k1_generator.h \ - secp256k1_rangeproof.h \ - secp256k1_recovery.h \ - secp256k1_surjectionproof.h \ - secp256k1_whitelist.h \ - gen_context.c - -# This file makes cgo go crazy, but we need it. -# The solution is to rename to avoid cgo compiling it, and then including it from -# umbrella.c -mv secp256k1.c secp256k1.k - -# Remove all folder references from includes, they are not needed anymore -sed -i "" 's/include \"[.\/a-z]*\/\([^.\/]*\.h\)/include "\1/g' \ - secp256k1.k \ - *.c \ - *.h - -go test -v diff --git a/libwallet/musig/keyagg.h b/libwallet/musig/keyagg.h deleted file mode 100644 index 50b674fd..00000000 --- a/libwallet/musig/keyagg.h +++ /dev/null @@ -1,28 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2021 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_MODULE_MUSIG_KEYAGG_H -#define SECP256K1_MODULE_MUSIG_KEYAGG_H - -typedef struct { - secp256k1_ge pk; - secp256k1_fe second_pk_x; - const unsigned char *pk_hash; - int is_tweaked; - secp256k1_scalar tweak; - int internal_key_parity; -} secp256k1_keyagg_cache_internal; - -/* Requires that the saved point is not infinity */ -static void secp256k1_point_save(unsigned char *data, secp256k1_ge *ge); - -static void secp256k1_point_load(secp256k1_ge *ge, const unsigned char *data); - -static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_keyagg_cache_internal *cache_i, const secp256k1_musig_keyagg_cache *cache); - -static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_fe *x); - -#endif diff --git a/libwallet/musig/keyagg_impl.h b/libwallet/musig/keyagg_impl.h deleted file mode 100644 index 921307cd..00000000 --- a/libwallet/musig/keyagg_impl.h +++ /dev/null @@ -1,273 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2021 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_MODULE_MUSIG_KEYAGG_IMPL -#define SECP256K1_MODULE_MUSIG_KEYAGG_IMPL - -#include "keyagg.h" - - -static void secp256k1_point_save(unsigned char *data, secp256k1_ge *ge) { - if (sizeof(secp256k1_ge_storage) == 64) { - secp256k1_ge_storage s; - secp256k1_ge_to_storage(&s, ge); - memcpy(data, &s, sizeof(s)); - } else { - VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); - secp256k1_fe_normalize_var(&ge->x); - secp256k1_fe_normalize_var(&ge->y); - secp256k1_fe_get_b32(data, &ge->x); - secp256k1_fe_get_b32(data + 32, &ge->y); - } -} - -static void secp256k1_point_load(secp256k1_ge *ge, const unsigned char *data) { - if (sizeof(secp256k1_ge_storage) == 64) { - /* When the secp256k1_ge_storage type is exactly 64 byte, use its - * representation as conversion is very fast. */ - secp256k1_ge_storage s; - memcpy(&s, data, sizeof(s)); - secp256k1_ge_from_storage(ge, &s); - } else { - /* Otherwise, fall back to 32-byte big endian for X and Y. */ - secp256k1_fe x, y; - secp256k1_fe_set_b32(&x, data); - secp256k1_fe_set_b32(&y, data + 32); - secp256k1_ge_set_xy(ge, &x, &y); - } -} - -static const unsigned char secp256k1_musig_keyagg_cache_magic[4] = { 0xf4, 0xad, 0xbb, 0xdf }; - -/* A keyagg cache consists of - * - 4 byte magic set during initialization to allow detecting an uninitialized - * object. - * - 64 byte aggregate (and potentially tweaked) public key - * - 32 byte X-coordinate of "second" public key (0 if not present) - * - 32 byte hash of all public keys - * - 1 byte indicating if the public key is tweaked and if so, also the parity - * of the internal key - * - 32 byte tweak - */ -/* Requires that cache_i->pk is not infinity */ -static void secp256k1_keyagg_cache_save(secp256k1_musig_keyagg_cache *cache, secp256k1_keyagg_cache_internal *cache_i) { - unsigned char *ptr = cache->data; - memcpy(ptr, secp256k1_musig_keyagg_cache_magic, 4); - ptr += 4; - secp256k1_point_save(ptr, &cache_i->pk); - ptr += 64; - secp256k1_fe_get_b32(ptr, &cache_i->second_pk_x); - ptr += 32; - memmove(ptr, cache_i->pk_hash, 32); - ptr += 32; - *ptr = cache_i->is_tweaked; - *ptr |= cache_i->internal_key_parity << 1; - ptr += 1; - secp256k1_scalar_get_b32(ptr, &cache_i->tweak); -} - -static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_keyagg_cache_internal *cache_i, const secp256k1_musig_keyagg_cache *cache) { - const unsigned char *ptr = cache->data; - ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_musig_keyagg_cache_magic, 4) == 0); - ptr += 4; - secp256k1_point_load(&cache_i->pk, ptr); - ptr += 64; - secp256k1_fe_set_b32(&cache_i->second_pk_x, ptr); - ptr += 32; - cache_i->pk_hash = ptr; - ptr += 32; - cache_i->is_tweaked = *ptr & 1; - cache_i->internal_key_parity = *ptr & 2; - ptr += 1; - secp256k1_scalar_set_b32(&cache_i->tweak, ptr, NULL); - return 1; -} - -/* Initializes SHA256 with fixed midstate. This midstate was computed by applying - * SHA256 to SHA256("KeyAgg list")||SHA256("KeyAgg list"). */ -static void secp256k1_musig_keyagglist_sha256(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - - sha->s[0] = 0xb399d5e0ul; - sha->s[1] = 0xc8fff302ul; - sha->s[2] = 0x6badac71ul; - sha->s[3] = 0x07c5b7f1ul; - sha->s[4] = 0x9701e2eful; - sha->s[5] = 0x2a72ecf8ul; - sha->s[6] = 0x201a4c7bul; - sha->s[7] = 0xab148a38ul; - sha->bytes = 64; -} - -/* Computes pk_hash = SHA256(pk[0], ..., pk[np-1]) */ -static int secp256k1_musig_compute_pk_hash(const secp256k1_context *ctx, unsigned char *pk_hash, const secp256k1_xonly_pubkey * const* pk, size_t np) { - secp256k1_sha256 sha; - size_t i; - - secp256k1_musig_keyagglist_sha256(&sha); - for (i = 0; i < np; i++) { - unsigned char ser[32]; - if (!secp256k1_xonly_pubkey_serialize(ctx, ser, pk[i])) { - return 0; - } - secp256k1_sha256_write(&sha, ser, 32); - } - secp256k1_sha256_finalize(&sha, pk_hash); - return 1; -} - -/* Initializes SHA256 with fixed midstate. This midstate was computed by applying - * SHA256 to SHA256("KeyAgg coefficient")||SHA256("KeyAgg coefficient"). */ -static void secp256k1_musig_keyaggcoef_sha256(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - - sha->s[0] = 0x6ef02c5aul; - sha->s[1] = 0x06a480deul; - sha->s[2] = 0x1f298665ul; - sha->s[3] = 0x1d1134f2ul; - sha->s[4] = 0x56a0b063ul; - sha->s[5] = 0x52da4147ul; - sha->s[6] = 0xf280d9d4ul; - sha->s[7] = 0x4484be15ul; - sha->bytes = 64; -} - -/* Compute KeyAgg coefficient which is constant 1 for the second pubkey and - * SHA256(pk_hash, x) where pk_hash is the hash of public keys otherwise. second_pk_x - * can be 0 in case there is no second_pk. Assumes both field elements x and - * second_pk_x are normalized. */ -static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsigned char *pk_hash, const secp256k1_fe *x, const secp256k1_fe *second_pk_x) { - secp256k1_sha256 sha; - unsigned char buf[32]; - - if (secp256k1_fe_cmp_var(x, second_pk_x) == 0) { - secp256k1_scalar_set_int(r, 1); - } else { - secp256k1_musig_keyaggcoef_sha256(&sha); - secp256k1_sha256_write(&sha, pk_hash, 32); - secp256k1_fe_get_b32(buf, x); - secp256k1_sha256_write(&sha, buf, 32); - secp256k1_sha256_finalize(&sha, buf); - secp256k1_scalar_set_b32(r, buf, NULL); - } - -} - -/* Assumes both field elements x and second_pk_x are normalized. */ -static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_fe *x) { - secp256k1_musig_keyaggcoef_internal(r, cache_i->pk_hash, x, &cache_i->second_pk_x); -} - -typedef struct { - const secp256k1_context *ctx; - /* pk_hash is the hash of the public keys */ - unsigned char pk_hash[32]; - const secp256k1_xonly_pubkey * const* pks; - secp256k1_fe second_pk_x; -} secp256k1_musig_pubkey_agg_ecmult_data; - -/* Callback for batch EC multiplication to compute pk_hash_0*P0 + pk_hash_1*P1 + ... */ -static int secp256k1_musig_pubkey_agg_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { - secp256k1_musig_pubkey_agg_ecmult_data *ctx = (secp256k1_musig_pubkey_agg_ecmult_data *) data; - int ret; - ret = secp256k1_xonly_pubkey_load(ctx->ctx, pt, ctx->pks[idx]); - /* pubkey_load can't fail because the same pks have already been loaded (and - * we test this) */ - VERIFY_CHECK(ret); - secp256k1_musig_keyaggcoef_internal(sc, ctx->pk_hash, &pt->x, &ctx->second_pk_x); - return 1; -} - -int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_xonly_pubkey * const* pubkeys, size_t n_pubkeys) { - secp256k1_musig_pubkey_agg_ecmult_data ecmult_data; - secp256k1_gej pkj; - secp256k1_ge pkp; - size_t i; - - VERIFY_CHECK(ctx != NULL); - if (agg_pk != NULL) { - memset(agg_pk, 0, sizeof(*agg_pk)); - } - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(pubkeys != NULL); - ARG_CHECK(n_pubkeys > 0); - - ecmult_data.ctx = ctx; - ecmult_data.pks = pubkeys; - /* No point on the curve has an X coordinate equal to 0 */ - secp256k1_fe_set_int(&ecmult_data.second_pk_x, 0); - for (i = 1; i < n_pubkeys; i++) { - secp256k1_ge pt; - if (!secp256k1_xonly_pubkey_load(ctx, &pt, pubkeys[i])) { - return 0; - } - if (secp256k1_memcmp_var(pubkeys[0], pubkeys[i], sizeof(*pubkeys[0])) != 0) { - ecmult_data.second_pk_x = pt.x; - break; - } - } - - if (!secp256k1_musig_compute_pk_hash(ctx, ecmult_data.pk_hash, pubkeys, n_pubkeys)) { - return 0; - } - if (!secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &pkj, NULL, secp256k1_musig_pubkey_agg_callback, (void *) &ecmult_data, n_pubkeys)) { - /* The current implementation of ecmult_multi_var makes this code unreachable with tests. */ - return 0; - } - secp256k1_ge_set_gej(&pkp, &pkj); - secp256k1_fe_normalize_var(&pkp.y); - /* The resulting public key is infinity with negligible probability */ - VERIFY_CHECK(!secp256k1_ge_is_infinity(&pkp)); - if (keyagg_cache != NULL) { - secp256k1_keyagg_cache_internal cache_i = { 0 }; - cache_i.pk = pkp; - cache_i.second_pk_x = ecmult_data.second_pk_x; - cache_i.pk_hash = ecmult_data.pk_hash; - secp256k1_keyagg_cache_save(keyagg_cache, &cache_i); - } - - secp256k1_extrakeys_ge_even_y(&pkp); - if (agg_pk != NULL) { - secp256k1_xonly_pubkey_save(agg_pk, &pkp); - } - return 1; -} - -int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, const unsigned char *tweak32, secp256k1_musig_keyagg_cache *keyagg_cache) { - secp256k1_keyagg_cache_internal cache_i; - int overflow = 0; - - VERIFY_CHECK(ctx != NULL); - if (output_pubkey != NULL) { - memset(output_pubkey, 0, sizeof(*output_pubkey)); - } - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(keyagg_cache != NULL); - ARG_CHECK(tweak32 != NULL); - - if(!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { - return 0; - } - /* This function can only be called once because otherwise signing would not - * succeed */ - ARG_CHECK(cache_i.is_tweaked == 0); - - cache_i.internal_key_parity = secp256k1_extrakeys_ge_even_y(&cache_i.pk); - secp256k1_scalar_set_b32(&cache_i.tweak, tweak32, &overflow); - if(overflow || !secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &cache_i.pk, &cache_i.tweak)) { - return 0; - } - cache_i.is_tweaked = 1; - /* eckey_pubkey_tweak_add fails if cache_i.pk is infinity */ - VERIFY_CHECK(!secp256k1_ge_is_infinity(&cache_i.pk)); - secp256k1_keyagg_cache_save(keyagg_cache, &cache_i); - if (output_pubkey != NULL) { - secp256k1_pubkey_save(output_pubkey, &cache_i.pk); - } - return 1; -} - -#endif diff --git a/libwallet/musig/libsecp256k1-config.h b/libwallet/musig/libsecp256k1-config.h deleted file mode 100644 index 35af95c1..00000000 --- a/libwallet/musig/libsecp256k1-config.h +++ /dev/null @@ -1,154 +0,0 @@ -/* src/libsecp256k1-config.h. Generated from libsecp256k1-config.h.in by configure. */ -/* src/libsecp256k1-config.h.in. Generated from configure.ac by autoheader. */ - -#ifndef LIBSECP256K1_CONFIG_H - -#define LIBSECP256K1_CONFIG_H - -/* Define this symbol to compile out all VERIFY code */ -/* #undef COVERAGE */ - -/* Set ecmult gen precision bits */ -#define ECMULT_GEN_PREC_BITS 4 - -/* Set window size for ecmult precomputation */ -#define ECMULT_WINDOW_SIZE 15 - -/* Define this symbol to enable the ECDH module */ -/* #undef ENABLE_MODULE_ECDH */ - -/* Define this symbol to enable the ECDSA adaptor module */ -/* #undef ENABLE_MODULE_ECDSA_ADAPTOR */ - -/* Define this symbol to enable the ECDSA sign-to-contract module */ -/* #undef ENABLE_MODULE_ECDSA_S2C */ - -/* Define this symbol to enable the extrakeys module */ -/* #undef ENABLE_MODULE_EXTRAKEYS */ - -/* Define this symbol to enable the NUMS generator module */ -/* #undef ENABLE_MODULE_GENERATOR */ - -/* Define this symbol to enable the MuSig module */ -/* #undef ENABLE_MODULE_MUSIG */ - -/* Define this symbol to enable the Pedersen / zero knowledge range proof - module */ -/* #undef ENABLE_MODULE_RANGEPROOF */ - -/* Define this symbol to enable the ECDSA pubkey recovery module */ -/* #undef ENABLE_MODULE_RECOVERY */ - -/* Define this symbol to enable the schnorrsig module */ -/* #undef ENABLE_MODULE_SCHNORRSIG */ - -/* Define this symbol to enable the surjection proof module */ -/* #undef ENABLE_MODULE_SURJECTIONPROOF */ - -/* Define this symbol to enable the key whitelisting module */ -/* #undef ENABLE_MODULE_WHITELIST */ - -/* Define this symbol if OpenSSL EC functions are available */ -#define ENABLE_OPENSSL_TESTS 1 - -/* Define this symbol if __builtin_clzll is available */ -#define HAVE_BUILTIN_CLZLL 1 - -/* Define this symbol if __builtin_popcount is available */ -#define HAVE_BUILTIN_POPCOUNT 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_DLFCN_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define this symbol if libcrypto is installed */ -#define HAVE_LIBCRYPTO 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDIO_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define this symbol if valgrind is installed */ -/* #undef HAVE_VALGRIND */ - -/* Define to the sub-directory where libtool stores uninstalled libraries. */ -#define LT_OBJDIR ".libs/" - -/* Name of package */ -#define PACKAGE "libsecp256k1" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "libsecp256k1" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "libsecp256k1 0.1" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "libsecp256k1" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "0.1" - -/* Define to 1 if all of the C90 standard headers exist (not just the ones - required in a freestanding environment). This macro is provided for - backward compatibility; new code need not use it. */ -#define STDC_HEADERS 1 - -/* Define this symbol to enable x86_64 assembly optimizations */ -/* #undef USE_ASM_X86_64 */ - -/* Define this symbol to use a statically generated ecmult table */ -#define USE_ECMULT_STATIC_PRECOMPUTATION 1 - -/* Define this symbol if an external (non-inline) assembly implementation is - used */ -/* #undef USE_EXTERNAL_ASM */ - -/* Define this symbol if an external implementation of the default callbacks - is used */ -/* #undef USE_EXTERNAL_DEFAULT_CALLBACKS */ - -/* Define this symbol to force the use of the (unsigned) __int128 based wide - multiplication implementation */ -/* #undef USE_FORCE_WIDEMUL_INT128 */ - -/* Define this symbol to force the use of the (u)int64_t based wide - multiplication implementation */ -/* #undef USE_FORCE_WIDEMUL_INT64 */ - -/* Define this symbol to reduce SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS to 16, - disabling parsing and verification */ -/* #undef USE_REDUCED_SURJECTION_PROOF_SIZE */ - -/* Version number of package */ -#define VERSION "0.1" - -#endif /*LIBSECP256K1_CONFIG_H*/ diff --git a/libwallet/musig/modinv32.h b/libwallet/musig/modinv32.h deleted file mode 100644 index 0efdda9a..00000000 --- a/libwallet/musig/modinv32.h +++ /dev/null @@ -1,42 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2020 Peter Dettman * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef SECP256K1_MODINV32_H -#define SECP256K1_MODINV32_H - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - -#include "util.h" - -/* A signed 30-bit limb representation of integers. - * - * Its value is sum(v[i] * 2^(30*i), i=0..8). */ -typedef struct { - int32_t v[9]; -} secp256k1_modinv32_signed30; - -typedef struct { - /* The modulus in signed30 notation, must be odd and in [3, 2^256]. */ - secp256k1_modinv32_signed30 modulus; - - /* modulus^{-1} mod 2^30 */ - uint32_t modulus_inv30; -} secp256k1_modinv32_modinfo; - -/* Replace x with its modular inverse mod modinfo->modulus. x must be in range [0, modulus). - * If x is zero, the result will be zero as well. If not, the inverse must exist (i.e., the gcd of - * x and modulus must be 1). These rules are automatically satisfied if the modulus is prime. - * - * On output, all of x's limbs will be in [0, 2^30). - */ -static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo); - -/* Same as secp256k1_modinv32_var, but constant time in x (not in the modulus). */ -static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo); - -#endif /* SECP256K1_MODINV32_H */ diff --git a/libwallet/musig/modinv32_impl.h b/libwallet/musig/modinv32_impl.h deleted file mode 100644 index 661c5fc0..00000000 --- a/libwallet/musig/modinv32_impl.h +++ /dev/null @@ -1,587 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2020 Peter Dettman * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef SECP256K1_MODINV32_IMPL_H -#define SECP256K1_MODINV32_IMPL_H - -#include "modinv32.h" - -#include "util.h" - -#include - -/* This file implements modular inversion based on the paper "Fast constant-time gcd computation and - * modular inversion" by Daniel J. Bernstein and Bo-Yin Yang. - * - * For an explanation of the algorithm, see doc/safegcd_implementation.md. This file contains an - * implementation for N=30, using 30-bit signed limbs represented as int32_t. - */ - -#ifdef VERIFY -static const secp256k1_modinv32_signed30 SECP256K1_SIGNED30_ONE = {{1}}; - -/* Compute a*factor and put it in r. All but the top limb in r will be in range [0,2^30). */ -static void secp256k1_modinv32_mul_30(secp256k1_modinv32_signed30 *r, const secp256k1_modinv32_signed30 *a, int alen, int32_t factor) { - const int32_t M30 = (int32_t)(UINT32_MAX >> 2); - int64_t c = 0; - int i; - for (i = 0; i < 8; ++i) { - if (i < alen) c += (int64_t)a->v[i] * factor; - r->v[i] = (int32_t)c & M30; c >>= 30; - } - if (8 < alen) c += (int64_t)a->v[8] * factor; - VERIFY_CHECK(c == (int32_t)c); - r->v[8] = (int32_t)c; -} - -/* Return -1 for ab*factor. A consists of alen limbs; b has 9. */ -static int secp256k1_modinv32_mul_cmp_30(const secp256k1_modinv32_signed30 *a, int alen, const secp256k1_modinv32_signed30 *b, int32_t factor) { - int i; - secp256k1_modinv32_signed30 am, bm; - secp256k1_modinv32_mul_30(&am, a, alen, 1); /* Normalize all but the top limb of a. */ - secp256k1_modinv32_mul_30(&bm, b, 9, factor); - for (i = 0; i < 8; ++i) { - /* Verify that all but the top limb of a and b are normalized. */ - VERIFY_CHECK(am.v[i] >> 30 == 0); - VERIFY_CHECK(bm.v[i] >> 30 == 0); - } - for (i = 8; i >= 0; --i) { - if (am.v[i] < bm.v[i]) return -1; - if (am.v[i] > bm.v[i]) return 1; - } - return 0; -} -#endif - -/* Take as input a signed30 number in range (-2*modulus,modulus), and add a multiple of the modulus - * to it to bring it to range [0,modulus). If sign < 0, the input will also be negated in the - * process. The input must have limbs in range (-2^30,2^30). The output will have limbs in range - * [0,2^30). */ -static void secp256k1_modinv32_normalize_30(secp256k1_modinv32_signed30 *r, int32_t sign, const secp256k1_modinv32_modinfo *modinfo) { - const int32_t M30 = (int32_t)(UINT32_MAX >> 2); - int32_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4], - r5 = r->v[5], r6 = r->v[6], r7 = r->v[7], r8 = r->v[8]; - int32_t cond_add, cond_negate; - -#ifdef VERIFY - /* Verify that all limbs are in range (-2^30,2^30). */ - int i; - for (i = 0; i < 9; ++i) { - VERIFY_CHECK(r->v[i] >= -M30); - VERIFY_CHECK(r->v[i] <= M30); - } - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, -2) > 0); /* r > -2*modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 1) < 0); /* r < modulus */ -#endif - - /* In a first step, add the modulus if the input is negative, and then negate if requested. - * This brings r from range (-2*modulus,modulus) to range (-modulus,modulus). As all input - * limbs are in range (-2^30,2^30), this cannot overflow an int32_t. Note that the right - * shifts below are signed sign-extending shifts (see assumptions.h for tests that that is - * indeed the behavior of the right shift operator). */ - cond_add = r8 >> 31; - r0 += modinfo->modulus.v[0] & cond_add; - r1 += modinfo->modulus.v[1] & cond_add; - r2 += modinfo->modulus.v[2] & cond_add; - r3 += modinfo->modulus.v[3] & cond_add; - r4 += modinfo->modulus.v[4] & cond_add; - r5 += modinfo->modulus.v[5] & cond_add; - r6 += modinfo->modulus.v[6] & cond_add; - r7 += modinfo->modulus.v[7] & cond_add; - r8 += modinfo->modulus.v[8] & cond_add; - cond_negate = sign >> 31; - r0 = (r0 ^ cond_negate) - cond_negate; - r1 = (r1 ^ cond_negate) - cond_negate; - r2 = (r2 ^ cond_negate) - cond_negate; - r3 = (r3 ^ cond_negate) - cond_negate; - r4 = (r4 ^ cond_negate) - cond_negate; - r5 = (r5 ^ cond_negate) - cond_negate; - r6 = (r6 ^ cond_negate) - cond_negate; - r7 = (r7 ^ cond_negate) - cond_negate; - r8 = (r8 ^ cond_negate) - cond_negate; - /* Propagate the top bits, to bring limbs back to range (-2^30,2^30). */ - r1 += r0 >> 30; r0 &= M30; - r2 += r1 >> 30; r1 &= M30; - r3 += r2 >> 30; r2 &= M30; - r4 += r3 >> 30; r3 &= M30; - r5 += r4 >> 30; r4 &= M30; - r6 += r5 >> 30; r5 &= M30; - r7 += r6 >> 30; r6 &= M30; - r8 += r7 >> 30; r7 &= M30; - - /* In a second step add the modulus again if the result is still negative, bringing r to range - * [0,modulus). */ - cond_add = r8 >> 31; - r0 += modinfo->modulus.v[0] & cond_add; - r1 += modinfo->modulus.v[1] & cond_add; - r2 += modinfo->modulus.v[2] & cond_add; - r3 += modinfo->modulus.v[3] & cond_add; - r4 += modinfo->modulus.v[4] & cond_add; - r5 += modinfo->modulus.v[5] & cond_add; - r6 += modinfo->modulus.v[6] & cond_add; - r7 += modinfo->modulus.v[7] & cond_add; - r8 += modinfo->modulus.v[8] & cond_add; - /* And propagate again. */ - r1 += r0 >> 30; r0 &= M30; - r2 += r1 >> 30; r1 &= M30; - r3 += r2 >> 30; r2 &= M30; - r4 += r3 >> 30; r3 &= M30; - r5 += r4 >> 30; r4 &= M30; - r6 += r5 >> 30; r5 &= M30; - r7 += r6 >> 30; r6 &= M30; - r8 += r7 >> 30; r7 &= M30; - - r->v[0] = r0; - r->v[1] = r1; - r->v[2] = r2; - r->v[3] = r3; - r->v[4] = r4; - r->v[5] = r5; - r->v[6] = r6; - r->v[7] = r7; - r->v[8] = r8; - -#ifdef VERIFY - VERIFY_CHECK(r0 >> 30 == 0); - VERIFY_CHECK(r1 >> 30 == 0); - VERIFY_CHECK(r2 >> 30 == 0); - VERIFY_CHECK(r3 >> 30 == 0); - VERIFY_CHECK(r4 >> 30 == 0); - VERIFY_CHECK(r5 >> 30 == 0); - VERIFY_CHECK(r6 >> 30 == 0); - VERIFY_CHECK(r7 >> 30 == 0); - VERIFY_CHECK(r8 >> 30 == 0); - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 0) >= 0); /* r >= 0 */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 1) < 0); /* r < modulus */ -#endif -} - -/* Data type for transition matrices (see section 3 of explanation). - * - * t = [ u v ] - * [ q r ] - */ -typedef struct { - int32_t u, v, q, r; -} secp256k1_modinv32_trans2x2; - -/* Compute the transition matrix and zeta for 30 divsteps. - * - * Input: zeta: initial zeta - * f0: bottom limb of initial f - * g0: bottom limb of initial g - * Output: t: transition matrix - * Return: final zeta - * - * Implements the divsteps_n_matrix function from the explanation. - */ -static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) { - /* u,v,q,r are the elements of the transformation matrix being built up, - * starting with the identity matrix. Semantically they are signed integers - * in range [-2^30,2^30], but here represented as unsigned mod 2^32. This - * permits left shifting (which is UB for negative numbers). The range - * being inside [-2^31,2^31) means that casting to signed works correctly. - */ - uint32_t u = 1, v = 0, q = 0, r = 1; - uint32_t c1, c2, f = f0, g = g0, x, y, z; - int i; - - for (i = 0; i < 30; ++i) { - VERIFY_CHECK((f & 1) == 1); /* f must always be odd */ - VERIFY_CHECK((u * f0 + v * g0) == f << i); - VERIFY_CHECK((q * f0 + r * g0) == g << i); - /* Compute conditional masks for (zeta < 0) and for (g & 1). */ - c1 = zeta >> 31; - c2 = -(g & 1); - /* Compute x,y,z, conditionally negated versions of f,u,v. */ - x = (f ^ c1) - c1; - y = (u ^ c1) - c1; - z = (v ^ c1) - c1; - /* Conditionally add x,y,z to g,q,r. */ - g += x & c2; - q += y & c2; - r += z & c2; - /* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */ - c1 &= c2; - /* Conditionally change zeta into -zeta-2 or zeta-1. */ - zeta = (zeta ^ c1) - 1; - /* Conditionally add g,q,r to f,u,v. */ - f += g & c1; - u += q & c1; - v += r & c1; - /* Shifts */ - g >>= 1; - u <<= 1; - v <<= 1; - /* Bounds on zeta that follow from the bounds on iteration count (max 20*30 divsteps). */ - VERIFY_CHECK(zeta >= -601 && zeta <= 601); - } - /* Return data in t and return value. */ - t->u = (int32_t)u; - t->v = (int32_t)v; - t->q = (int32_t)q; - t->r = (int32_t)r; - /* The determinant of t must be a power of two. This guarantees that multiplication with t - * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which - * will be divided out again). As each divstep's individual matrix has determinant 2, the - * aggregate of 30 of them will have determinant 2^30. */ - VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30); - return zeta; -} - -/* Compute the transition matrix and eta for 30 divsteps (variable time). - * - * Input: eta: initial eta - * f0: bottom limb of initial f - * g0: bottom limb of initial g - * Output: t: transition matrix - * Return: final eta - * - * Implements the divsteps_n_matrix_var function from the explanation. - */ -static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) { - /* inv256[i] = -(2*i+1)^-1 (mod 256) */ - static const uint8_t inv256[128] = { - 0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59, - 0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31, - 0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89, - 0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61, - 0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9, - 0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91, - 0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9, - 0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1, - 0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19, - 0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1, - 0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01 - }; - - /* Transformation matrix; see comments in secp256k1_modinv32_divsteps_30. */ - uint32_t u = 1, v = 0, q = 0, r = 1; - uint32_t f = f0, g = g0, m; - uint16_t w; - int i = 30, limit, zeros; - - for (;;) { - /* Use a sentinel bit to count zeros only up to i. */ - zeros = secp256k1_ctz32_var(g | (UINT32_MAX << i)); - /* Perform zeros divsteps at once; they all just divide g by two. */ - g >>= zeros; - u <<= zeros; - v <<= zeros; - eta -= zeros; - i -= zeros; - /* We're done once we've done 30 divsteps. */ - if (i == 0) break; - VERIFY_CHECK((f & 1) == 1); - VERIFY_CHECK((g & 1) == 1); - VERIFY_CHECK((u * f0 + v * g0) == f << (30 - i)); - VERIFY_CHECK((q * f0 + r * g0) == g << (30 - i)); - /* Bounds on eta that follow from the bounds on iteration count (max 25*30 divsteps). */ - VERIFY_CHECK(eta >= -751 && eta <= 751); - /* If eta is negative, negate it and replace f,g with g,-f. */ - if (eta < 0) { - uint32_t tmp; - eta = -eta; - tmp = f; f = g; g = -tmp; - tmp = u; u = q; q = -tmp; - tmp = v; v = r; r = -tmp; - } - /* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more - * than i can be cancelled out (as we'd be done before that point), and no more than eta+1 - * can be done as its sign will flip once that happens. */ - limit = ((int)eta + 1) > i ? i : ((int)eta + 1); - /* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */ - VERIFY_CHECK(limit > 0 && limit <= 30); - m = (UINT32_MAX >> (32 - limit)) & 255U; - /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */ - w = (g * inv256[(f >> 1) & 127]) & m; - /* Do so. */ - g += f * w; - q += u * w; - r += v * w; - VERIFY_CHECK((g & m) == 0); - } - /* Return data in t and return value. */ - t->u = (int32_t)u; - t->v = (int32_t)v; - t->q = (int32_t)q; - t->r = (int32_t)r; - /* The determinant of t must be a power of two. This guarantees that multiplication with t - * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which - * will be divided out again). As each divstep's individual matrix has determinant 2, the - * aggregate of 30 of them will have determinant 2^30. */ - VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30); - return eta; -} - -/* Compute (t/2^30) * [d, e] mod modulus, where t is a transition matrix for 30 divsteps. - * - * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range - * (-2^30,2^30). - * - * This implements the update_de function from the explanation. - */ -static void secp256k1_modinv32_update_de_30(secp256k1_modinv32_signed30 *d, secp256k1_modinv32_signed30 *e, const secp256k1_modinv32_trans2x2 *t, const secp256k1_modinv32_modinfo* modinfo) { - const int32_t M30 = (int32_t)(UINT32_MAX >> 2); - const int32_t u = t->u, v = t->v, q = t->q, r = t->r; - int32_t di, ei, md, me, sd, se; - int64_t cd, ce; - int i; -#ifdef VERIFY - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, -2) > 0); /* d > -2*modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, 1) < 0); /* d < modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, -2) > 0); /* e > -2*modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, 1) < 0); /* e < modulus */ - VERIFY_CHECK((labs(u) + labs(v)) >= 0); /* |u|+|v| doesn't overflow */ - VERIFY_CHECK((labs(q) + labs(r)) >= 0); /* |q|+|r| doesn't overflow */ - VERIFY_CHECK((labs(u) + labs(v)) <= M30 + 1); /* |u|+|v| <= 2^30 */ - VERIFY_CHECK((labs(q) + labs(r)) <= M30 + 1); /* |q|+|r| <= 2^30 */ -#endif - /* [md,me] start as zero; plus [u,q] if d is negative; plus [v,r] if e is negative. */ - sd = d->v[8] >> 31; - se = e->v[8] >> 31; - md = (u & sd) + (v & se); - me = (q & sd) + (r & se); - /* Begin computing t*[d,e]. */ - di = d->v[0]; - ei = e->v[0]; - cd = (int64_t)u * di + (int64_t)v * ei; - ce = (int64_t)q * di + (int64_t)r * ei; - /* Correct md,me so that t*[d,e]+modulus*[md,me] has 30 zero bottom bits. */ - md -= (modinfo->modulus_inv30 * (uint32_t)cd + md) & M30; - me -= (modinfo->modulus_inv30 * (uint32_t)ce + me) & M30; - /* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */ - cd += (int64_t)modinfo->modulus.v[0] * md; - ce += (int64_t)modinfo->modulus.v[0] * me; - /* Verify that the low 30 bits of the computation are indeed zero, and then throw them away. */ - VERIFY_CHECK(((int32_t)cd & M30) == 0); cd >>= 30; - VERIFY_CHECK(((int32_t)ce & M30) == 0); ce >>= 30; - /* Now iteratively compute limb i=1..8 of t*[d,e]+modulus*[md,me], and store them in output - * limb i-1 (shifting down by 30 bits). */ - for (i = 1; i < 9; ++i) { - di = d->v[i]; - ei = e->v[i]; - cd += (int64_t)u * di + (int64_t)v * ei; - ce += (int64_t)q * di + (int64_t)r * ei; - cd += (int64_t)modinfo->modulus.v[i] * md; - ce += (int64_t)modinfo->modulus.v[i] * me; - d->v[i - 1] = (int32_t)cd & M30; cd >>= 30; - e->v[i - 1] = (int32_t)ce & M30; ce >>= 30; - } - /* What remains is limb 9 of t*[d,e]+modulus*[md,me]; store it as output limb 8. */ - d->v[8] = (int32_t)cd; - e->v[8] = (int32_t)ce; -#ifdef VERIFY - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, -2) > 0); /* d > -2*modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, 1) < 0); /* d < modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, -2) > 0); /* e > -2*modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, 1) < 0); /* e < modulus */ -#endif -} - -/* Compute (t/2^30) * [f, g], where t is a transition matrix for 30 divsteps. - * - * This implements the update_fg function from the explanation. - */ -static void secp256k1_modinv32_update_fg_30(secp256k1_modinv32_signed30 *f, secp256k1_modinv32_signed30 *g, const secp256k1_modinv32_trans2x2 *t) { - const int32_t M30 = (int32_t)(UINT32_MAX >> 2); - const int32_t u = t->u, v = t->v, q = t->q, r = t->r; - int32_t fi, gi; - int64_t cf, cg; - int i; - /* Start computing t*[f,g]. */ - fi = f->v[0]; - gi = g->v[0]; - cf = (int64_t)u * fi + (int64_t)v * gi; - cg = (int64_t)q * fi + (int64_t)r * gi; - /* Verify that the bottom 30 bits of the result are zero, and then throw them away. */ - VERIFY_CHECK(((int32_t)cf & M30) == 0); cf >>= 30; - VERIFY_CHECK(((int32_t)cg & M30) == 0); cg >>= 30; - /* Now iteratively compute limb i=1..8 of t*[f,g], and store them in output limb i-1 (shifting - * down by 30 bits). */ - for (i = 1; i < 9; ++i) { - fi = f->v[i]; - gi = g->v[i]; - cf += (int64_t)u * fi + (int64_t)v * gi; - cg += (int64_t)q * fi + (int64_t)r * gi; - f->v[i - 1] = (int32_t)cf & M30; cf >>= 30; - g->v[i - 1] = (int32_t)cg & M30; cg >>= 30; - } - /* What remains is limb 9 of t*[f,g]; store it as output limb 8. */ - f->v[8] = (int32_t)cf; - g->v[8] = (int32_t)cg; -} - -/* Compute (t/2^30) * [f, g], where t is a transition matrix for 30 divsteps. - * - * Version that operates on a variable number of limbs in f and g. - * - * This implements the update_fg function from the explanation in modinv64_impl.h. - */ -static void secp256k1_modinv32_update_fg_30_var(int len, secp256k1_modinv32_signed30 *f, secp256k1_modinv32_signed30 *g, const secp256k1_modinv32_trans2x2 *t) { - const int32_t M30 = (int32_t)(UINT32_MAX >> 2); - const int32_t u = t->u, v = t->v, q = t->q, r = t->r; - int32_t fi, gi; - int64_t cf, cg; - int i; - VERIFY_CHECK(len > 0); - /* Start computing t*[f,g]. */ - fi = f->v[0]; - gi = g->v[0]; - cf = (int64_t)u * fi + (int64_t)v * gi; - cg = (int64_t)q * fi + (int64_t)r * gi; - /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */ - VERIFY_CHECK(((int32_t)cf & M30) == 0); cf >>= 30; - VERIFY_CHECK(((int32_t)cg & M30) == 0); cg >>= 30; - /* Now iteratively compute limb i=1..len of t*[f,g], and store them in output limb i-1 (shifting - * down by 30 bits). */ - for (i = 1; i < len; ++i) { - fi = f->v[i]; - gi = g->v[i]; - cf += (int64_t)u * fi + (int64_t)v * gi; - cg += (int64_t)q * fi + (int64_t)r * gi; - f->v[i - 1] = (int32_t)cf & M30; cf >>= 30; - g->v[i - 1] = (int32_t)cg & M30; cg >>= 30; - } - /* What remains is limb (len) of t*[f,g]; store it as output limb (len-1). */ - f->v[len - 1] = (int32_t)cf; - g->v[len - 1] = (int32_t)cg; -} - -/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (constant time in x). */ -static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) { - /* Start with d=0, e=1, f=modulus, g=x, zeta=-1. */ - secp256k1_modinv32_signed30 d = {{0}}; - secp256k1_modinv32_signed30 e = {{1}}; - secp256k1_modinv32_signed30 f = modinfo->modulus; - secp256k1_modinv32_signed30 g = *x; - int i; - int32_t zeta = -1; /* zeta = -(delta+1/2); delta is initially 1/2. */ - - /* Do 20 iterations of 30 divsteps each = 600 divsteps. 590 suffices for 256-bit inputs. */ - for (i = 0; i < 20; ++i) { - /* Compute transition matrix and new zeta after 30 divsteps. */ - secp256k1_modinv32_trans2x2 t; - zeta = secp256k1_modinv32_divsteps_30(zeta, f.v[0], g.v[0], &t); - /* Update d,e using that transition matrix. */ - secp256k1_modinv32_update_de_30(&d, &e, &t, modinfo); - /* Update f,g using that transition matrix. */ -#ifdef VERIFY - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) > 0); /* f > -modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) <= 0); /* f <= modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, -1) > 0); /* g > -modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, 1) < 0); /* g < modulus */ -#endif - secp256k1_modinv32_update_fg_30(&f, &g, &t); -#ifdef VERIFY - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) > 0); /* f > -modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) <= 0); /* f <= modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, -1) > 0); /* g > -modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, 1) < 0); /* g < modulus */ -#endif - } - - /* At this point sufficient iterations have been performed that g must have reached 0 - * and (if g was not originally 0) f must now equal +/- GCD of the initial f, g - * values i.e. +/- 1, and d now contains +/- the modular inverse. */ -#ifdef VERIFY - /* g == 0 */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &SECP256K1_SIGNED30_ONE, 0) == 0); - /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &SECP256K1_SIGNED30_ONE, -1) == 0 || - secp256k1_modinv32_mul_cmp_30(&f, 9, &SECP256K1_SIGNED30_ONE, 1) == 0 || - (secp256k1_modinv32_mul_cmp_30(x, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 && - secp256k1_modinv32_mul_cmp_30(&d, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 && - (secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) == 0 || - secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) == 0))); -#endif - - /* Optionally negate d, normalize to [0,modulus), and return it. */ - secp256k1_modinv32_normalize_30(&d, f.v[8], modinfo); - *x = d; -} - -/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (variable time). */ -static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) { - /* Start with d=0, e=1, f=modulus, g=x, eta=-1. */ - secp256k1_modinv32_signed30 d = {{0, 0, 0, 0, 0, 0, 0, 0, 0}}; - secp256k1_modinv32_signed30 e = {{1, 0, 0, 0, 0, 0, 0, 0, 0}}; - secp256k1_modinv32_signed30 f = modinfo->modulus; - secp256k1_modinv32_signed30 g = *x; -#ifdef VERIFY - int i = 0; -#endif - int j, len = 9; - int32_t eta = -1; /* eta = -delta; delta is initially 1 (faster for the variable-time code) */ - int32_t cond, fn, gn; - - /* Do iterations of 30 divsteps each until g=0. */ - while (1) { - /* Compute transition matrix and new eta after 30 divsteps. */ - secp256k1_modinv32_trans2x2 t; - eta = secp256k1_modinv32_divsteps_30_var(eta, f.v[0], g.v[0], &t); - /* Update d,e using that transition matrix. */ - secp256k1_modinv32_update_de_30(&d, &e, &t, modinfo); - /* Update f,g using that transition matrix. */ -#ifdef VERIFY - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ -#endif - secp256k1_modinv32_update_fg_30_var(len, &f, &g, &t); - /* If the bottom limb of g is 0, there is a chance g=0. */ - if (g.v[0] == 0) { - cond = 0; - /* Check if all other limbs are also 0. */ - for (j = 1; j < len; ++j) { - cond |= g.v[j]; - } - /* If so, we're done. */ - if (cond == 0) break; - } - - /* Determine if len>1 and limb (len-1) of both f and g is 0 or -1. */ - fn = f.v[len - 1]; - gn = g.v[len - 1]; - cond = ((int32_t)len - 2) >> 31; - cond |= fn ^ (fn >> 31); - cond |= gn ^ (gn >> 31); - /* If so, reduce length, propagating the sign of f and g's top limb into the one below. */ - if (cond == 0) { - f.v[len - 2] |= (uint32_t)fn << 30; - g.v[len - 2] |= (uint32_t)gn << 30; - --len; - } -#ifdef VERIFY - VERIFY_CHECK(++i < 25); /* We should never need more than 25*30 = 750 divsteps */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ -#endif - } - - /* At this point g is 0 and (if g was not originally 0) f must now equal +/- GCD of - * the initial f, g values i.e. +/- 1, and d now contains +/- the modular inverse. */ -#ifdef VERIFY - /* g == 0 */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &SECP256K1_SIGNED30_ONE, 0) == 0); - /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */ - VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &SECP256K1_SIGNED30_ONE, -1) == 0 || - secp256k1_modinv32_mul_cmp_30(&f, len, &SECP256K1_SIGNED30_ONE, 1) == 0 || - (secp256k1_modinv32_mul_cmp_30(x, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 && - secp256k1_modinv32_mul_cmp_30(&d, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 && - (secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) == 0 || - secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) == 0))); -#endif - - /* Optionally negate d, normalize to [0,modulus), and return it. */ - secp256k1_modinv32_normalize_30(&d, f.v[len - 1], modinfo); - *x = d; -} - -#endif /* SECP256K1_MODINV32_IMPL_H */ diff --git a/libwallet/musig/modinv64.h b/libwallet/musig/modinv64.h deleted file mode 100644 index da506dfa..00000000 --- a/libwallet/musig/modinv64.h +++ /dev/null @@ -1,46 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2020 Peter Dettman * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef SECP256K1_MODINV64_H -#define SECP256K1_MODINV64_H - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - -#include "util.h" - -#ifndef SECP256K1_WIDEMUL_INT128 -#error "modinv64 requires 128-bit wide multiplication support" -#endif - -/* A signed 62-bit limb representation of integers. - * - * Its value is sum(v[i] * 2^(62*i), i=0..4). */ -typedef struct { - int64_t v[5]; -} secp256k1_modinv64_signed62; - -typedef struct { - /* The modulus in signed62 notation, must be odd and in [3, 2^256]. */ - secp256k1_modinv64_signed62 modulus; - - /* modulus^{-1} mod 2^62 */ - uint64_t modulus_inv62; -} secp256k1_modinv64_modinfo; - -/* Replace x with its modular inverse mod modinfo->modulus. x must be in range [0, modulus). - * If x is zero, the result will be zero as well. If not, the inverse must exist (i.e., the gcd of - * x and modulus must be 1). These rules are automatically satisfied if the modulus is prime. - * - * On output, all of x's limbs will be in [0, 2^62). - */ -static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo); - -/* Same as secp256k1_modinv64_var, but constant time in x (not in the modulus). */ -static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo); - -#endif /* SECP256K1_MODINV64_H */ diff --git a/libwallet/musig/modinv64_impl.h b/libwallet/musig/modinv64_impl.h deleted file mode 100644 index 0743a9c8..00000000 --- a/libwallet/musig/modinv64_impl.h +++ /dev/null @@ -1,593 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2020 Peter Dettman * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef SECP256K1_MODINV64_IMPL_H -#define SECP256K1_MODINV64_IMPL_H - -#include "modinv64.h" - -#include "util.h" - -/* This file implements modular inversion based on the paper "Fast constant-time gcd computation and - * modular inversion" by Daniel J. Bernstein and Bo-Yin Yang. - * - * For an explanation of the algorithm, see doc/safegcd_implementation.md. This file contains an - * implementation for N=62, using 62-bit signed limbs represented as int64_t. - */ - -#ifdef VERIFY -/* Helper function to compute the absolute value of an int64_t. - * (we don't use abs/labs/llabs as it depends on the int sizes). */ -static int64_t secp256k1_modinv64_abs(int64_t v) { - VERIFY_CHECK(v > INT64_MIN); - if (v < 0) return -v; - return v; -} - -static const secp256k1_modinv64_signed62 SECP256K1_SIGNED62_ONE = {{1}}; - -/* Compute a*factor and put it in r. All but the top limb in r will be in range [0,2^62). */ -static void secp256k1_modinv64_mul_62(secp256k1_modinv64_signed62 *r, const secp256k1_modinv64_signed62 *a, int alen, int64_t factor) { - const int64_t M62 = (int64_t)(UINT64_MAX >> 2); - int128_t c = 0; - int i; - for (i = 0; i < 4; ++i) { - if (i < alen) c += (int128_t)a->v[i] * factor; - r->v[i] = (int64_t)c & M62; c >>= 62; - } - if (4 < alen) c += (int128_t)a->v[4] * factor; - VERIFY_CHECK(c == (int64_t)c); - r->v[4] = (int64_t)c; -} - -/* Return -1 for ab*factor. A has alen limbs; b has 5. */ -static int secp256k1_modinv64_mul_cmp_62(const secp256k1_modinv64_signed62 *a, int alen, const secp256k1_modinv64_signed62 *b, int64_t factor) { - int i; - secp256k1_modinv64_signed62 am, bm; - secp256k1_modinv64_mul_62(&am, a, alen, 1); /* Normalize all but the top limb of a. */ - secp256k1_modinv64_mul_62(&bm, b, 5, factor); - for (i = 0; i < 4; ++i) { - /* Verify that all but the top limb of a and b are normalized. */ - VERIFY_CHECK(am.v[i] >> 62 == 0); - VERIFY_CHECK(bm.v[i] >> 62 == 0); - } - for (i = 4; i >= 0; --i) { - if (am.v[i] < bm.v[i]) return -1; - if (am.v[i] > bm.v[i]) return 1; - } - return 0; -} -#endif - -/* Take as input a signed62 number in range (-2*modulus,modulus), and add a multiple of the modulus - * to it to bring it to range [0,modulus). If sign < 0, the input will also be negated in the - * process. The input must have limbs in range (-2^62,2^62). The output will have limbs in range - * [0,2^62). */ -static void secp256k1_modinv64_normalize_62(secp256k1_modinv64_signed62 *r, int64_t sign, const secp256k1_modinv64_modinfo *modinfo) { - const int64_t M62 = (int64_t)(UINT64_MAX >> 2); - int64_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4]; - int64_t cond_add, cond_negate; - -#ifdef VERIFY - /* Verify that all limbs are in range (-2^62,2^62). */ - int i; - for (i = 0; i < 5; ++i) { - VERIFY_CHECK(r->v[i] >= -M62); - VERIFY_CHECK(r->v[i] <= M62); - } - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, -2) > 0); /* r > -2*modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 1) < 0); /* r < modulus */ -#endif - - /* In a first step, add the modulus if the input is negative, and then negate if requested. - * This brings r from range (-2*modulus,modulus) to range (-modulus,modulus). As all input - * limbs are in range (-2^62,2^62), this cannot overflow an int64_t. Note that the right - * shifts below are signed sign-extending shifts (see assumptions.h for tests that that is - * indeed the behavior of the right shift operator). */ - cond_add = r4 >> 63; - r0 += modinfo->modulus.v[0] & cond_add; - r1 += modinfo->modulus.v[1] & cond_add; - r2 += modinfo->modulus.v[2] & cond_add; - r3 += modinfo->modulus.v[3] & cond_add; - r4 += modinfo->modulus.v[4] & cond_add; - cond_negate = sign >> 63; - r0 = (r0 ^ cond_negate) - cond_negate; - r1 = (r1 ^ cond_negate) - cond_negate; - r2 = (r2 ^ cond_negate) - cond_negate; - r3 = (r3 ^ cond_negate) - cond_negate; - r4 = (r4 ^ cond_negate) - cond_negate; - /* Propagate the top bits, to bring limbs back to range (-2^62,2^62). */ - r1 += r0 >> 62; r0 &= M62; - r2 += r1 >> 62; r1 &= M62; - r3 += r2 >> 62; r2 &= M62; - r4 += r3 >> 62; r3 &= M62; - - /* In a second step add the modulus again if the result is still negative, bringing - * r to range [0,modulus). */ - cond_add = r4 >> 63; - r0 += modinfo->modulus.v[0] & cond_add; - r1 += modinfo->modulus.v[1] & cond_add; - r2 += modinfo->modulus.v[2] & cond_add; - r3 += modinfo->modulus.v[3] & cond_add; - r4 += modinfo->modulus.v[4] & cond_add; - /* And propagate again. */ - r1 += r0 >> 62; r0 &= M62; - r2 += r1 >> 62; r1 &= M62; - r3 += r2 >> 62; r2 &= M62; - r4 += r3 >> 62; r3 &= M62; - - r->v[0] = r0; - r->v[1] = r1; - r->v[2] = r2; - r->v[3] = r3; - r->v[4] = r4; - -#ifdef VERIFY - VERIFY_CHECK(r0 >> 62 == 0); - VERIFY_CHECK(r1 >> 62 == 0); - VERIFY_CHECK(r2 >> 62 == 0); - VERIFY_CHECK(r3 >> 62 == 0); - VERIFY_CHECK(r4 >> 62 == 0); - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 0) >= 0); /* r >= 0 */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 1) < 0); /* r < modulus */ -#endif -} - -/* Data type for transition matrices (see section 3 of explanation). - * - * t = [ u v ] - * [ q r ] - */ -typedef struct { - int64_t u, v, q, r; -} secp256k1_modinv64_trans2x2; - -/* Compute the transition matrix and eta for 59 divsteps (where zeta=-(delta+1/2)). - * Note that the transformation matrix is scaled by 2^62 and not 2^59. - * - * Input: zeta: initial zeta - * f0: bottom limb of initial f - * g0: bottom limb of initial g - * Output: t: transition matrix - * Return: final zeta - * - * Implements the divsteps_n_matrix function from the explanation. - */ -static int64_t secp256k1_modinv64_divsteps_59(int64_t zeta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t) { - /* u,v,q,r are the elements of the transformation matrix being built up, - * starting with the identity matrix times 8 (because the caller expects - * a result scaled by 2^62). Semantically they are signed integers - * in range [-2^62,2^62], but here represented as unsigned mod 2^64. This - * permits left shifting (which is UB for negative numbers). The range - * being inside [-2^63,2^63) means that casting to signed works correctly. - */ - uint64_t u = 8, v = 0, q = 0, r = 8; - uint64_t c1, c2, f = f0, g = g0, x, y, z; - int i; - - for (i = 3; i < 62; ++i) { - VERIFY_CHECK((f & 1) == 1); /* f must always be odd */ - VERIFY_CHECK((u * f0 + v * g0) == f << i); - VERIFY_CHECK((q * f0 + r * g0) == g << i); - /* Compute conditional masks for (zeta < 0) and for (g & 1). */ - c1 = zeta >> 63; - c2 = -(g & 1); - /* Compute x,y,z, conditionally negated versions of f,u,v. */ - x = (f ^ c1) - c1; - y = (u ^ c1) - c1; - z = (v ^ c1) - c1; - /* Conditionally add x,y,z to g,q,r. */ - g += x & c2; - q += y & c2; - r += z & c2; - /* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */ - c1 &= c2; - /* Conditionally change zeta into -zeta-2 or zeta-1. */ - zeta = (zeta ^ c1) - 1; - /* Conditionally add g,q,r to f,u,v. */ - f += g & c1; - u += q & c1; - v += r & c1; - /* Shifts */ - g >>= 1; - u <<= 1; - v <<= 1; - /* Bounds on zeta that follow from the bounds on iteration count (max 10*59 divsteps). */ - VERIFY_CHECK(zeta >= -591 && zeta <= 591); - } - /* Return data in t and return value. */ - t->u = (int64_t)u; - t->v = (int64_t)v; - t->q = (int64_t)q; - t->r = (int64_t)r; - /* The determinant of t must be a power of two. This guarantees that multiplication with t - * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which - * will be divided out again). As each divstep's individual matrix has determinant 2, the - * aggregate of 59 of them will have determinant 2^59. Multiplying with the initial - * 8*identity (which has determinant 2^6) means the overall outputs has determinant - * 2^65. */ - VERIFY_CHECK((int128_t)t->u * t->r - (int128_t)t->v * t->q == ((int128_t)1) << 65); - return zeta; -} - -/* Compute the transition matrix and eta for 62 divsteps (variable time, eta=-delta). - * - * Input: eta: initial eta - * f0: bottom limb of initial f - * g0: bottom limb of initial g - * Output: t: transition matrix - * Return: final eta - * - * Implements the divsteps_n_matrix_var function from the explanation. - */ -static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t) { - /* Transformation matrix; see comments in secp256k1_modinv64_divsteps_62. */ - uint64_t u = 1, v = 0, q = 0, r = 1; - uint64_t f = f0, g = g0, m; - uint32_t w; - int i = 62, limit, zeros; - - for (;;) { - /* Use a sentinel bit to count zeros only up to i. */ - zeros = secp256k1_ctz64_var(g | (UINT64_MAX << i)); - /* Perform zeros divsteps at once; they all just divide g by two. */ - g >>= zeros; - u <<= zeros; - v <<= zeros; - eta -= zeros; - i -= zeros; - /* We're done once we've done 62 divsteps. */ - if (i == 0) break; - VERIFY_CHECK((f & 1) == 1); - VERIFY_CHECK((g & 1) == 1); - VERIFY_CHECK((u * f0 + v * g0) == f << (62 - i)); - VERIFY_CHECK((q * f0 + r * g0) == g << (62 - i)); - /* Bounds on eta that follow from the bounds on iteration count (max 12*62 divsteps). */ - VERIFY_CHECK(eta >= -745 && eta <= 745); - /* If eta is negative, negate it and replace f,g with g,-f. */ - if (eta < 0) { - uint64_t tmp; - eta = -eta; - tmp = f; f = g; g = -tmp; - tmp = u; u = q; q = -tmp; - tmp = v; v = r; r = -tmp; - /* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled - * out (as we'd be done before that point), and no more than eta+1 can be done as its - * will flip again once that happens. */ - limit = ((int)eta + 1) > i ? i : ((int)eta + 1); - VERIFY_CHECK(limit > 0 && limit <= 62); - /* m is a mask for the bottom min(limit, 6) bits. */ - m = (UINT64_MAX >> (64 - limit)) & 63U; - /* Find what multiple of f must be added to g to cancel its bottom min(limit, 6) - * bits. */ - w = (f * g * (f * f - 2)) & m; - } else { - /* In this branch, use a simpler formula that only lets us cancel up to 4 bits of g, as - * eta tends to be smaller here. */ - limit = ((int)eta + 1) > i ? i : ((int)eta + 1); - VERIFY_CHECK(limit > 0 && limit <= 62); - /* m is a mask for the bottom min(limit, 4) bits. */ - m = (UINT64_MAX >> (64 - limit)) & 15U; - /* Find what multiple of f must be added to g to cancel its bottom min(limit, 4) - * bits. */ - w = f + (((f + 1) & 4) << 1); - w = (-w * g) & m; - } - g += f * w; - q += u * w; - r += v * w; - VERIFY_CHECK((g & m) == 0); - } - /* Return data in t and return value. */ - t->u = (int64_t)u; - t->v = (int64_t)v; - t->q = (int64_t)q; - t->r = (int64_t)r; - /* The determinant of t must be a power of two. This guarantees that multiplication with t - * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which - * will be divided out again). As each divstep's individual matrix has determinant 2, the - * aggregate of 62 of them will have determinant 2^62. */ - VERIFY_CHECK((int128_t)t->u * t->r - (int128_t)t->v * t->q == ((int128_t)1) << 62); - return eta; -} - -/* Compute (t/2^62) * [d, e] mod modulus, where t is a transition matrix scaled by 2^62. - * - * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range - * (-2^62,2^62). - * - * This implements the update_de function from the explanation. - */ -static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp256k1_modinv64_signed62 *e, const secp256k1_modinv64_trans2x2 *t, const secp256k1_modinv64_modinfo* modinfo) { - const int64_t M62 = (int64_t)(UINT64_MAX >> 2); - const int64_t d0 = d->v[0], d1 = d->v[1], d2 = d->v[2], d3 = d->v[3], d4 = d->v[4]; - const int64_t e0 = e->v[0], e1 = e->v[1], e2 = e->v[2], e3 = e->v[3], e4 = e->v[4]; - const int64_t u = t->u, v = t->v, q = t->q, r = t->r; - int64_t md, me, sd, se; - int128_t cd, ce; -#ifdef VERIFY - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, -2) > 0); /* d > -2*modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, 1) < 0); /* d < modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, -2) > 0); /* e > -2*modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, 1) < 0); /* e < modulus */ - VERIFY_CHECK((secp256k1_modinv64_abs(u) + secp256k1_modinv64_abs(v)) >= 0); /* |u|+|v| doesn't overflow */ - VERIFY_CHECK((secp256k1_modinv64_abs(q) + secp256k1_modinv64_abs(r)) >= 0); /* |q|+|r| doesn't overflow */ - VERIFY_CHECK((secp256k1_modinv64_abs(u) + secp256k1_modinv64_abs(v)) <= M62 + 1); /* |u|+|v| <= 2^62 */ - VERIFY_CHECK((secp256k1_modinv64_abs(q) + secp256k1_modinv64_abs(r)) <= M62 + 1); /* |q|+|r| <= 2^62 */ -#endif - /* [md,me] start as zero; plus [u,q] if d is negative; plus [v,r] if e is negative. */ - sd = d4 >> 63; - se = e4 >> 63; - md = (u & sd) + (v & se); - me = (q & sd) + (r & se); - /* Begin computing t*[d,e]. */ - cd = (int128_t)u * d0 + (int128_t)v * e0; - ce = (int128_t)q * d0 + (int128_t)r * e0; - /* Correct md,me so that t*[d,e]+modulus*[md,me] has 62 zero bottom bits. */ - md -= (modinfo->modulus_inv62 * (uint64_t)cd + md) & M62; - me -= (modinfo->modulus_inv62 * (uint64_t)ce + me) & M62; - /* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */ - cd += (int128_t)modinfo->modulus.v[0] * md; - ce += (int128_t)modinfo->modulus.v[0] * me; - /* Verify that the low 62 bits of the computation are indeed zero, and then throw them away. */ - VERIFY_CHECK(((int64_t)cd & M62) == 0); cd >>= 62; - VERIFY_CHECK(((int64_t)ce & M62) == 0); ce >>= 62; - /* Compute limb 1 of t*[d,e]+modulus*[md,me], and store it as output limb 0 (= down shift). */ - cd += (int128_t)u * d1 + (int128_t)v * e1; - ce += (int128_t)q * d1 + (int128_t)r * e1; - if (modinfo->modulus.v[1]) { /* Optimize for the case where limb of modulus is zero. */ - cd += (int128_t)modinfo->modulus.v[1] * md; - ce += (int128_t)modinfo->modulus.v[1] * me; - } - d->v[0] = (int64_t)cd & M62; cd >>= 62; - e->v[0] = (int64_t)ce & M62; ce >>= 62; - /* Compute limb 2 of t*[d,e]+modulus*[md,me], and store it as output limb 1. */ - cd += (int128_t)u * d2 + (int128_t)v * e2; - ce += (int128_t)q * d2 + (int128_t)r * e2; - if (modinfo->modulus.v[2]) { /* Optimize for the case where limb of modulus is zero. */ - cd += (int128_t)modinfo->modulus.v[2] * md; - ce += (int128_t)modinfo->modulus.v[2] * me; - } - d->v[1] = (int64_t)cd & M62; cd >>= 62; - e->v[1] = (int64_t)ce & M62; ce >>= 62; - /* Compute limb 3 of t*[d,e]+modulus*[md,me], and store it as output limb 2. */ - cd += (int128_t)u * d3 + (int128_t)v * e3; - ce += (int128_t)q * d3 + (int128_t)r * e3; - if (modinfo->modulus.v[3]) { /* Optimize for the case where limb of modulus is zero. */ - cd += (int128_t)modinfo->modulus.v[3] * md; - ce += (int128_t)modinfo->modulus.v[3] * me; - } - d->v[2] = (int64_t)cd & M62; cd >>= 62; - e->v[2] = (int64_t)ce & M62; ce >>= 62; - /* Compute limb 4 of t*[d,e]+modulus*[md,me], and store it as output limb 3. */ - cd += (int128_t)u * d4 + (int128_t)v * e4; - ce += (int128_t)q * d4 + (int128_t)r * e4; - cd += (int128_t)modinfo->modulus.v[4] * md; - ce += (int128_t)modinfo->modulus.v[4] * me; - d->v[3] = (int64_t)cd & M62; cd >>= 62; - e->v[3] = (int64_t)ce & M62; ce >>= 62; - /* What remains is limb 5 of t*[d,e]+modulus*[md,me]; store it as output limb 4. */ - d->v[4] = (int64_t)cd; - e->v[4] = (int64_t)ce; -#ifdef VERIFY - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, -2) > 0); /* d > -2*modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, 1) < 0); /* d < modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, -2) > 0); /* e > -2*modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, 1) < 0); /* e < modulus */ -#endif -} - -/* Compute (t/2^62) * [f, g], where t is a transition matrix scaled by 2^62. - * - * This implements the update_fg function from the explanation. - */ -static void secp256k1_modinv64_update_fg_62(secp256k1_modinv64_signed62 *f, secp256k1_modinv64_signed62 *g, const secp256k1_modinv64_trans2x2 *t) { - const int64_t M62 = (int64_t)(UINT64_MAX >> 2); - const int64_t f0 = f->v[0], f1 = f->v[1], f2 = f->v[2], f3 = f->v[3], f4 = f->v[4]; - const int64_t g0 = g->v[0], g1 = g->v[1], g2 = g->v[2], g3 = g->v[3], g4 = g->v[4]; - const int64_t u = t->u, v = t->v, q = t->q, r = t->r; - int128_t cf, cg; - /* Start computing t*[f,g]. */ - cf = (int128_t)u * f0 + (int128_t)v * g0; - cg = (int128_t)q * f0 + (int128_t)r * g0; - /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */ - VERIFY_CHECK(((int64_t)cf & M62) == 0); cf >>= 62; - VERIFY_CHECK(((int64_t)cg & M62) == 0); cg >>= 62; - /* Compute limb 1 of t*[f,g], and store it as output limb 0 (= down shift). */ - cf += (int128_t)u * f1 + (int128_t)v * g1; - cg += (int128_t)q * f1 + (int128_t)r * g1; - f->v[0] = (int64_t)cf & M62; cf >>= 62; - g->v[0] = (int64_t)cg & M62; cg >>= 62; - /* Compute limb 2 of t*[f,g], and store it as output limb 1. */ - cf += (int128_t)u * f2 + (int128_t)v * g2; - cg += (int128_t)q * f2 + (int128_t)r * g2; - f->v[1] = (int64_t)cf & M62; cf >>= 62; - g->v[1] = (int64_t)cg & M62; cg >>= 62; - /* Compute limb 3 of t*[f,g], and store it as output limb 2. */ - cf += (int128_t)u * f3 + (int128_t)v * g3; - cg += (int128_t)q * f3 + (int128_t)r * g3; - f->v[2] = (int64_t)cf & M62; cf >>= 62; - g->v[2] = (int64_t)cg & M62; cg >>= 62; - /* Compute limb 4 of t*[f,g], and store it as output limb 3. */ - cf += (int128_t)u * f4 + (int128_t)v * g4; - cg += (int128_t)q * f4 + (int128_t)r * g4; - f->v[3] = (int64_t)cf & M62; cf >>= 62; - g->v[3] = (int64_t)cg & M62; cg >>= 62; - /* What remains is limb 5 of t*[f,g]; store it as output limb 4. */ - f->v[4] = (int64_t)cf; - g->v[4] = (int64_t)cg; -} - -/* Compute (t/2^62) * [f, g], where t is a transition matrix for 62 divsteps. - * - * Version that operates on a variable number of limbs in f and g. - * - * This implements the update_fg function from the explanation. - */ -static void secp256k1_modinv64_update_fg_62_var(int len, secp256k1_modinv64_signed62 *f, secp256k1_modinv64_signed62 *g, const secp256k1_modinv64_trans2x2 *t) { - const int64_t M62 = (int64_t)(UINT64_MAX >> 2); - const int64_t u = t->u, v = t->v, q = t->q, r = t->r; - int64_t fi, gi; - int128_t cf, cg; - int i; - VERIFY_CHECK(len > 0); - /* Start computing t*[f,g]. */ - fi = f->v[0]; - gi = g->v[0]; - cf = (int128_t)u * fi + (int128_t)v * gi; - cg = (int128_t)q * fi + (int128_t)r * gi; - /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */ - VERIFY_CHECK(((int64_t)cf & M62) == 0); cf >>= 62; - VERIFY_CHECK(((int64_t)cg & M62) == 0); cg >>= 62; - /* Now iteratively compute limb i=1..len of t*[f,g], and store them in output limb i-1 (shifting - * down by 62 bits). */ - for (i = 1; i < len; ++i) { - fi = f->v[i]; - gi = g->v[i]; - cf += (int128_t)u * fi + (int128_t)v * gi; - cg += (int128_t)q * fi + (int128_t)r * gi; - f->v[i - 1] = (int64_t)cf & M62; cf >>= 62; - g->v[i - 1] = (int64_t)cg & M62; cg >>= 62; - } - /* What remains is limb (len) of t*[f,g]; store it as output limb (len-1). */ - f->v[len - 1] = (int64_t)cf; - g->v[len - 1] = (int64_t)cg; -} - -/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (constant time in x). */ -static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) { - /* Start with d=0, e=1, f=modulus, g=x, zeta=-1. */ - secp256k1_modinv64_signed62 d = {{0, 0, 0, 0, 0}}; - secp256k1_modinv64_signed62 e = {{1, 0, 0, 0, 0}}; - secp256k1_modinv64_signed62 f = modinfo->modulus; - secp256k1_modinv64_signed62 g = *x; - int i; - int64_t zeta = -1; /* zeta = -(delta+1/2); delta starts at 1/2. */ - - /* Do 10 iterations of 59 divsteps each = 590 divsteps. This suffices for 256-bit inputs. */ - for (i = 0; i < 10; ++i) { - /* Compute transition matrix and new zeta after 59 divsteps. */ - secp256k1_modinv64_trans2x2 t; - zeta = secp256k1_modinv64_divsteps_59(zeta, f.v[0], g.v[0], &t); - /* Update d,e using that transition matrix. */ - secp256k1_modinv64_update_de_62(&d, &e, &t, modinfo); - /* Update f,g using that transition matrix. */ -#ifdef VERIFY - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) > 0); /* f > -modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) <= 0); /* f <= modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, -1) > 0); /* g > -modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, 1) < 0); /* g < modulus */ -#endif - secp256k1_modinv64_update_fg_62(&f, &g, &t); -#ifdef VERIFY - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) > 0); /* f > -modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) <= 0); /* f <= modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, -1) > 0); /* g > -modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, 1) < 0); /* g < modulus */ -#endif - } - - /* At this point sufficient iterations have been performed that g must have reached 0 - * and (if g was not originally 0) f must now equal +/- GCD of the initial f, g - * values i.e. +/- 1, and d now contains +/- the modular inverse. */ -#ifdef VERIFY - /* g == 0 */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &SECP256K1_SIGNED62_ONE, 0) == 0); - /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &SECP256K1_SIGNED62_ONE, -1) == 0 || - secp256k1_modinv64_mul_cmp_62(&f, 5, &SECP256K1_SIGNED62_ONE, 1) == 0 || - (secp256k1_modinv64_mul_cmp_62(x, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 && - secp256k1_modinv64_mul_cmp_62(&d, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 && - (secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) == 0 || - secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) == 0))); -#endif - - /* Optionally negate d, normalize to [0,modulus), and return it. */ - secp256k1_modinv64_normalize_62(&d, f.v[4], modinfo); - *x = d; -} - -/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (variable time). */ -static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) { - /* Start with d=0, e=1, f=modulus, g=x, eta=-1. */ - secp256k1_modinv64_signed62 d = {{0, 0, 0, 0, 0}}; - secp256k1_modinv64_signed62 e = {{1, 0, 0, 0, 0}}; - secp256k1_modinv64_signed62 f = modinfo->modulus; - secp256k1_modinv64_signed62 g = *x; -#ifdef VERIFY - int i = 0; -#endif - int j, len = 5; - int64_t eta = -1; /* eta = -delta; delta is initially 1 */ - int64_t cond, fn, gn; - - /* Do iterations of 62 divsteps each until g=0. */ - while (1) { - /* Compute transition matrix and new eta after 62 divsteps. */ - secp256k1_modinv64_trans2x2 t; - eta = secp256k1_modinv64_divsteps_62_var(eta, f.v[0], g.v[0], &t); - /* Update d,e using that transition matrix. */ - secp256k1_modinv64_update_de_62(&d, &e, &t, modinfo); - /* Update f,g using that transition matrix. */ -#ifdef VERIFY - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ -#endif - secp256k1_modinv64_update_fg_62_var(len, &f, &g, &t); - /* If the bottom limb of g is zero, there is a chance that g=0. */ - if (g.v[0] == 0) { - cond = 0; - /* Check if the other limbs are also 0. */ - for (j = 1; j < len; ++j) { - cond |= g.v[j]; - } - /* If so, we're done. */ - if (cond == 0) break; - } - - /* Determine if len>1 and limb (len-1) of both f and g is 0 or -1. */ - fn = f.v[len - 1]; - gn = g.v[len - 1]; - cond = ((int64_t)len - 2) >> 63; - cond |= fn ^ (fn >> 63); - cond |= gn ^ (gn >> 63); - /* If so, reduce length, propagating the sign of f and g's top limb into the one below. */ - if (cond == 0) { - f.v[len - 2] |= (uint64_t)fn << 62; - g.v[len - 2] |= (uint64_t)gn << 62; - --len; - } -#ifdef VERIFY - VERIFY_CHECK(++i < 12); /* We should never need more than 12*62 = 744 divsteps */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ -#endif - } - - /* At this point g is 0 and (if g was not originally 0) f must now equal +/- GCD of - * the initial f, g values i.e. +/- 1, and d now contains +/- the modular inverse. */ -#ifdef VERIFY - /* g == 0 */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &SECP256K1_SIGNED62_ONE, 0) == 0); - /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */ - VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &SECP256K1_SIGNED62_ONE, -1) == 0 || - secp256k1_modinv64_mul_cmp_62(&f, len, &SECP256K1_SIGNED62_ONE, 1) == 0 || - (secp256k1_modinv64_mul_cmp_62(x, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 && - secp256k1_modinv64_mul_cmp_62(&d, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 && - (secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) == 0 || - secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) == 0))); -#endif - - /* Optionally negate d, normalize to [0,modulus), and return it. */ - secp256k1_modinv64_normalize_62(&d, f.v[len - 1], modinfo); - *x = d; -} - -#endif /* SECP256K1_MODINV64_IMPL_H */ diff --git a/libwallet/musig/musig.go b/libwallet/musig/musig.go deleted file mode 100644 index 73641479..00000000 --- a/libwallet/musig/musig.go +++ /dev/null @@ -1,591 +0,0 @@ -package musig - -// #include -// #include "umbrella.h" -// #cgo CFLAGS: -DECMULT_WINDOW_SIZE=15 -DECMULT_GEN_PREC_BITS=4 -DSECP256K1_BUILD -import "C" -import ( - "bytes" - "crypto/rand" - "fmt" - "unsafe" - - "github.com/btcsuite/btcd/btcec" - "github.com/muun/libwallet/btcsuitew/chainhashw" -) - -func toUchar(buf []byte) *C.uchar { - // See https://stackoverflow.com/a/51428826 on why this is needed - var bufptr *byte - if cap(buf) > 0 { - bufptr = &(buf[:1][0]) - } - return (*C.uchar)(bufptr) -} - -var ctx *C.struct_secp256k1_context_struct - -func init() { - ctx = C.secp256k1_context_create(C.SECP256K1_CONTEXT_SIGN | C.SECP256K1_CONTEXT_VERIFY) - // TODO: consider using secp256k1_context_set_illegal_callback -} - -func CombinePubKeysWithTweak(userKey, muunKey *btcec.PublicKey, customTweak []byte) (*btcec.PublicKey, error) { - combined, err := combinePubKeys(userKey, muunKey) - if err != nil { - return nil, err - } - - tweak, err := tagTweakOrDefault(combined, customTweak) - if err != nil { - return nil, err - } - - var tweakPubKey C.secp256k1_pubkey - if C.secp256k1_xonly_pubkey_tweak_add( - ctx, - &tweakPubKey, - combined, - toUchar(tweak[:]), - ) == 0 { - return nil, fmt.Errorf("failed to tweak key") - } - - var serialized [33]byte - var serializedSize C.size_t = 33 - if C.secp256k1_ec_pubkey_serialize( - ctx, - toUchar(serialized[:]), - &serializedSize, - &tweakPubKey, - C.SECP256K1_EC_COMPRESSED, - ) == 0 { - return nil, fmt.Errorf("failed to serialize tweaked key") - } - - return btcec.ParsePubKey(serialized[:], btcec.S256()) -} - -func combinePubKeys(userKey *btcec.PublicKey, muunKey *btcec.PublicKey) (*C.secp256k1_xonly_pubkey, error) { - - // Safe C-interop rules require C pointer (ie the array) can't contain go - // pointers. These go into an array, so we need to allocate manually. - userXOnly := (*C.secp256k1_xonly_pubkey)(C.malloc(C.sizeof_secp256k1_xonly_pubkey)) - defer C.free(unsafe.Pointer(userXOnly)) - - muunXOnly := (*C.secp256k1_xonly_pubkey)(C.malloc(C.sizeof_secp256k1_xonly_pubkey)) - defer C.free(unsafe.Pointer(muunXOnly)) - - if C.secp256k1_xonly_pubkey_parse(ctx, userXOnly, toUchar(userKey.SerializeCompressed()[1:])) == 0 { - return nil, fmt.Errorf("failed to parse user key") - } - - if C.secp256k1_xonly_pubkey_parse(ctx, muunXOnly, toUchar(muunKey.SerializeCompressed()[1:])) == 0 { - return nil, fmt.Errorf("failed to parse muun key") - } - - keys := []*C.secp256k1_xonly_pubkey{ - userXOnly, - muunXOnly, - } - - var combined C.secp256k1_xonly_pubkey - if C.secp256k1_musig_pubkey_agg( - ctx, - nil, - &combined, - nil, - (**C.secp256k1_xonly_pubkey)(&keys[:1][0]), - 2, - ) == 0 { - return nil, fmt.Errorf("failed to combne keys") - } - - return &combined, nil -} - -// RandomSessionId returns a safe random session id. Session IDs must not be -// repeated otherwise private keys are compromised. -func RandomSessionId() [32]byte { - var buf [32]byte - _, err := rand.Read(buf[:]) - if err != nil { - panic("couldn't read random bytes") - } - - return buf -} - -// GeneratePubNonce returns the pub nonce for a given session id -func GeneratePubNonce(sessionId [32]byte) [66]byte { - - var secnonce C.secp256k1_musig_secnonce - var pubNonce C.secp256k1_musig_pubnonce - - res := C.secp256k1_musig_nonce_gen( - ctx, - &secnonce, - &pubNonce, - toUchar(sessionId[:]), - nil, - nil, - nil, - nil, - ) - - if res == 0 { - panic("failed to generate nonce") - } - - var pubNonceBytes [66]byte - res = C.secp256k1_musig_pubnonce_serialize( - ctx, - toUchar(pubNonceBytes[:]), - &pubNonce, - ) - if res == 0 { - panic("failed to serialize pub nonce") - } - - return pubNonceBytes -} - -// AddUserSignatureAndCombine with partial muun signature. -func AddUserSignatureAndCombine( - data [32]byte, - userKey *btcec.PrivateKey, - muunKey *btcec.PublicKey, - rawMuunPartialSig [32]byte, - rawMuunPubNonce [66]byte, - sessionId [32]byte, - customTweak []byte, -) ([64]byte, error) { - - var signature [64]byte - - // Safe C-interop rules require C pointer (ie the array) can't contain go - // pointers. These go into an array, so we need to allocate manually. - userXOnly := (*C.secp256k1_xonly_pubkey)(C.malloc(C.sizeof_secp256k1_xonly_pubkey)) - defer C.free(unsafe.Pointer(userXOnly)) - - muunXOnly := (*C.secp256k1_xonly_pubkey)(C.malloc(C.sizeof_secp256k1_xonly_pubkey)) - defer C.free(unsafe.Pointer(muunXOnly)) - - if C.secp256k1_xonly_pubkey_parse( - ctx, - userXOnly, - toUchar(userKey.PubKey().SerializeCompressed()[1:]), - ) == 0 { - return signature, fmt.Errorf("failed to make xonly from user key") - } - - if C.secp256k1_xonly_pubkey_parse( - ctx, - muunXOnly, - toUchar(muunKey.SerializeCompressed()[1:]), - ) == 0 { - return signature, fmt.Errorf("failed to make xonly from user key") - } - - keys := []*C.secp256k1_xonly_pubkey{ - userXOnly, - muunXOnly, - } - - var combined C.secp256k1_xonly_pubkey - var keyaggCache C.secp256k1_musig_keyagg_cache - if C.secp256k1_musig_pubkey_agg( - ctx, - nil, - &combined, - &keyaggCache, - (**C.secp256k1_xonly_pubkey)(&keys[:1][0]), - 2, - ) == 0 { - return signature, fmt.Errorf("failed to combine keys") - } - - var secnonce C.secp256k1_musig_secnonce - userPubNonce := (*C.secp256k1_musig_pubnonce)(C.malloc(C.sizeof_secp256k1_musig_pubnonce)) - defer C.free(unsafe.Pointer(userPubNonce)) - - if C.secp256k1_musig_nonce_gen( - ctx, - &secnonce, - userPubNonce, - toUchar(sessionId[:]), - nil, - nil, - nil, - nil, - ) == 0 { - return signature, fmt.Errorf("failed to generate user nonce") - } - - muunPubNonce := (*C.secp256k1_musig_pubnonce)(C.malloc(C.sizeof_secp256k1_musig_pubnonce)) - defer C.free(unsafe.Pointer(muunPubNonce)) - - if C.secp256k1_musig_pubnonce_parse( - ctx, - muunPubNonce, - toUchar(rawMuunPubNonce[:]), - ) == 0 { - return signature, fmt.Errorf("failed to parse muun pub nonce") - } - - tweak, err := tagTweakOrDefault(&combined, customTweak) - if err != nil { - return signature, err - } - - var tweakedPubKey C.secp256k1_pubkey - if C.secp256k1_musig_pubkey_tweak_add( - ctx, - &tweakedPubKey, - toUchar(tweak[:]), - &keyaggCache, - ) == 0 { - return signature, fmt.Errorf("failed to tweak key") - } - - // The API is kinda unhappy, and now requires us to transform the - // tweaked pub key to x-only and overwrite the previous combined key - if C.secp256k1_xonly_pubkey_from_pubkey( - ctx, - &combined, - nil, - &tweakedPubKey, - ) == 0 { - return signature, fmt.Errorf("failed to transform tweaked key to xonly") - } - - var aggNonce C.secp256k1_musig_aggnonce - - pubNonces := []*C.secp256k1_musig_pubnonce{ - userPubNonce, - muunPubNonce, - } - if C.secp256k1_musig_nonce_agg( - ctx, - &aggNonce, - (**C.secp256k1_musig_pubnonce)(&pubNonces[:1][0]), - 2, - ) == 0 { - return signature, fmt.Errorf("failed to aggregate nonces") - } - - var session C.secp256k1_musig_session - - if C.secp256k1_musig_nonce_process( - ctx, - &session, - &aggNonce, - toUchar(data[:]), - &keyaggCache, - nil, - ) == 0 { - return signature, fmt.Errorf("failed to process nonces") - } - - // Heap allocated since it will go in an array soon - muunPartialSig := (*C.secp256k1_musig_partial_sig)( - C.malloc(C.sizeof_secp256k1_musig_partial_sig), - ) - defer C.free(unsafe.Pointer(muunPartialSig)) - - if C.secp256k1_musig_partial_sig_parse( - ctx, - muunPartialSig, - toUchar(rawMuunPartialSig[:]), - ) == 0 { - return signature, fmt.Errorf("failed to parse muun partial sig") - } - - if C.secp256k1_musig_partial_sig_verify( - ctx, - muunPartialSig, - pubNonces[1], - muunXOnly, - &keyaggCache, - &session, - ) == 0 { - return signature, fmt.Errorf("partial sig is invalid") - } - - var userKeyPair C.secp256k1_keypair - if C.secp256k1_keypair_create( - ctx, - &userKeyPair, - toUchar(userKey.Serialize()), - ) == 0 { - return signature, fmt.Errorf("failed to create user key pair") - } - - // Heap allocated since it will go in an array soon - userPartialSig := (*C.secp256k1_musig_partial_sig)( - C.malloc(C.sizeof_secp256k1_musig_partial_sig), - ) - defer C.free(unsafe.Pointer(userPartialSig)) - - if C.secp256k1_musig_partial_sign( - ctx, - userPartialSig, - &secnonce, - &userKeyPair, - &keyaggCache, - &session, - ) == 0 { - return signature, fmt.Errorf("failed to sign with user key") - } - - partialSigs := []*C.secp256k1_musig_partial_sig{ - userPartialSig, muunPartialSig, - } - - if C.secp256k1_musig_partial_sig_agg( - ctx, - toUchar(signature[:]), - &session, - (**C.secp256k1_musig_partial_sig)(&partialSigs[:1][0]), - 2, - ) == 0 { - return signature, fmt.Errorf("failed to combine signatures") - } - - return signature, nil -} - -func ComputeMuunPartialSignature( - data [32]byte, - userKey *btcec.PublicKey, - muunKey *btcec.PrivateKey, - rawUserPubNonce [66]byte, - sessionId [32]byte, - customTweak []byte, -) ([32]byte, error) { - var rawPartialMuunSig [32]byte - - // Safe C-interop rules require C pointer (ie the array) can't contain go - // pointers. These go into an array, so we need to allocate manually. - userXOnly := (*C.secp256k1_xonly_pubkey)( - C.malloc(C.sizeof_secp256k1_xonly_pubkey), - ) - defer C.free(unsafe.Pointer(userXOnly)) - - muunXOnly := (*C.secp256k1_xonly_pubkey)( - C.malloc(C.sizeof_secp256k1_xonly_pubkey), - ) - defer C.free(unsafe.Pointer(muunXOnly)) - - if C.secp256k1_xonly_pubkey_parse( - ctx, - userXOnly, - toUchar(userKey.SerializeCompressed()[1:]), - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to make xonly from user key") - } - - if C.secp256k1_xonly_pubkey_parse( - ctx, - muunXOnly, - toUchar(muunKey.PubKey().SerializeCompressed()[1:]), - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to make xonly from user key") - } - - keys := []*C.secp256k1_xonly_pubkey{ - userXOnly, - muunXOnly, - } - - var combined C.secp256k1_xonly_pubkey - var keyaggCache C.secp256k1_musig_keyagg_cache - if C.secp256k1_musig_pubkey_agg( - ctx, - nil, - &combined, - &keyaggCache, - (**C.secp256k1_xonly_pubkey)(&keys[:1][0]), - 2, - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to combine keys") - } - - var secnonce C.secp256k1_musig_secnonce - muunPubNonce := (*C.secp256k1_musig_pubnonce)(C.malloc(C.sizeof_secp256k1_musig_pubnonce)) - - if C.secp256k1_musig_nonce_gen( - ctx, - &secnonce, - muunPubNonce, - toUchar(sessionId[:]), - nil, - nil, - nil, - nil, - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to create pre session") - } - - userPubNonce := (*C.secp256k1_musig_pubnonce)(C.malloc(C.sizeof_secp256k1_musig_pubnonce)) - defer C.free(unsafe.Pointer(userPubNonce)) - - if C.secp256k1_musig_pubnonce_parse( - ctx, - userPubNonce, - toUchar(rawUserPubNonce[:]), - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to parse muun pub nonce") - } - - tweak, err := tagTweakOrDefault(&combined, customTweak) - if err != nil { - return rawPartialMuunSig, err - } - - var tweakedPubKey C.secp256k1_pubkey - if C.secp256k1_musig_pubkey_tweak_add( - ctx, - &tweakedPubKey, - toUchar(tweak[:]), - &keyaggCache, - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to tweak key") - } - - // The API is kinda unhappy, and now requires us to transform the - // tweaked pub key to x-only and overwrite the previous combined key - if C.secp256k1_xonly_pubkey_from_pubkey( - ctx, - &combined, - nil, - &tweakedPubKey, - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to transform tweaked key to xonly") - } - - var aggNonce C.secp256k1_musig_aggnonce - - pubNonces := []*C.secp256k1_musig_pubnonce{ - userPubNonce, - muunPubNonce, - } - if C.secp256k1_musig_nonce_agg( - ctx, - &aggNonce, - (**C.secp256k1_musig_pubnonce)(&pubNonces[:1][0]), - 2, - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to aggregate nonces") - } - - var session C.secp256k1_musig_session - - if C.secp256k1_musig_nonce_process( - ctx, - &session, - &aggNonce, - toUchar(data[:]), - &keyaggCache, - nil, - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to process nonces") - } - - var muunKeyPair C.secp256k1_keypair - if C.secp256k1_keypair_create( - ctx, - &muunKeyPair, - toUchar(muunKey.Serialize()), - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to create user key pair") - } - - var muunPartialSig C.secp256k1_musig_partial_sig - if C.secp256k1_musig_partial_sign( - ctx, - &muunPartialSig, - &secnonce, - &muunKeyPair, - &keyaggCache, - &session, - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to sign with muun key") - } - - // Here to catch bugs! - if C.secp256k1_musig_partial_sig_verify( - ctx, - &muunPartialSig, - muunPubNonce, - muunXOnly, - &keyaggCache, - &session, - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("partial sig is invalid") - } - - if C.secp256k1_musig_partial_sig_serialize( - ctx, - toUchar(rawPartialMuunSig[:]), - &muunPartialSig, - ) == 0 { - return rawPartialMuunSig, fmt.Errorf("failed to serialize partial sig") - } - - return rawPartialMuunSig, nil -} - -// VerifySignature checks a Schnorr signature. -func VerifySignature(data [32]byte, signature [64]byte, pubKey *btcec.PublicKey) bool { - var xOnly C.secp256k1_xonly_pubkey - if C.secp256k1_xonly_pubkey_parse( - ctx, - &xOnly, - toUchar(pubKey.SerializeCompressed()[1:]), - ) == 0 { - return false - } - - return C.secp256k1_schnorrsig_verify( - ctx, - toUchar(signature[:]), - toUchar(data[:]), - 32, - &xOnly, - ) == 1 -} - -func tagTweakOrDefault(pubKey *C.secp256k1_xonly_pubkey, customTweak []byte) ([32]byte, error) { - var untaggedTweak []byte - - if customTweak != nil { - if len(customTweak) != 32 { - return [32]byte{}, fmt.Errorf("tweak must be 32 bytes long, not %d", len(customTweak)) - } - - var emptyTweak [32]byte - if bytes.Equal(customTweak, emptyTweak[:]) { - return [32]byte{}, fmt.Errorf("tweak can't be empty (zero-filled slice given)") - } - - untaggedTweak = customTweak[:] - - } else { - var serializedKey [32]byte - if C.secp256k1_xonly_pubkey_serialize( - ctx, - toUchar(serializedKey[:]), - pubKey, - ) == 0 { - return [32]byte{}, fmt.Errorf("failed to serialize key to calculate default tweak") - } - - untaggedTweak = serializedKey[:] - } - - var taggedTweak [32]byte - copy(taggedTweak[:], chainhashw.TaggedHashB(chainhashw.TagTapTweak, untaggedTweak)) - - return taggedTweak, nil -} diff --git a/libwallet/musig/musig2.go b/libwallet/musig/musig2.go new file mode 100644 index 00000000..6987da49 --- /dev/null +++ b/libwallet/musig/musig2.go @@ -0,0 +1,485 @@ +package musig + +// This file contains generic adapters for both versions musig2v040 and v100 + +import ( + "bytes" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + musig2v100 "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/lightningnetwork/lnd/input" + "github.com/muun/libwallet/musig2v040" +) + +type MusigVersion uint8 + +const ( + // Muun's variant of MuSig2 based on secp256k1_zkp implementation + // at commit https://github.com/jonasnick/secp256k1-zkp/tree/0aeaa5dfb19445f845890f3a4502c934550f4548 + // and nonces calculated with random entropy from sessionId (only). + // - not null scriptPath are not spendable with this implementation + // - key sorting is disabled, the order [user,muun] is enforced + // - xOnly keys are used + // - tapscript is not spendable + Musig2v040Muun MusigVersion = 40 + + // version 1.0.0rc2 of the MuSig2 BIP draft. + // It uses the github.com/btcsuite/btcd/btcec/v2/schnorr/musig2 package + // imported by go.mod + Musig2v100 MusigVersion = 100 +) + +func MuSig2GenerateNonce( + musigVersion MusigVersion, + sessionId []byte, + publicKeyBytes []byte, +) (*musig2v100.Nonces, error) { + + switch musigVersion { + case Musig2v040Muun: + return musig2v040.GenNonces( + musig2v040.WithCustomRand( + bytes.NewBuffer(sessionId), + ), + ) + + case Musig2v100: + if len(publicKeyBytes) == 0 { + return nil, fmt.Errorf("a public key must be provided to generate nonces for MuSig2v100") + } + + publicKey, err := ParsePubKey(musigVersion, publicKeyBytes) + if err != nil { + return nil, err + } + + return musig2v100.GenNonces( + musig2v100.WithPublicKey(publicKey), + musig2v100.WithCustomRand(bytes.NewBuffer(sessionId)), + ) + default: + return nil, fmt.Errorf("unknown address version: <%d>", + musigVersion) + } +} + +// ParsePubKey forces the kind of PublicKey needed for each MuSig version +func ParsePubKey(musigVersion MusigVersion, pubKeyBytes []byte) (*btcec.PublicKey, error) { + switch musigVersion { + case Musig2v040Muun: + var ( + pubKey *btcec.PublicKey + err error + ) + + if len(pubKeyBytes) == 33 { + // if the not xOnly compressed was provided, then remove the + // parity bit + pubKey, err = schnorr.ParsePubKey(pubKeyBytes[1:]) + if err != nil { + return nil, fmt.Errorf( + "error parsing public key for v0.4.0 (compressed format): %v", + err, + ) + } + } else { + pubKey, err = schnorr.ParsePubKey(pubKeyBytes) + if err != nil { + return nil, fmt.Errorf( + "error parsing public key for v0.4.0 (x-only format): %v", + err, + ) + } + } + + return pubKey, nil + case Musig2v100: + pubKey, err := btcec.ParsePubKey(pubKeyBytes) + if err != nil { + return nil, fmt.Errorf("error parsing public key for v1.0.0 ("+ + "compressed format): %v", err) + } + return pubKey, nil + default: + return nil, fmt.Errorf("unknown MuSig2 version: <%d>", + musigVersion) + } +} + +// MuSig2ParsePubKeys parses a list of raw public keys as the signing keys of a +// MuSig2 signing session. +func MuSig2ParsePubKeys(musigVersion MusigVersion, + rawPubKeys [][]byte) ([]*btcec.PublicKey, error) { + + allSignerPubKeys := make([]*btcec.PublicKey, len(rawPubKeys)) + if len(rawPubKeys) < 2 { + return nil, fmt.Errorf("need at least two signing public keys") + } + + for idx, pubKeyBytes := range rawPubKeys { + pubKey, err := ParsePubKey(musigVersion, pubKeyBytes) + if err != nil { + return nil, fmt.Errorf("error parsing signer "+ + "public key %d: %v", idx, err) + } + allSignerPubKeys[idx] = pubKey + } + + return allSignerPubKeys, nil +} + +// Computes the tweakedKey using a TapScript.merkleRoot or empty bytes as +// recommended by BIP0086. +// The tweakedKey is used to generate the output address: Bech32m(tweakedKey) +func Musig2CombinePubKeysWithTweak( + musigVersion MusigVersion, + pubKeys [][]byte, + tweaks *MuSig2Tweaks, +) (*musig2v100.AggregateKey, error) { + + keys, err := MuSig2ParsePubKeys(musigVersion, pubKeys) + if err != nil { + return nil, err + } + + return MuSig2CombineKeys(musigVersion, keys, tweaks) +} + +// MuSig2CombineKeys combines the given set of public keys into a single +// combined MuSig2 combined public key, applying the given tweaks. +func MuSig2CombineKeys(musigVersion MusigVersion, + allSignerPubKeys []*btcec.PublicKey, + tweaks *MuSig2Tweaks) (*musig2v100.AggregateKey, error) { + + sortKeys := musigVersion != Musig2v040Muun + + switch musigVersion { + case Musig2v040Muun: + return combineKeysV040(allSignerPubKeys, sortKeys, tweaks) + + case Musig2v100: + return combineKeysV100RC2(allSignerPubKeys, sortKeys, tweaks) + + default: + return nil, fmt.Errorf("unknown MuSig2 version: <%d>", + musigVersion) + } +} + +// combineKeysV100rc1 implements the MuSigCombineKeys logic for the MuSig2 BIP +// draft version 1.0.0rc2. +func combineKeysV100RC2(allSignerPubKeys []*btcec.PublicKey, sortKeys bool, + tweaks *MuSig2Tweaks) (*musig2v100.AggregateKey, error) { + + // Convert the tweak options into the appropriate MuSig2 API functional + // options. + var keyAggOpts []musig2v100.KeyAggOption + switch { + case tweaks.TaprootBIP0086Tweak: + keyAggOpts = append(keyAggOpts, musig2v100.WithBIP86KeyTweak()) + case len(tweaks.TaprootTweak) > 0: + keyAggOpts = append(keyAggOpts, musig2v100.WithTaprootKeyTweak( + tweaks.TaprootTweak, + )) + case len(tweaks.GenericTweaks) > 0: + keyAggOpts = append(keyAggOpts, musig2v100.WithKeyTweaks( + tweaks.GenericTweaks..., + )) + case len(tweaks.UnhardenedDerivationPath) > 0: + bip328tweaks, err := getKeyDerivationTweaksForMusig( + allSignerPubKeys, tweaks.UnhardenedDerivationPath) + if err != nil { + return nil, err + } + keyAggOpts = append(keyAggOpts, musig2v100.WithKeyTweaks( + bip328tweaks..., + )) + } + + // Then we'll use this information to compute the aggregated public key. + combinedKey, _, _, err := musig2v100.AggregateKeys( + allSignerPubKeys, sortKeys, keyAggOpts..., + ) + return combinedKey, err +} + +// returns a list of generic tweaks to derive a specific unhardened key for Musig2v100 +// as per BIP32 + BIP328 +func getKeyDerivationTweaksForMusig( + allSignerPubKeys []*btcec.PublicKey, + unhardenedDerivationPath []uint32, +) ([]musig2v100.KeyTweakDesc, error) { + + aggregatedKey, err := MuSig2CombineKeys( + Musig2v100, allSignerPubKeys, NoopTweak()) + if err != nil { + return nil, err + } + + _, tweakDerivationSteps, err := getBip32TweaksForAggregatedKey( + aggregatedKey.PreTweakedKey, unhardenedDerivationPath) + if err != nil { + return nil, err + } + + return tweakDerivationSteps, nil +} + +// combineKeysV040 implements the MuSigCombineKeys logic for the MuSig2 BIP +// draft version 0.4.0. +func combineKeysV040(allSignerPubKeys []*btcec.PublicKey, sortKeys bool, + tweaks *MuSig2Tweaks) (*musig2v100.AggregateKey, error) { + + // Convert the tweak options into the appropriate MuSig2 API functional + // options. + var keyAggOpts []musig2v040.KeyAggOption + switch { + case tweaks.TaprootBIP0086Tweak: + keyAggOpts = append(keyAggOpts, musig2v040.WithBIP86KeyTweak()) + case len(tweaks.TaprootTweak) > 0: + return nil, fmt.Errorf( + "taproot tweak bytes are not allowed for MuSig2v040Muun") + case len(tweaks.GenericTweaks) > 0: + return nil, fmt.Errorf( + "generic tweaks are not available for Musig2v040Muun") + case len(tweaks.UnhardenedDerivationPath) > 0: + return nil, fmt.Errorf( + "unhardened derivation is not available for Musig2v040Muun") + } + + // Then we'll use this information to compute the aggregated public key. + combinedKey, _, _, err := musig2v040.AggregateKeys( + allSignerPubKeys, sortKeys, keyAggOpts..., + ) + + // Copy the result back into the default version's native type. + return &musig2v100.AggregateKey{ + FinalKey: combinedKey.FinalKey, + PreTweakedKey: combinedKey.PreTweakedKey, + }, err +} + +// MuSig2CreateContext creates a new MuSig2 signing context. +func MuSig2CreateContext( + musigVersion MusigVersion, + privKey *btcec.PrivateKey, + allSignerPubKeys []*btcec.PublicKey, + tweaks *MuSig2Tweaks, + localNonces *musig2v100.Nonces, +) (input.MuSig2Context, input.MuSig2Session, error) { + + switch musigVersion { + case Musig2v040Muun: + if len(tweaks.UnhardenedDerivationPath) > 0 { + return nil, nil, fmt.Errorf( + "unhardened derivation is not available for Musig2v040Muun") + } + + if len(tweaks.TaprootTweak) > 0 { + return nil, nil, fmt.Errorf( + "taproot tweak bytes are not allowed for MuSig2v040Muun") + } + + return createContextV040( + privKey, allSignerPubKeys, tweaks, localNonces, + ) + + case Musig2v100: + return createContextV100RC2( + privKey, allSignerPubKeys, tweaks, localNonces, + ) + + default: + return nil, nil, fmt.Errorf("unknown MuSig2 : <%d>", + musigVersion) + } +} + +// createContextV100RC2 implements the MuSig2CreateContext logic for the MuSig2 +// BIP draft version 1.0.0rc2. +func createContextV100RC2( + privKey *btcec.PrivateKey, + allSignerPubKeys []*btcec.PublicKey, + tweaks *MuSig2Tweaks, + localNonces *musig2v100.Nonces, +) (*musig2v100.Context, *musig2v100.Session, error) { + + if localNonces == nil { + return nil, nil, fmt.Errorf("error creating MuSig2 signing " + + "context: localNonces must be provided") + } + + // The context keeps track of all signing keys and our local key. + options, err := tweaks.ToContextOptions(allSignerPubKeys) + if err != nil { + return nil, nil, err + } + allOpts := append(options, musig2v100.WithKnownSigners(allSignerPubKeys)) + muSigContext, err := musig2v100.NewContext(privKey, true, allOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating MuSig2 signing "+ + "context: %v", err) + } + + muSigSession, err := muSigContext.NewSession( + musig2v100.WithPreGeneratedNonce(localNonces), + ) + if err != nil { + return nil, nil, fmt.Errorf("error creating MuSig2 signing "+ + "session: %v", err) + } + + return muSigContext, muSigSession, nil +} + +// createContextV040 implements the MuSig2CreateContext logic for the MuSig2 BIP +// draft version 0.4.0. +func createContextV040( + privKey *btcec.PrivateKey, + allSignerPubKeys []*btcec.PublicKey, + tweaks *MuSig2Tweaks, + localNonces *musig2v100.Nonces, +) (*musig2v040.Context, *musig2v040.Session, error) { + + if localNonces == nil { + return nil, nil, fmt.Errorf("error creating MuSig2 signing " + + "context: localNonces must be provided") + } + + // The context keeps track of all signing keys and our local key. + allOpts := append( + []musig2v040.ContextOption{ + musig2v040.WithKnownSigners(allSignerPubKeys), + }, + tweaks.ToV040ContextOptions()..., + ) + muSigContext, err := musig2v040.NewContext(privKey, false, allOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating MuSig2 signing "+ + "context: %v", err) + } + + muSigSession, err := muSigContext.NewSession( + musig2v040.WithPreGeneratedNonce(localNonces), + ) + if err != nil { + return nil, nil, fmt.Errorf("error creating MuSig2 signing "+ + "session: %v", err) + } + + return muSigContext, muSigSession, nil +} + +// MuSig2Sign calls the Sign() method on the given versioned signing session and +// returns the result in the most recent version of the MuSig2 API. +func MuSig2Sign( + session input.MuSig2Session, + msg [32]byte, +) (*musig2v100.PartialSignature, error) { + + switch s := session.(type) { + case *musig2v100.Session: + partialSig, err := s.Sign(msg, musig2v100.WithSortedKeys()) + if err != nil { + return nil, fmt.Errorf("error signing with local key: "+ + "%v", err) + } + + return partialSig, nil + + case *musig2v040.Session: + partialSig, err := s.Sign(msg) + if err != nil { + return nil, fmt.Errorf("error signing with local key: "+ + "%v", err) + } + + return &musig2v100.PartialSignature{ + S: partialSig.S, + R: partialSig.R, + }, nil + + default: + return nil, fmt.Errorf("invalid session type <%T>", s) + } +} + +// MuSig2CombineSig calls the CombineSig() method on the given versioned signing +// session and returns the result in the most recent version of the MuSig2 API. +func MuSig2CombineSig( + session input.MuSig2Session, + otherPartialSig *musig2v100.PartialSignature, +) (bool, error) { + + switch s := session.(type) { + case *musig2v100.Session: + haveAllSigs, err := s.CombineSig(otherPartialSig) + if err != nil { + return false, fmt.Errorf("error combining partial "+ + "signature: %v", err) + } + + return haveAllSigs, nil + + case *musig2v040.Session: + haveAllSigs, err := s.CombineSig(&musig2v040.PartialSignature{ + S: otherPartialSig.S, + R: otherPartialSig.R, + }) + if err != nil { + return false, fmt.Errorf("error combining partial "+ + "signature: %v", err) + } + + return haveAllSigs, nil + + default: + return false, fmt.Errorf("invalid session type <%T>", s) + } +} + +// SerializePartialSignature encodes the partial signature to a fixed size byte +// array. +func SerializePartialSignature( + sig *musig2v100.PartialSignature, +) ([input.MuSig2PartialSigSize]byte, error) { + + var ( + buf bytes.Buffer + result [input.MuSig2PartialSigSize]byte + ) + if err := sig.Encode(&buf); err != nil { + return result, fmt.Errorf("error encoding partial signature: "+ + "%v", err) + } + + if buf.Len() != input.MuSig2PartialSigSize { + return result, fmt.Errorf("invalid partial signature length, "+ + "got %d wanted %d", buf.Len(), input.MuSig2PartialSigSize) + } + + copy(result[:], buf.Bytes()) + + return result, nil +} + +// DeserializePartialSignature decodes a partial signature from a byte slice. +func DeserializePartialSignature( + scalarBytes []byte, +) (*musig2v100.PartialSignature, error) { + + if len(scalarBytes) != input.MuSig2PartialSigSize { + return nil, fmt.Errorf("invalid partial signature length, got "+ + "%d wanted %d", len(scalarBytes), input.MuSig2PartialSigSize) + } + + sig := &musig2v100.PartialSignature{} + if err := sig.Decode(bytes.NewReader(scalarBytes)); err != nil { + return nil, fmt.Errorf("error decoding partial signature: %w", + err) + } + + return sig, nil +} diff --git a/libwallet/musig/musig2_bip32.go b/libwallet/musig/musig2_bip32.go new file mode 100644 index 00000000..6c0a72c0 --- /dev/null +++ b/libwallet/musig/musig2_bip32.go @@ -0,0 +1,128 @@ +package musig + +import ( + "crypto/hmac" + "crypto/sha512" + "encoding/binary" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + musig2v100 "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +type bip32TweakStep struct { + depth uint8 + pubKeyBytes []byte + chainCode []byte + tweakBytes [32]byte +} + +// The following function receives an aggregatedKey and the unhardened +// derivation steps to produce a list of MuSig KeyTweaks. Those tweaks are used +// to produce a valid signature for the fresly derived key. To only derive the +// xpub, BIP32 can be used with a special chaincode. See the tests or BIP328 +// for details. +func getBip32TweaksForAggregatedKey(aggregatedKey *secp256k1.PublicKey, path []uint32) (*bip32TweakStep, []musig2v100.KeyTweakDesc, error) { + // chainCode := SHA256("MuSig2MuSig2MuSig2") + chainCode := []byte{ + 0x86, 0x80, 0x87, 0xca, 0x02, 0xa6, 0xf9, 0x74, + 0xc4, 0x59, 0x89, 0x24, 0xc3, 0x6b, 0x57, 0x76, + 0x2d, 0x32, 0xcb, 0x45, 0x71, 0x71, 0x67, 0xe3, + 0x00, 0x62, 0x2c, 0x71, 0x67, 0xe3, 0x89, 0x65, + } + + var tweaks []musig2v100.KeyTweakDesc + var err error + + tweakContext := &bip32TweakStep{ + chainCode: chainCode, + depth: 0, + pubKeyBytes: aggregatedKey.SerializeCompressed(), + } + + for _, i := range path { + tweakContext, err = tweakContext.child(i) + if err != nil { + return nil, nil, err + } + tweaks = append(tweaks, musig2v100.KeyTweakDesc{ + IsXOnly: false, + Tweak: tweakContext.tweakBytes, + }) + } + + return tweakContext, tweaks, nil +} + +// Performs a single BIP32 derivation step. The btcec implementation of +// ExtendedKey is not used here because it does not expose the Il value, which +// is needed to produce the tweaks. +func (parent *bip32TweakStep) child(i uint32) (*bip32TweakStep, error) { + // Prevent derivation of children beyond the max allowed depth. + if parent.depth == 255 { + return nil, fmt.Errorf("trying to derive avobe max depth") + } + + if i >= 0x80000000 { + return nil, fmt.Errorf("trying to derive a hardened MuSig key") + } + + // let data = serialize(parentPubKey) || serializeU32(i) + keyLen := 33 + data := make([]byte, keyLen+4) + copy(data, parent.pubKeyBytes) // write key + binary.BigEndian.PutUint32(data[keyLen:], i) // write i) + + // Take the HMAC-SHA512 of the current key's chain code and the derived data: + // + // I = HMAC-SHA512(Key = chainCode, Data = data) + hmac512 := hmac.New(sha512.New, parent.chainCode) + hmac512.Write(data) + ilr := hmac512.Sum(nil) + + // Split "I" into two 32-byte sequences Il and Ir where: + // + // Il = intermediate key used to derive the child + // Ir = child chain code + il := ilr[:32] + childChainCode := ilr[32:] + + // Both derived public or private keys rely on treating the left 32-byte + // sequence calculated above (Il) as a 256-bit integer that must be + // within the valid range for a secp256k1 private key. There is a small + // chance (< 1 in 2^127) this condition will not hold, and in that case, + // a child extended key can't be created for this index and the caller + // should simply increment to the next index. + ilNum := new(btcec.ModNScalar) + overflows := ilNum.SetBytes((*[32]byte)(il)) + if overflows > 0 { + return nil, fmt.Errorf("generated IL overflows P %d", overflows) + } + + // Convert the serialized compressed parent public key into X + // and Y coordinates so it can be added to the intermediate child key. + parentKeyCoordinates, err := btcec.ParseJacobian(parent.pubKeyBytes) + if err != nil { + return nil, err + } + + // Add the intermediate child key to the parent public key to derive + // the final child key. + // + // childKey = parse256(Il)*G + parentKey + var childKey btcec.JacobianPoint + btcec.ScalarBaseMultNonConst(ilNum, &childKey) // childKey = Il*G + btcec.AddNonConst(&childKey, &parentKeyCoordinates, &childKey) // childKey += parentKey + childKey.ToAffine() + pubKeyBytes := btcec.NewPublicKey(&childKey.X, &childKey.Y).SerializeCompressed() + + ret := bip32TweakStep{ + chainCode: childChainCode, + depth: parent.depth + 1, + pubKeyBytes: pubKeyBytes, + tweakBytes: ([32]byte)(il), + } + + return &ret, nil +} diff --git a/libwallet/musig/musig2_bip32_test.go b/libwallet/musig/musig2_bip32_test.go new file mode 100644 index 00000000..7d5b4b54 --- /dev/null +++ b/libwallet/musig/musig2_bip32_test.go @@ -0,0 +1,295 @@ +package musig + +import ( + "encoding/hex" + "fmt" + "testing" + + "github.com/btcsuite/btcd/btcutil/hdkeychain" + "github.com/btcsuite/btcd/txscript" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/stretchr/testify/require" +) + +// This test ensures that an BIP32 derivated pub xpub/1/2 signature matches +// musig(..)/1/2 using our signature helpers. The testing procedure ensures +// a stable hdkeychain derivation matches our method. It also ensures that +// a valid signature for that derived key is produced. +func TestMuSig2Bip32UnhardenedSignature(t *testing.T) { + // parameters + musigVersion := Musig2v100 + pubKeys := [][]byte{ + userKey.PubKey().SerializeCompressed(), + muunKey.PubKey().SerializeCompressed(), + } + path := []uint32{1, 2} // derivation path + + // derive musig(user,muun)/1/2 using our helper + aggKeyAll, err := MuSig2ComputeInternalKey(musigVersion, pubKeys, path) + require.NoError(t, err) + + // derive let xpub=musig(user,muun); xpub/1/2 using bip32 impl from btcec + bip32pub := nativeBip32MusigDerivation(t, aggKeyAll.PreTweakedKey, path) + + // Validate that the hdkeychain.ExtendedKey derivation matches our implenentation + // asserting that generated pubkeys match + require.Equal( + t, + bip32pub.SerializeCompressed(), + aggKeyAll.FinalKey.SerializeCompressed(), + ) + + // Then validate that musig signature works for the unhardened key + { + msg, _ := hex.DecodeString( + "1111111111111111111111111111111111111111111111111111111111111111") + tweak := NoopTweak().WithUnhardenedDerivationPath(path) + sig := muunSignMusig(t, musigVersion, msg, tweak) + + valid, err := VerifySignature( + musigVersion, + msg, + bip32pub.SerializeCompressed(), + sig, + ) + require.NoError(t, err) + require.True(t, valid) + } + + // additionally, ensure that the pubkey we calculate using our internal + // MuSig2Bip32Tweaks function matches the derived xpub + { + step, _, err := getBip32TweaksForAggregatedKey(aggKeyAll.PreTweakedKey, path) + require.NoError(t, err) + + // asserting that generated pubkeys match + require.Equal( + t, + bip32pub.SerializeCompressed(), + step.pubKeyBytes, + ) + } +} + +func TestMuSig2Bip32FailureModes(t *testing.T) { + // parameters + musigVersion := Musig2v100 + pubKeys := [][]byte{ + userKey.PubKey().SerializeCompressed(), + muunKey.PubKey().SerializeCompressed(), + } + path := []uint32{0x80000000 | 3, 1} // derivation path + + // derive musig(user,muun)/3'/1 using our helper + _, err := MuSig2ComputeInternalKey(musigVersion, pubKeys, path) + require.Error(t, err, "Trying to derive a hardened MuSig key") +} + +// Test that signatures work for bip328+taproot tweak bytes +func TestMuSig2Bip32UnhardenedSignatureTaprootTweak2(t *testing.T) { + // parameters + musigVersion := Musig2v100 + pubKeys := [][]byte{ + userKey.PubKey().SerializeCompressed(), + muunKey.PubKey().SerializeCompressed(), + } + taprootTweak, _ := hex.DecodeString( + "2222222222222222222222222222222222222222222222222222222222222222") + + tweak := TapScriptTweak(taprootTweak).WithUnhardenedDerivationPath([]uint32{1, 2}) + // vvv━━━━━━━━━━━━━━━━━━━━━━━━━━━━━^^^^ + // Create final key for tr(musig(user,muun)/1/2, {...}) + taprootAggKey, err := Musig2CombinePubKeysWithTweak( + musigVersion, pubKeys, tweak) + + require.NoError(t, err) + require.Equal(t, + "0324aec31c06d51473a81c35f8070485b6c19c8d652e42ebfae9cead99d17f9b44", + hex.EncodeToString(taprootAggKey.FinalKey.SerializeCompressed()), + ) + + // Then validate that musig signature works for the unhardened key + { + msg, _ := hex.DecodeString( + "1111111111111111111111111111111111111111111111111111111111111111") + + sig := muunSignMusig(t, musigVersion, msg, tweak) + + valid, err := VerifySignature( + musigVersion, + msg, + taprootAggKey.FinalKey.SerializeCompressed(), + sig, + ) + require.NoError(t, err) + require.True(t, valid) + } +} + +// This test ensures that the MuSig helpers can successfully derive an +// unhardened path from an aggregated key. It also ensures that the helpers can +// successfully create a valid signature for it +// +// This test also ensures that the following output descriptor can be paid and +// redeemed. +// +// Output descriptor of this test: +// +// tr( +// musig(userKey, muunKey)/123, +// { +// musig(userKey, muunKey)/88, +// musig(userKey, muunKey)/1/2, +// } +// ) +// +// It is important to notice that there will be no unhardened private key to +// produce a signature here. Instead, BIP32 will be used to derive tweaks for +// the signature & aggregated key. The trick is that the individual keys will +// not change, we're only tweaking the aggregated public key. +func TestMuSig2Bip328UnhardenedTapscript(t *testing.T) { + musigVersion := Musig2v100 + + pubKeys := [][]byte{ + userKey.PubKey().SerializeCompressed(), + muunKey.PubKey().SerializeCompressed(), + } + + // derive musig(user,muun)/1/2 + agg_1_2, err := MuSig2ComputeInternalKey(musigVersion, pubKeys, []uint32{1, 2}) + require.NoError(t, err) + signerCombinedPubKey_1_2 := agg_1_2.FinalKey + + // derive musig(user,muun)/88 + agg_88, err := MuSig2ComputeInternalKey(musigVersion, pubKeys, []uint32{88}) + require.NoError(t, err) + signerCombinedPubKey_88 := agg_88.FinalKey + + // derive musig(user,muun)/123 + internalKeyAgg, err := MuSig2ComputeInternalKey(musigVersion, pubKeys, []uint32{1, 2}) + require.NoError(t, err) + internalKey := internalKeyAgg.FinalKey + + // We're going to commit to a script and spend the output using the + // script. This is just an OP_CHECKSIG with the combined MuSig2 public + // key. + leaf_88 := testScriptSchnorrSig(t, signerCombinedPubKey_88) + leaf_1_2 := testScriptSchnorrSig(t, signerCombinedPubKey_1_2) + tapScriptTree := txscript.AssembleTaprootScriptTree(leaf_88, leaf_1_2) + + // Create final key for tr(musig(user,muun)/123, {...}) applying taproot + // tweak bytes for tapscript.rootMerlkeHash and bip32 tweaks for /123 + // derivation path + rootMerkleHash := tapScriptTree.RootNode.TapHash() + + tweak := + TapScriptTweak(rootMerkleHash[:]). + WithUnhardenedDerivationPath([]uint32{123}) + + keySpendAggregatedKey, err := Musig2CombinePubKeysWithTweak( + musigVersion, pubKeys, tweak) + require.NoError(t, err) + p2trKey := keySpendAggregatedKey.FinalKey + require.Equal(t, + "02a7be2941c035b89481f985f5ea1853c7ad806449078bc47d397849b308084b16", + hex.EncodeToString(p2trKey.SerializeCompressed()), + ) + + testCases := []tapscriptTestCase{ + { + // tr( + // musig(userKey, muunKey)/123, <- redeem internalKey + // { + // musig(userKey, muunKey)/88, + // musig(userKey, muunKey)/1/2, + // } + // ) + description: "keyspend with musig(user,muun)/123", + p2trKey: p2trKey, + + rootScript: tapScriptTree, + witnessScript: nil, + + signer: func(t *testing.T, msg []byte) []byte { + // in the keyspend path we must pass the script root + // hash to compute the final tweaked key. + return muunSignMusig(t, musigVersion, msg, tweak) + }, + }, + { + // tr( + // musig(userKey, muunKey)/123, + // { + // musig(userKey, muunKey)/88, <------- redeem + // musig(userKey, muunKey)/1/2, + // } + // ) + description: "tapscript with musig(user,muun)/88", + internalKey: internalKey, + + rootScript: tapScriptTree, + witnessScript: leaf_88.Script, + + signer: func(t *testing.T, msg []byte) []byte { + tweak := NoopTweak().WithUnhardenedDerivationPath([]uint32{88}) + return muunSignMusig(t, musigVersion, msg, tweak) + }, + }, + { + // tr( + // musig(userKey, muunKey)/123, + // { + // musig(userKey, muunKey)/88, + // musig(userKey, muunKey)/1/2, <------ redeem + // } + // ) + description: "tapscript with musig(user,muun)/1/2", + internalKey: signerCombinedPubKey_88, + + rootScript: tapScriptTree, + witnessScript: leaf_1_2.Script, + + signer: func(t *testing.T, msg []byte) []byte { + tweak := NoopTweak().WithUnhardenedDerivationPath([]uint32{1, 2}) + return muunSignMusig(t, musigVersion, msg, tweak) + }, + }, + } + + for _, test := range testCases { + name := fmt.Sprintf("Bip328 test case=%s", test.description) + t.Run(name, func(t *testing.T) { + testTapscriptSpend(t, test) + }) + } +} + +// implementation of bip32 for musig, the naive way. use this function to validate +// against implementated code +func nativeBip32MusigDerivation(t *testing.T, aggregatedKey *secp256k1.PublicKey, path []uint32) *secp256k1.PublicKey { + chainCode, _ := hex.DecodeString( + "868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965") + + retKey := aggregatedKey + var err error + + // derive using ExtendedKey + xpub := hdkeychain.NewExtendedKey( + []byte{0x04, 0x88, 0xb2, 0x1e}, // version=xpub + aggregatedKey.SerializeCompressed(), + chainCode, + []byte{0x00, 0x00, 0x00, 0x00}, // parentFP + 0, // depth + 0, // childNum + false, // isPrivate + ) + + for _, child := range path { + xpub, err = xpub.Derive(child) + require.NoError(t, err) + retKey, err = xpub.ECPubKey() + require.NoError(t, err) + } + + return retKey +} diff --git a/libwallet/musig/musig2_test.go b/libwallet/musig/musig2_test.go new file mode 100644 index 00000000..8cb27656 --- /dev/null +++ b/libwallet/musig/musig2_test.go @@ -0,0 +1,503 @@ +package musig + +import ( + "bytes" + "encoding/hex" + "fmt" + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/stretchr/testify/require" +) + +// SerializePublicKey either serializes with 32 byte x-only or 33 bytes compressed +// serializations depending on the MuSig version +func SerializePublicKey(bipVersion MusigVersion, key *secp256k1.PublicKey) []byte { + pub := key.SerializeCompressed() + xOnly := bipVersion == Musig2v040Muun + if xOnly { + return pub[1:] + } + return pub +} + +func hexDecode(keyStr string) []byte { + keyBytes, _ := hex.DecodeString(keyStr) + return keyBytes +} + +func TestMuSig2Tests2of2(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + version MusigVersion + expectedErr string + + userKey string + muunKey string + tweak *MuSig2Tweaks + msg string + muunSessionId string + userSessionId string + combinedPubUntweaked string + combinedPub string + userNonce string + muunNonce string + muunPartialSignature string + fullSignature string + }{{ + name: "sanity 1 (v040)", + version: Musig2v040Muun, + userKey: "507d881f0b5e1b12423cb0c84a196fb24227f3fe1540a1c7b20bf78d83de4533", + muunKey: "b6f14c73ee5269f5a13a11f48ad54306293ee134e924f680fcd35f615881105b", + tweak: KeySpendOnlyTweak(), + msg: "ef2ecc1f48c0b28ccaf8f3a8c6477740d869964ebc152a2c5f93f19e7b84b103", + userSessionId: "5c9360026e39ad06251a27916dcf086a7b2deb6789c5dcd75ba10e540cf37e13", + muunSessionId: "cad3ec6737e2fb125d976bfe382441c59c6a4d46382bfab75e9d3f1b43a9b0a7", + combinedPubUntweaked: "5ecd943b359fa0c52ba88c0395ca7f7bfa256c8d3c63609527856768185cab16", + combinedPub: "a2d0d99f9f2706846b18f070aaf95afb579332da15a58162987e48f59a90bb59", + userNonce: "03ce5914d3fd813391318ce9706227f325cb272a352b900be8bc9e813911f188b802ef1739fc0ca286ac81773daca310f6ff750f8ac0bbf11a253dcdcac93d2ede79", + muunNonce: "0294690a525328949fed6272f8308e48f7639ee270d83cad1d08b66e870d91a46e02e1a957ad994a6cbe6ee3db26a91e9031a38b92ec10b272ffd6cf8a303784bdb3", + muunPartialSignature: "ef83958ce09b6859756a114a6e83d5654fb559064b6f21d78ddfd89e35f2a41c", + fullSignature: "6eda42c24fd743f88749c0ee662491fb5462db1f1e3bf3572eb75f4ae6ef6b19991c9d55038c56cb64132913b0ff8dc57bced644ed2ebbc8b3a3616ba373082d", + }, { + name: "sanity 1 (v100)", + version: Musig2v100, + userKey: "507d881f0b5e1b12423cb0c84a196fb24227f3fe1540a1c7b20bf78d83de4533", + muunKey: "b6f14c73ee5269f5a13a11f48ad54306293ee134e924f680fcd35f615881105b", + tweak: KeySpendOnlyTweak(), + msg: "ef2ecc1f48c0b28ccaf8f3a8c6477740d869964ebc152a2c5f93f19e7b84b103", + userSessionId: "5c9360026e39ad06251a27916dcf086a7b2deb6789c5dcd75ba10e540cf37e13", + muunSessionId: "cad3ec6737e2fb125d976bfe382441c59c6a4d46382bfab75e9d3f1b43a9b0a7", + combinedPubUntweaked: "03515ffb8569741ba605a113f9eb99bc81f9daacc8923d2578a31177e78aa0463c", // differs from 040 + combinedPub: "03c367d7ef80b10687820dda279e0e6054dadccc30550c8eae7e21a945069cb7e0", // differs from 040 + userNonce: "02b2c9e32786ab8612b4805e99f56086588d199486b7da5af99655402ffcc68cbd02aa0b18c2026742ed309fe00656cd9e68402bb10dea6aac05e69400f6f6c70ee5", + muunNonce: "0371c084c83326c3c721aeeefdf647d19d1417eba0ac9a09023bb363300eaeba7702539c3793f6a1a130b86a3168dbdb9ed686b04fdde57250dea20424047bc037ec", + muunPartialSignature: "18422b132ac447af9e98db197d45becb26c83aa4fa658312dd8357e1e8309ce4", + fullSignature: "5e9034fe55b901308dd4751855e50a2181ec264b76fb5a986c87e78084202a9a83588f0e45e103645a01f313d7f3b03a5fcc5bc68ff7c41ae4d451271441d20b", + }, { + name: "sanity 2 (v040)", + version: Musig2v040Muun, + userKey: "5475f961bc879e5c34aaeb2b2de3272db2c2b849265b70836175dda4d9f4e323", + muunKey: "36bae62a1f853f8e0fd06a8dc92e40b279c8661cf331c098ee384430d51e0908", + tweak: KeySpendOnlyTweak(), + msg: "fc5b875bf3cba4b01ff541b30029c3a2b40d8839c4e5a59d17b83d5ea0746a00", + userSessionId: "7097c074c9e821e9f6f8b2305bd79cd4e244c1968e139704261954cb6195fdb8", + muunSessionId: "577515cff85d3574a8f5f407b8017b3e1f7afeb604244ca5ddd946e6c4a9360a", + combinedPub: "d5ed5fbfd6598566ff101c671e7e2194604bd7b4aaa2e6913d691794ceaee3fd", + userNonce: "03162fcde2205a9e7c44ad646c904afb879ae8ae1e93086e245e9b152734c762a802bd70bfd7c80c7d5e7e91017920603916bb8475ee81d0f09d2049d514b12ae82e", + muunNonce: "028d5ce815841f0001b69f7cc82bb70ae1295a5bb51dfda73d0991b91216a6becd03238845a40801636793ddb0dd1ff5104a6e9afd4db4a439b5cc98e227785e079e", + muunPartialSignature: "6b2e8c7878882e22a7dbdd1e89607105b2e64e95f72943a38908f9643900e11b", + fullSignature: "c1f24ddb83f8e6caf6fffc454a52f1e9dd67d9cfbccce309f0253361b94b7edcd6df44317e272f47a8a6dd823a65b7380055088bab914432f99ad92c277732aa", + }, { + name: "sanity 3 (v100)", + version: Musig2v100, + userKey: "e3be9721e2f3422d15a959b371b340c1f197fd9eb8a38b3f1801a759261f6766", + muunKey: "399508d30aa80e152bcfb6524ff900793d6a7ba0db0c1f6f74a80aca7482f131", + tweak: TapScriptTweak(hexDecode("83997adeb6df251a624c330f9f9b5dc1d825f1a5d73872d4aded55990f89a390")), + msg: "d1752376fb27655a076f4dc2d3d1154f48faf98a2b3cde703b2c7c3bd6ca1054", + userSessionId: "d3fb624d2253acde57525b5b1b9ea4fba686de69acbb4ecabc87800a8fdd94b3", + muunSessionId: "dbab8752a87f26c85d9dad982a8d4b0e399079c6a9bf400cd2619d3422eee446", + combinedPub: "02128ded81436e25b77192b02771729eb9417b1c0bac848a657316cb69ead4d2fc", + userNonce: "022716e86001574bd783eafba6e4faf580760b5a5289f4481d198a8abf39af9c5903b5c0740167645a66f4ba37a8f090d96df4690de004d39525ccb320f59a5df7c6", + muunNonce: "0217f8ea11abe0b92070a43f7e2553f37f4afc7905165e190b266ed6f1aca2e76f02976491a364e4459fee8fae9bc82d0f3b08385607adfda238833d45ddc33be66e", + muunPartialSignature: "a4843772adac7976e3133b68e4bf03dd93e9d233db9193a2e65ca2b352ae82d1", + fullSignature: "5dfafb89a5b7665a16f5391ada533e0c5f1283e126459e18c502383b21e60ede2b0c51206357a4974cd468cd8d6e9091b7fdcf82c33269e11bdba3e08286891a", + }, { + name: "sanity 4 (v040)", + version: Musig2v040Muun, + userKey: "cace2b7518d8866c8a41bb45aea06882dfb16e79d46fd02eb8af3a795fec5ada", + muunKey: "234221af43d249604ed3972c958677fb25e453e57cd808e2b32901dc6936fe94", + tweak: KeySpendOnlyTweak(), + msg: "21a87f41d45b0a74d2d49328b580204f8b027ee4bb73bddfdf5388db8ab6bb49", + userSessionId: "b7aed480f9248d27d64744e5680ad91c6111d420a1e7c47db104b30fbc513899", + muunSessionId: "f7f7a39a0b434e4b9151f4f9797e12c4c1a8dc323c9efc4edd48ba6674c33762", + combinedPubUntweaked: "ff591962a3be86cd58360ddd19212ce355a6fdd4566c219fe6bf0f6883a67f97", + combinedPub: "6fbfbe50f81503feb8163d01229e6e69d5a959186a0aa2d6e6881042fa0e0022", + userNonce: "0341f6cafeda073092125d7752f1d06f5d50dc8caf43eee212e2caf0ace8c03d4202c8e244bbc650734b8f186512b69798bb9d54a190a5c77a0d353068fe4806049d", + muunNonce: "03a3095a17a728b36f78d5428e397fcd4d7aaa1fd3d4aaaa2fb95a98a4092385b30361d094f79948ecd771bbf3f99a5fcd43bd023a3ea1bed8492213f4cf2327f4f7", + muunPartialSignature: "a15f67ea4bfcb77b19cab3e44e37a9e03eb9921b80cdc8b3bf7d8a346af9139f", + fullSignature: "794e2dba30eacf59048559d471c382929b99ff86f657a760086492cdcb0c3a39e1f3d59367c585e083d3eadcdf8253b51c446f9d350c522ce7b2a5174b8b3513", + }, { + name: "sanity 5 (v100)", + version: Musig2v100, + userKey: "856067e727dda8e93fe2d92b481c2eb0726c5d70074c134567fd9870138fa9a9", + muunKey: "d65faacf0e9c75741cd487e9c0d17fa5fb23abb117b0b2d3817dbc9cc9a89eac", + tweak: TapScriptTweak(hexDecode("ce535b4f4059c9414d44afa5582b5650e6693739be869fda6c186cb2add3df04")), + msg: "8ca8fbf5661b8cb7cf98eea348b8686c721b0d9d11f9ae7ea850322657c76dca", + userSessionId: "d74678ceb70ee4e875b52ab9a9b749d9882cfb3f184fbc13d9d96d1306ff9f54", + muunSessionId: "e92cd9db84ddea807401442036cc64878f03cdcc5030c78cecf187d4b166684f", + combinedPub: "02d26f1761a00851216ea9a87e479ab57bb03d38f7fd4d6d5a67f7954b7aed5517", + userNonce: "03d243bc9b76eb7212d9f71cfc8461fb3a32a0e72ffba9e41b056800722df329440363ab1202649637c2e6197f78edeb79bfc272a4f4ebac6d577baa6ec17eb8b7d2", + muunNonce: "03176ead83f618b25510ef0674d6a820fc23a6afe9ab6a507a68d251e0a615185203527ed0fa9ce1fd1c9993f41f52bd3edb441e069018a2637974558cde7a190cc2", + muunPartialSignature: "7d8359f5159b7e3084000329abe5aa743d9535fa8c4ec37097b28d695371d6b2", + fullSignature: "4329e7aeb9467ace1af726b9f9101c4e4d7f776e348864cc9803a6928e83710de659b3bd81496e8497bed260d66070b982badd7eca20b3b32b2cb92f8c6fd902", + }, { + name: "sanity 6 (v040)", + version: Musig2v040Muun, + userKey: "a31e8d00ea74d66013802a878c44438da64ff89d4b8754492ae342b1849285e3", + muunKey: "5ebf2750bdc86e49e589ddee207eab6638672e1e9d037a5461ae5fdc702f8621", + tweak: KeySpendOnlyTweak(), + msg: "8c664cb4843e6670d128469e3c5f53a131cd92939ba49efc015ae4d9db487e1b", + userSessionId: "eb8a7990a594347bbfbc1fc208230ba77e5aeb57968298ca989f278b675069ea", + muunSessionId: "cc9f909537f3ce40c7292a461910cf2133e3e2b1dbeee3cabe0964edffa45f2e", + combinedPub: "3aeb9ee01226a3e50f421d1472a064c5a0537034cc1770174834d8df44d4bac6", + userNonce: "02bdd624154b6b11a167a26aef832cfbd6d9d0fbeb2a6b1ef230502c92065474a00367af25653aa9f55e6b0a8b78824dd25a0af7945ebe4ed3da1d1b0f86b0f0fd54", + muunNonce: "022bdb069642c36ea5eb8f6ee4af5dafa602f5a1c4e87c1364dec9481d8769c7cd03940111b4ab28cbf1920360c80a4861eaabc0fbdf443ccb3f5e91c0ea95753ba4", + muunPartialSignature: "29ccc19ad440e90dd03e3011652af94a780e5ee9d5d2cb647fac0d8723f8f7e8", + fullSignature: "3bbf83075b8df8bfc1f493d9e51ed0eb22f419d22a3df6f9434fd666be9bacaf893b43fe38eff9c73c66f0525d2bc8b7a96cfd1ad5e145b97ed2e5a9813c53af", + }, { + name: "sanity 7 (v040)", + version: Musig2v040Muun, + userKey: "938743adf2d94f83342f28109dd146423c287fdada42a1bfc94cd257dce10e0d", + muunKey: "29ef6014b97d3d6c2a9bf93395a245735137733b080913b59f53e1755636c5e2", + tweak: KeySpendOnlyTweak(), + msg: "6fe7c58ad896b25f1c86c26bfdb85524bc4ecc87fc38acd0a16448415db4845e", + userSessionId: "16ace9f016dd36d59b33576105eaf0b441ad2550bb8aa53940b0886dff30741c", + muunSessionId: "31e79edbd0849023181218d1573c67235c6f9f7b7ad88fb6b0ea817a5af22817", + combinedPub: "3e33b66b01724807173ca68b264fc211f875501296606ee5699aed7cbf2732eb", + userNonce: "0283c130da2c27cde19073b7386ecd34fa297341427a6c190fa53b3efdd8e4d2c40378d05fcc62cfe78ace614e8fbab1124b141b635efe99e4ff686f40fa2b4c6b9d", + muunNonce: "0227fbea16aa0f54c5d4b4115edb9ff0d76ce7c808bea2705ee0d9bbe6fd4de19c0236e16ae2604e9d1f0ccb3943b7d1564a3dce418ba2d5a83ae4555347703079d2", + muunPartialSignature: "c67cc164dde0b8a80208b220761c04a20490ec64ae5e4a7ec400cde8afa20fc0", + fullSignature: "c1b7b742705fa6888f44d045bb1a3436c234f6de42e26a4e3e3eb2682812e769198af2f2652b59aa00f0a6e102387c870105031e5ad714d91831501e89c2ae28", + }, { + name: "sanity 8 (v100)", + version: Musig2v100, + userKey: "c96ed4f9da26be21374dce108bbe915541b1bfa47efc017980c4320dcb1d5a39", + muunKey: "60eb8658f768f9be266c69a181f4dc201ade73909232985127e2c10d6b11ded4", + tweak: TapScriptTweak(hexDecode("3f024acdf7597c4ee7b6344a6ff8c8e08b77acd2fe6e42c72d199eebb2c997b1")), + msg: "f5eed1ed2561713a07f9b2e4889b0abd84cf0ff20783b4db952ac52162235763", + userSessionId: "8d586a38b6d3c9662faffae3b4cf9ede137bb4aba1e582b0cb4cf13a88e041ad", + muunSessionId: "03b942e5de91597f05c2ce50d44131c67936cad0af2fe67d8fce90a8cae0b925", + combinedPub: "02b26c54bbeb897bed5e171109eaa765b980b0e8d94cb8a8924660d40a4a77c2b8", + userNonce: "02f73c3c63c659f9020444f09134f1a665b9ea4f945333266bcf480e05a5a73f1203936e2303f9f3b99cf43b66c736aa48fcfa6d88cc239e89e52250527d169bab96", + muunNonce: "03ab48e4612c3225f661b0a8d8f45753711c7de7e8ef2769e5130f391a7db75a1303556d0914aca06cd479f284ccbfcaf41a6165a773af10b99e421a5c7f36ed5a89", + muunPartialSignature: "5d719ef048d33ac14c6a0011f1dd6f4693f2d0597d5dd340068f53a2b898d8f5", + fullSignature: "a508ef2e4e8fa795537c4e04fb62f548ec05b0f16c97732949904661554b52584ea8a5f7e2f4336f5679744d88205482092844ffc99282e1e1084411d2b7a714", + }, { + name: "sanity 9 (v100)", + version: Musig2v100, + userKey: "f163af2ffd707882721f4f594fe9e5c09a5de9af45a5b9c9d81c873d3cbf3439", + muunKey: "e5b41cee7d37e82262678145ad6ac8f664506a7c43d67f73cc3a1471d28c93f4", + tweak: TapScriptTweak(hexDecode("53b0a68bd9224772ad17966c6c1d055dc47dc7450d065e61e9a65050ce48e85e")), + msg: "3ca3b9bb8e531664e8b25c638673a679b82f15c26f3b52d368d99f8c089ffd9a", + userSessionId: "1e81f6dc4801c1df619d817d366055cc5e15a51ff46aabd563df4b956855fa40", + muunSessionId: "98364c25dca3a8cec1ec59a9baf825863b208e532ce71336b0e14b63289523ba", + combinedPub: "030b6113091eb6aedd1a12aa5b6a58f2bf4c24d5e22944be9d6d2e89fc3187469e", + userNonce: "0324761e824500b1798c1d2af367b6229c70ab96aa328fc89f9a4a3ddbc6b96d9503bb3b54ba779641c0700dbe1a8f5e72f9afdc7bfe329599ac5bb2c7b212953316", + muunNonce: "03309e05f403e2ee36c7c05975a7fea411a2ba169e4d32366bdb8931cc3d45e42a02d34f33695c07af5f76a476556ad3aed40c66a8c31af70c6fd77fd058e065d58c", + muunPartialSignature: "a3eea0cdf3d9564e354892c6090473ebdfb92611a488c0173ecbe53331734f55", + fullSignature: "3de568db8a74734c8823a106566cb13d00273668ac2dec68df8e80c492d678e293da485ec944e4fff9e94248665f9455d1a901132037395f413c7199a9fcd9b8", + }, { + name: "sanity 10 (v040)", + version: Musig2v040Muun, + userKey: "a4de0154616f5e9a8d1beb77bca6a6c4ee5a3fc0d0edb966a1ee7f5e1f7318af", + muunKey: "43889a9efda32bf3f39c364b3d373ba4f4b450da6e4ec7b920cf189127300e9b", + tweak: KeySpendOnlyTweak(), + msg: "c7d15407975a8a3ed8686b607ee955880745289fedde01c5bdfb4a933c73f98f", + userSessionId: "40c4aad751f1e50bf013623dd13fef7285b83155688287fe2f00b00cfcda62b1", + muunSessionId: "77df399174557b3a04db01fc9209efe2379b7750ac1cc0cb3fadb2e0632b77ca", + combinedPub: "53a65378992d5c521f9390d5716ca6402e2933047aab907a78de6066400adf0b", + combinedPubUntweaked: "69fd98b014388378f286239e8115444d8a6ad77aeef8d3a4ac63d27cf395a473", + userNonce: "0287e08a04a2b96cdebdb59d88b34ea0e39dde004f40fe995408f0c5b423ed8cb0025b2b0d15298ad8cdb64af32849e27e4e628403188c571cfd7b0b1bda06fabc39", + muunNonce: "03b0514f0c78e8d6c85781e1f6c63a14add69bb6dce00827c3eacfe8599970cdc1032c8892752ac8ac2b444af84fa58263e3cb4d8301987c25d068249f554401efcd", + muunPartialSignature: "fab9805aef289b0fb1a5b7e4285920f709fe9552b51633825c548140aff7283e", + fullSignature: "e50774777d17470b26631d6ab748d09f8632c56f2b3b29f2f4f81b7f5359d2814752ec522569f1409185cbf118c420219750ab0e2f0138413b28be7913ad9a23", + }, { + name: "sanity 10 (v100)", + version: Musig2v100, + userKey: "a4de0154616f5e9a8d1beb77bca6a6c4ee5a3fc0d0edb966a1ee7f5e1f7318af", + muunKey: "43889a9efda32bf3f39c364b3d373ba4f4b450da6e4ec7b920cf189127300e9b", + tweak: KeySpendOnlyTweak(), + msg: "c7d15407975a8a3ed8686b607ee955880745289fedde01c5bdfb4a933c73f98f", + userSessionId: "40c4aad751f1e50bf013623dd13fef7285b83155688287fe2f00b00cfcda62b1", + muunSessionId: "77df399174557b3a04db01fc9209efe2379b7750ac1cc0cb3fadb2e0632b77ca", + combinedPub: "03a5277614222885d7e9c9096483447785999ed9f1ad7eb5fd008917fecaeebf57", + combinedPubUntweaked: "035b5ba78d7db091e64827f4f5fa24a644182ce2a3e7cf63720667ca86fc7d7a04", + userNonce: "026ec2cc8947d5b8a04e0d06d1fc6c603db250eb204a3ca6f9a88af3a08944767602c9919f05bc49d0d710dda3ac5409d66b82c47f9565c0779faea0420b6e6aefeb", + muunNonce: "0217abf0457b4d2b933ed13d6f9afdf3817883642f7ad99665a4a1ec2889743cf4020416bc7dac258f2ce9e37961465f0f8554f73127d72f82458b93e0663899bc1c", + muunPartialSignature: "094ef219a2613edd393a656a800a24550926a091cb3c0c70522c0978b50d231f", + fullSignature: "c74f86e54264c8c4f5c88251ece57b0ee6e318dc03d48bb41f60955c7779c03183631112bfd5514645efe183e7b5e25c156a8edbc8fcc8830acb7eab0c3a83a3", + }, { + name: "sanity 10 (v100) unhardened /1/2", + version: Musig2v100, + userKey: "a4de0154616f5e9a8d1beb77bca6a6c4ee5a3fc0d0edb966a1ee7f5e1f7318af", + muunKey: "43889a9efda32bf3f39c364b3d373ba4f4b450da6e4ec7b920cf189127300e9b", + tweak: KeySpendOnlyTweak().WithUnhardenedDerivationPath([]uint32{1, 2}), + msg: "c7d15407975a8a3ed8686b607ee955880745289fedde01c5bdfb4a933c73f98f", + userSessionId: "40c4aad751f1e50bf013623dd13fef7285b83155688287fe2f00b00cfcda62b1", + muunSessionId: "77df399174557b3a04db01fc9209efe2379b7750ac1cc0cb3fadb2e0632b77ca", + combinedPub: "03a5277614222885d7e9c9096483447785999ed9f1ad7eb5fd008917fecaeebf57", + combinedPubUntweaked: "035b5ba78d7db091e64827f4f5fa24a644182ce2a3e7cf63720667ca86fc7d7a04", + userNonce: "026ec2cc8947d5b8a04e0d06d1fc6c603db250eb204a3ca6f9a88af3a08944767602c9919f05bc49d0d710dda3ac5409d66b82c47f9565c0779faea0420b6e6aefeb", + muunNonce: "0217abf0457b4d2b933ed13d6f9afdf3817883642f7ad99665a4a1ec2889743cf4020416bc7dac258f2ce9e37961465f0f8554f73127d72f82458b93e0663899bc1c", + muunPartialSignature: "094ef219a2613edd393a656a800a24550926a091cb3c0c70522c0978b50d231f", + fullSignature: "c74f86e54264c8c4f5c88251ece57b0ee6e318dc03d48bb41f60955c7779c03183631112bfd5514645efe183e7b5e25c156a8edbc8fcc8830acb7eab0c3a83a3", + }} + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(tt *testing.T) { + tt.Parallel() + + // parse params + var err error + userKey := hexDecode(tc.userKey) + muunKey := hexDecode(tc.muunKey) + msg := hexDecode(tc.msg) + muunSessionId := hexDecode(tc.muunSessionId) + userSessionId := hexDecode(tc.userSessionId) + + userPrivateKey := secp256k1.PrivKeyFromBytes(userKey) + userPublicKeyBytes := SerializePublicKey(tc.version, userPrivateKey.PubKey()) + + muunPrivateKey := secp256k1.PrivKeyFromBytes(muunKey) + muunPublicKeyBytes := SerializePublicKey(tc.version, muunPrivateKey.PubKey()) + + // test aggregateKey + aggregateKey, err := Musig2CombinePubKeysWithTweak( + tc.version, + [][]byte{userPublicKeyBytes, muunPublicKeyBytes}, + tc.tweak, + ) + if err != nil { + if tc.expectedErr != "" { + require.ErrorContains(tt, err, tc.expectedErr) + } else { + tt.Fatal("Error aggregating keys", err) + } + return + } + aggregateKeyBytes := SerializePublicKey(tc.version, aggregateKey.FinalKey) + require.Equal(tt, tc.combinedPub, hex.EncodeToString(aggregateKeyBytes)) + + if len(tc.combinedPubUntweaked) > 0 { + aggregateKeyUntweakedBytes := SerializePublicKey(tc.version, aggregateKey.PreTweakedKey) + require.Equal(tt, tc.combinedPubUntweaked, hex.EncodeToString(aggregateKeyUntweakedBytes)) + } + + // test user nonce + userNonce, err := MuSig2GenerateNonce(tc.version, userSessionId, userPublicKeyBytes) + if err != nil { + if tc.expectedErr != "" { + require.ErrorContains(tt, err, tc.expectedErr) + } else { + tt.Fatal("Error in user nonce", err) + } + return + } + require.Equal(tt, tc.userNonce, hex.EncodeToString(userNonce.PubNonce[:])) + + // test muun nonce + muunNonce, err := MuSig2GenerateNonce(tc.version, muunSessionId, muunPublicKeyBytes) + if err != nil { + if tc.expectedErr != "" { + require.ErrorContains(tt, err, tc.expectedErr) + } else { + tt.Fatal("Error in muun nonce", err) + } + return + } + require.Equal(tt, tc.muunNonce, hex.EncodeToString(muunNonce.PubNonce[:])) + + // test muun partial signature + muunPartialSignatureBytes, err := ComputeMuunPartialSignature( + tc.version, + msg, + userPublicKeyBytes, + muunKey, + userNonce.PubNonce[:], + muunSessionId, + tc.tweak, + ) + if err != nil { + if tc.expectedErr != "" { + require.ErrorContains(tt, err, tc.expectedErr) + } else { + tt.Fatal("Error in partial signature", err) + } + return + } + require.Equal(tt, tc.muunPartialSignature, hex.EncodeToString(muunPartialSignatureBytes)) + + // finish 2-of-2 signature + sig, err := ComputeUserPartialSignature( + tc.version, + msg, + userKey, + muunPublicKeyBytes, + muunPartialSignatureBytes, + muunNonce.PubNonce[:], + userSessionId, + tc.tweak, + ) + if err != nil { + if tc.expectedErr != "" { + require.ErrorContains(tt, err, tc.expectedErr) + } else { + tt.Fatal("Error in full signature. ", err) + } + return + } + + require.Equal(tt, tc.fullSignature, hex.EncodeToString(sig)) + + valid, err := VerifySignature(tc.version, msg, aggregateKeyBytes, sig) + if err != nil { + if tc.expectedErr != "" { + require.ErrorContains(tt, err, tc.expectedErr) + } else { + tt.Fatal("Error in validation", err) + } + return + } + require.Equal(tt, true, valid) + + require.NoError(tt, err) + }) + } +} + +func combinePubKeysWithTweak(userPublicKey, muunPublicKey *secp256k1.PublicKey, tweak *MuSig2Tweaks) (*secp256k1.PublicKey, error) { + pubKeys := [][]byte{ + userPublicKey.SerializeCompressed(), + muunPublicKey.SerializeCompressed(), + } + + aggregateKey, err := Musig2CombinePubKeysWithTweak(Musig2v040Muun, pubKeys, tweak) + if err != nil { + return nil, fmt.Errorf("Error combining keys: %w", err) + } + + return aggregateKey.FinalKey, nil +} + +func TestSigning(t *testing.T) { + toSign := [32]byte{1, 2, 3} + + userPriv, _ := btcec.NewPrivateKey() + muunPriv, _ := btcec.NewPrivateKey() + + tweak := KeySpendOnlyTweak() + + combined, err := combinePubKeysWithTweak(userPriv.PubKey(), muunPriv.PubKey(), tweak) + if err != nil { + t.Fatal(err) + } + + userSessionId := RandomSessionId() + muunSessionId := RandomSessionId() + + userPubNonces, _ := MuSig2GenerateNonce(Musig2v040Muun, userSessionId[:], nil) + muunPubNonces, _ := MuSig2GenerateNonce(Musig2v040Muun, muunSessionId[:], nil) + + muunSig, err := ComputeMuunPartialSignature( + Musig2v040Muun, + toSign[:], + userPriv.PubKey().SerializeCompressed(), + muunPriv.Serialize(), + userPubNonces.PubNonce[:], + muunSessionId[:], + tweak, + ) + if err != nil { + t.Fatal(err) + } + + fullSig, err := ComputeUserPartialSignature( + Musig2v040Muun, + toSign[:], + userPriv.Serialize(), + muunPriv.PubKey().SerializeCompressed(), + muunSig, + muunPubNonces.PubNonce[:], + userSessionId[:], + tweak, + ) + if err != nil { + t.Fatal(err) + } + + valid, err := VerifySignature(Musig2v040Muun, toSign[:], combined.SerializeCompressed(), fullSig[:]) + if err != nil { + t.Fatal(err) + } + if !valid { + t.Fatal("failed to verify sig") + } +} + +func TestCrossWithJava(t *testing.T) { + decode32Bytes := func(str string) (result [32]byte) { + d, _ := hex.DecodeString(str) + copy(result[:], d) + return + } + + rawUserPriv := decode32Bytes("6e39c6add6323a5ac5f65e50231fb815026476e734eb9f4f66dce3298fddf1dc") + rawMuunPriv := decode32Bytes("b876ecf97c19588cf4be95ddc0b06c0d9f623f2cf679276c25e4dfb512b19743") + userSessionId := decode32Bytes("52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649") + muunSessionId := decode32Bytes("81855ad8681d0d86d1e91e00167939cb6694d2c422acd208a0072939487f6999") + toSign := decode32Bytes("0102030000000000000000000000000000000000000000000000000000000000") + + expectedKey, _ := hex.DecodeString("027ca7eab04c2ad445418fa6a0ed2a331f121444aedd043adef94bdc00040ff96c") + expectedSig, _ := hex.DecodeString("773ad923eb5eef593095a6787b674675de2a558335dc3e44fe40cf7b3736637e66d508e4eaadca28dab02e2fdcf5707392b561fffa1837205d2fa77b74cbc82f") + + userPriv, userPub := btcec.PrivKeyFromBytes(rawUserPriv[:]) + muunPriv, muunPub := btcec.PrivKeyFromBytes(rawMuunPriv[:]) + tweak := KeySpendOnlyTweak() + + fmt.Printf("userpub %x", userPub.SerializeCompressed()) + fmt.Printf("muunpub %x", muunPub.SerializeCompressed()) + + combined, err := combinePubKeysWithTweak(userPub, muunPub, tweak) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(combined.SerializeCompressed(), expectedKey) { + t.Fatal("Combined key doesn't match") + } + userPubNonces, _ := MuSig2GenerateNonce(Musig2v040Muun, userSessionId[:], nil) + muunPubNonces, _ := MuSig2GenerateNonce(Musig2v040Muun, muunSessionId[:], nil) + + muunSig, err := ComputeMuunPartialSignature( + Musig2v040Muun, + toSign[:], + userPriv.PubKey().SerializeCompressed(), + muunPriv.Serialize(), + userPubNonces.PubNonce[:], + muunSessionId[:], + tweak, + ) + if err != nil { + t.Fatal(err) + } + + fullSig, err := ComputeUserPartialSignature( + Musig2v040Muun, + toSign[:], + userPriv.Serialize(), + muunPriv.PubKey().SerializeCompressed(), + muunSig, + muunPubNonces.PubNonce[:], + userSessionId[:], + tweak, + ) + if err != nil { + t.Fatal(err) + } + + valid, err := VerifySignature(Musig2v040Muun, toSign[:], combined.SerializeCompressed(), fullSig[:]) + if err != nil { + t.Fatal(err) + } + if !valid { + t.Fatal("failed to verify sig") + } + if !bytes.Equal(fullSig[:], expectedSig) { + t.Fatal("Signatures do no match") + } +} diff --git a/libwallet/musig/musig_main_impl.h b/libwallet/musig/musig_main_impl.h deleted file mode 100644 index f27c1d8e..00000000 --- a/libwallet/musig/musig_main_impl.h +++ /dev/null @@ -1,18 +0,0 @@ -/********************************************************************** - * Copyright (c) 2018 Andrew Poelstra, Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef _SECP256K1_MODULE_MUSIG_MAIN_ -#define _SECP256K1_MODULE_MUSIG_MAIN_ - -#include -#include "secp256k1.h" -#include "secp256k1_musig.h" -#include "hash.h" -#include "keyagg_impl.h" -#include "session_impl.h" -#include "adaptor_impl.h" - -#endif diff --git a/libwallet/musig/musig_test.go b/libwallet/musig/musig_test.go deleted file mode 100644 index 2437f66d..00000000 --- a/libwallet/musig/musig_test.go +++ /dev/null @@ -1,253 +0,0 @@ -package musig - -import ( - "bytes" - "encoding/hex" - "testing" - - "github.com/btcsuite/btcd/btcec" -) - -func TestSigning(t *testing.T) { - toSign := [32]byte{1, 2, 3} - - userPriv, _ := btcec.NewPrivateKey(btcec.S256()) - muunPriv, _ := btcec.NewPrivateKey(btcec.S256()) - - combined, err := CombinePubKeysWithTweak(userPriv.PubKey(), muunPriv.PubKey(), nil) - if err != nil { - t.Fatal(err) - } - - userSessionId := RandomSessionId() - muunSessionId := RandomSessionId() - - userPubNonces := GeneratePubNonce(userSessionId) - muunPubNonces := GeneratePubNonce(muunSessionId) - - regeneratedUserNonces := GeneratePubNonce(userSessionId) - if !bytes.Equal(userPubNonces[:], regeneratedUserNonces[:]) { - t.Fatalf( - "Nonces do not match %v != %v", - hex.EncodeToString(userPubNonces[:]), - hex.EncodeToString(regeneratedUserNonces[:]), - ) - } - - muunSig, err := ComputeMuunPartialSignature( - toSign, - userPriv.PubKey(), - muunPriv, - userPubNonces, - muunSessionId, - nil, - ) - if err != nil { - t.Fatal(err) - } - - fullSig, err := AddUserSignatureAndCombine( - toSign, - userPriv, - muunPriv.PubKey(), - muunSig, - muunPubNonces, - userSessionId, - nil, - ) - if err != nil { - t.Fatal(err) - } - - if !VerifySignature(toSign, fullSig, combined) { - t.Fatal("failed to verify sig") - } -} - -func TestSigningWithCustomTweak(t *testing.T) { - someRandomKey, _ := btcec.NewPrivateKey(btcec.S256()) - customTweak := someRandomKey.Serialize() - - toSign := [32]byte{1, 2, 3} - - userPriv, _ := btcec.NewPrivateKey(btcec.S256()) - muunPriv, _ := btcec.NewPrivateKey(btcec.S256()) - - combined, err := CombinePubKeysWithTweak(userPriv.PubKey(), muunPriv.PubKey(), customTweak) - if err != nil { - t.Fatal(err) - } - - userSessionId := RandomSessionId() - muunSessionId := RandomSessionId() - - userPubNonces := GeneratePubNonce(userSessionId) - muunPubNonces := GeneratePubNonce(muunSessionId) - - regeneratedUserNonces := GeneratePubNonce(userSessionId) - if !bytes.Equal(userPubNonces[:], regeneratedUserNonces[:]) { - t.Fatalf( - "Nonces do not match %v != %v", - hex.EncodeToString(userPubNonces[:]), - hex.EncodeToString(regeneratedUserNonces[:]), - ) - } - - muunSig, err := ComputeMuunPartialSignature( - toSign, - userPriv.PubKey(), - muunPriv, - userPubNonces, - muunSessionId, - customTweak, - ) - if err != nil { - t.Fatal(err) - } - - fullSig, err := AddUserSignatureAndCombine( - toSign, - userPriv, - muunPriv.PubKey(), - muunSig, - muunPubNonces, - userSessionId, - customTweak, - ) - if err != nil { - t.Fatal(err) - } - - if !VerifySignature(toSign, fullSig, combined) { - t.Fatal("failed to verify sig") - } -} - -func TestCrossWithJava(t *testing.T) { - - decode32Bytes := func(str string) (result [32]byte) { - d, _ := hex.DecodeString(str) - copy(result[:], d) - return - } - - rawUserPriv := decode32Bytes("6e39c6add6323a5ac5f65e50231fb815026476e734eb9f4f66dce3298fddf1dc") - rawMuunPriv := decode32Bytes("b876ecf97c19588cf4be95ddc0b06c0d9f623f2cf679276c25e4dfb512b19743") - userSessionId := decode32Bytes("52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649") - muunSessionId := decode32Bytes("81855ad8681d0d86d1e91e00167939cb6694d2c422acd208a0072939487f6999") - toSign := decode32Bytes("0102030000000000000000000000000000000000000000000000000000000000") - - expectedKey, _ := hex.DecodeString("027ca7eab04c2ad445418fa6a0ed2a331f121444aedd043adef94bdc00040ff96c") - expectedSig, _ := hex.DecodeString("773ad923eb5eef593095a6787b674675de2a558335dc3e44fe40cf7b3736637e66d508e4eaadca28dab02e2fdcf5707392b561fffa1837205d2fa77b74cbc82f") - - userPriv, _ := btcec.PrivKeyFromBytes(btcec.S256(), rawUserPriv[:]) - muunPriv, _ := btcec.PrivKeyFromBytes(btcec.S256(), rawMuunPriv[:]) - combined, err := CombinePubKeysWithTweak(userPriv.PubKey(), muunPriv.PubKey(), nil) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(combined.SerializeCompressed(), expectedKey) { - t.Fatal("Combined key doesn't match") - } - - userPubNonces := GeneratePubNonce(userSessionId) - muunPubNonces := GeneratePubNonce(muunSessionId) - - muunSig, err := ComputeMuunPartialSignature( - toSign, - userPriv.PubKey(), - muunPriv, - userPubNonces, - muunSessionId, - nil, - ) - if err != nil { - t.Fatal(err) - } - - fullSig, err := AddUserSignatureAndCombine( - toSign, - userPriv, - muunPriv.PubKey(), - muunSig, - muunPubNonces, - userSessionId, - nil, - ) - if err != nil { - t.Fatal(err) - } - - if !VerifySignature(toSign, fullSig, combined) { - t.Fatal("failed to verify sig") - } - if !bytes.Equal(fullSig[:], expectedSig) { - t.Fatal("Signatures do no match") - } -} - -func TestCrossWithJavaUsingTweak(t *testing.T) { - - decode32Bytes := func(str string) (result [32]byte) { - d, _ := hex.DecodeString(str) - copy(result[:], d) - return - } - - tweak := decode32Bytes("99f8135f02f85b24687a7b221f3cbc5c8641851b52c6b1a305fc32f6b7259fa0") - rawUserPriv := decode32Bytes("316f4ead37ad5ea564c9ca653a535cb8e5cad0fce249625372ba20453db01afe") - rawMuunPriv := decode32Bytes("cbd712e93a39b0eac8b4ae2922de2baa7f3765172ddecf3109e5fce4bff24560") - userSessionId := decode32Bytes("52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649") - muunSessionId := decode32Bytes("81855ad8681d0d86d1e91e00167939cb6694d2c422acd208a0072939487f6999") - toSign := decode32Bytes("0102030000000000000000000000000000000000000000000000000000000000") - - expectedKey, _ := hex.DecodeString("0348131bb4b25e17ad573923b3a24f0ac780a9cf5cefaefe44f0b762c1e83bfc06") - expectedSig, _ := hex.DecodeString("1467fb39edcfc400f036ee97c0e5b0f10879e8f9c53de022c97fbd4e997b2ed8ad2a170747b3edbafd370e203c6284aad605486696887791c09d9e99195c2b85") - - userPriv, _ := btcec.PrivKeyFromBytes(btcec.S256(), rawUserPriv[:]) - muunPriv, _ := btcec.PrivKeyFromBytes(btcec.S256(), rawMuunPriv[:]) - combined, err := CombinePubKeysWithTweak(userPriv.PubKey(), muunPriv.PubKey(), tweak[:]) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(combined.SerializeCompressed(), expectedKey) { - t.Fatal("Combined key doesn't match") - } - - userPubNonces := GeneratePubNonce(userSessionId) - muunPubNonces := GeneratePubNonce(muunSessionId) - - muunSig, err := ComputeMuunPartialSignature( - toSign, - userPriv.PubKey(), - muunPriv, - userPubNonces, - muunSessionId, - tweak[:], - ) - if err != nil { - t.Fatal(err) - } - - fullSig, err := AddUserSignatureAndCombine( - toSign, - userPriv, - muunPriv.PubKey(), - muunSig, - muunPubNonces, - userSessionId, - tweak[:], - ) - if err != nil { - t.Fatal(err) - } - - if !VerifySignature(toSign, fullSig, combined) { - t.Fatal("failed to verify sig") - } - if !bytes.Equal(fullSig[:], expectedSig) { - t.Fatal("Signatures do no match") - } -} diff --git a/libwallet/musig/scalar.h b/libwallet/musig/scalar.h deleted file mode 100644 index 36eb0db8..00000000 --- a/libwallet/musig/scalar.h +++ /dev/null @@ -1,111 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_SCALAR_H -#define SECP256K1_SCALAR_H - -#include "util.h" - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - -#if defined(EXHAUSTIVE_TEST_ORDER) -#include "scalar_low.h" -#elif defined(SECP256K1_WIDEMUL_INT128) -#include "scalar_4x64.h" -#elif defined(SECP256K1_WIDEMUL_INT64) -#include "scalar_8x32.h" -#else -#error "Please select wide multiplication implementation" -#endif - -/** Clear a scalar to prevent the leak of sensitive data. */ -static void secp256k1_scalar_clear(secp256k1_scalar *r); - -/** Access bits from a scalar. All requested bits must belong to the same 32-bit limb. */ -static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count); - -/** Access bits from a scalar. Not constant time. */ -static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count); - -/** Set a scalar from a big endian byte array. The scalar will be reduced modulo group order `n`. - * In: bin: pointer to a 32-byte array. - * Out: r: scalar to be set. - * overflow: non-zero if the scalar was bigger or equal to `n` before reduction, zero otherwise (can be NULL). - */ -static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *bin, int *overflow); - -/** Set a scalar from a big endian byte array and returns 1 if it is a valid - * seckey and 0 otherwise. */ -static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin); - -/** Set a scalar to an unsigned integer. */ -static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v); - -/** Set a scalar to an unsigned 64-bit integer */ -static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v); - -/** Convert a scalar to a byte array. */ -static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a); - -/** Add two scalars together (modulo the group order). Returns whether it overflowed. */ -static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); - -/** Conditionally add a power of two to a scalar. The result is not allowed to overflow. */ -static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag); - -/** Multiply two scalars (modulo the group order). */ -static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); - -/** Shift a scalar right by some amount strictly between 0 and 16, returning - * the low bits that were shifted off */ -static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n); - -/** Compute the inverse of a scalar (modulo the group order). */ -static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a); - -/** Compute the inverse of a scalar (modulo the group order), without constant-time guarantee. */ -static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *a); - -/** Compute the complement of a scalar (modulo the group order). */ -static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a); - -/** Check whether a scalar equals zero. */ -static int secp256k1_scalar_is_zero(const secp256k1_scalar *a); - -/** Check whether a scalar equals one. */ -static int secp256k1_scalar_is_one(const secp256k1_scalar *a); - -/** Check whether a scalar, considered as an nonnegative integer, is even. */ -static int secp256k1_scalar_is_even(const secp256k1_scalar *a); - -/** Check whether a scalar is higher than the group order divided by 2. */ -static int secp256k1_scalar_is_high(const secp256k1_scalar *a); - -/** Conditionally negate a number, in constant time. - * Returns -1 if the number was negated, 1 otherwise */ -static int secp256k1_scalar_cond_negate(secp256k1_scalar *a, int flag); - -/** Compare two scalars. */ -static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b); - -/** Find r1 and r2 such that r1+r2*2^128 = k. */ -static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k); -/** Find r1 and r2 such that r1+r2*lambda = k, - * where r1 and r2 or their negations are maximum 128 bits long (see secp256k1_ge_mul_lambda). */ -static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k); - -/** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ -static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); - -/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ -static void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag); - -/** Generate two scalars from a 32-byte seed and an integer using the chacha20 stream cipher */ -static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx); - -#endif /* SECP256K1_SCALAR_H */ diff --git a/libwallet/musig/scalar_4x64.h b/libwallet/musig/scalar_4x64.h deleted file mode 100644 index 70096429..00000000 --- a/libwallet/musig/scalar_4x64.h +++ /dev/null @@ -1,19 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_SCALAR_REPR_H -#define SECP256K1_SCALAR_REPR_H - -#include - -/** A scalar modulo the group order of the secp256k1 curve. */ -typedef struct { - uint64_t d[4]; -} secp256k1_scalar; - -#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}} - -#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/libwallet/musig/scalar_4x64_impl.h b/libwallet/musig/scalar_4x64_impl.h deleted file mode 100644 index 6b0b44ed..00000000 --- a/libwallet/musig/scalar_4x64_impl.h +++ /dev/null @@ -1,965 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_SCALAR_REPR_IMPL_H -#define SECP256K1_SCALAR_REPR_IMPL_H - -#include "scalar.h" -#include - -#include "modinv64_impl.h" - -/* Limbs of the secp256k1 order. */ -#define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) -#define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL) -#define SECP256K1_N_2 ((uint64_t)0xFFFFFFFFFFFFFFFEULL) -#define SECP256K1_N_3 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) - -/* Limbs of 2^256 minus the secp256k1 order. */ -#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) -#define SECP256K1_N_C_1 (~SECP256K1_N_1) -#define SECP256K1_N_C_2 (1) - -/* Limbs of half the secp256k1 order. */ -#define SECP256K1_N_H_0 ((uint64_t)0xDFE92F46681B20A0ULL) -#define SECP256K1_N_H_1 ((uint64_t)0x5D576E7357A4501DULL) -#define SECP256K1_N_H_2 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) -#define SECP256K1_N_H_3 ((uint64_t)0x7FFFFFFFFFFFFFFFULL) - -SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { - r->d[0] = 0; - r->d[1] = 0; - r->d[2] = 0; - r->d[3] = 0; -} - -SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { - r->d[0] = v; - r->d[1] = 0; - r->d[2] = 0; - r->d[3] = 0; -} - -SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { - r->d[0] = v; - r->d[1] = 0; - r->d[2] = 0; - r->d[3] = 0; -} - -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { - VERIFY_CHECK((offset + count - 1) >> 6 == offset >> 6); - return (a->d[offset >> 6] >> (offset & 0x3F)) & ((((uint64_t)1) << count) - 1); -} - -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { - VERIFY_CHECK(count < 32); - VERIFY_CHECK(offset + count <= 256); - if ((offset + count - 1) >> 6 == offset >> 6) { - return secp256k1_scalar_get_bits(a, offset, count); - } else { - VERIFY_CHECK((offset >> 6) + 1 < 4); - return ((a->d[offset >> 6] >> (offset & 0x3F)) | (a->d[(offset >> 6) + 1] << (64 - (offset & 0x3F)))) & ((((uint64_t)1) << count) - 1); - } -} - -SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { - int yes = 0; - int no = 0; - no |= (a->d[3] < SECP256K1_N_3); /* No need for a > check. */ - no |= (a->d[2] < SECP256K1_N_2); - yes |= (a->d[2] > SECP256K1_N_2) & ~no; - no |= (a->d[1] < SECP256K1_N_1); - yes |= (a->d[1] > SECP256K1_N_1) & ~no; - yes |= (a->d[0] >= SECP256K1_N_0) & ~no; - return yes; -} - -SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, unsigned int overflow) { - uint128_t t; - VERIFY_CHECK(overflow <= 1); - t = (uint128_t)r->d[0] + overflow * SECP256K1_N_C_0; - r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)r->d[1] + overflow * SECP256K1_N_C_1; - r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)r->d[2] + overflow * SECP256K1_N_C_2; - r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint64_t)r->d[3]; - r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; - return overflow; -} - -static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { - int overflow; - uint128_t t = (uint128_t)a->d[0] + b->d[0]; - r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)a->d[1] + b->d[1]; - r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)a->d[2] + b->d[2]; - r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)a->d[3] + b->d[3]; - r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - overflow = t + secp256k1_scalar_check_overflow(r); - VERIFY_CHECK(overflow == 0 || overflow == 1); - secp256k1_scalar_reduce(r, overflow); - return overflow; -} - -static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { - uint128_t t; - VERIFY_CHECK(bit < 256); - bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 6) > 3 makes this a noop */ - t = (uint128_t)r->d[0] + (((uint64_t)((bit >> 6) == 0)) << (bit & 0x3F)); - r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)r->d[1] + (((uint64_t)((bit >> 6) == 1)) << (bit & 0x3F)); - r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)r->d[2] + (((uint64_t)((bit >> 6) == 2)) << (bit & 0x3F)); - r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)r->d[3] + (((uint64_t)((bit >> 6) == 3)) << (bit & 0x3F)); - r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; -#ifdef VERIFY - VERIFY_CHECK((t >> 64) == 0); - VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); -#endif -} - -static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { - int over; - r->d[0] = (uint64_t)b32[31] | (uint64_t)b32[30] << 8 | (uint64_t)b32[29] << 16 | (uint64_t)b32[28] << 24 | (uint64_t)b32[27] << 32 | (uint64_t)b32[26] << 40 | (uint64_t)b32[25] << 48 | (uint64_t)b32[24] << 56; - r->d[1] = (uint64_t)b32[23] | (uint64_t)b32[22] << 8 | (uint64_t)b32[21] << 16 | (uint64_t)b32[20] << 24 | (uint64_t)b32[19] << 32 | (uint64_t)b32[18] << 40 | (uint64_t)b32[17] << 48 | (uint64_t)b32[16] << 56; - r->d[2] = (uint64_t)b32[15] | (uint64_t)b32[14] << 8 | (uint64_t)b32[13] << 16 | (uint64_t)b32[12] << 24 | (uint64_t)b32[11] << 32 | (uint64_t)b32[10] << 40 | (uint64_t)b32[9] << 48 | (uint64_t)b32[8] << 56; - r->d[3] = (uint64_t)b32[7] | (uint64_t)b32[6] << 8 | (uint64_t)b32[5] << 16 | (uint64_t)b32[4] << 24 | (uint64_t)b32[3] << 32 | (uint64_t)b32[2] << 40 | (uint64_t)b32[1] << 48 | (uint64_t)b32[0] << 56; - over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); - if (overflow) { - *overflow = over; - } -} - -static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { - bin[0] = a->d[3] >> 56; bin[1] = a->d[3] >> 48; bin[2] = a->d[3] >> 40; bin[3] = a->d[3] >> 32; bin[4] = a->d[3] >> 24; bin[5] = a->d[3] >> 16; bin[6] = a->d[3] >> 8; bin[7] = a->d[3]; - bin[8] = a->d[2] >> 56; bin[9] = a->d[2] >> 48; bin[10] = a->d[2] >> 40; bin[11] = a->d[2] >> 32; bin[12] = a->d[2] >> 24; bin[13] = a->d[2] >> 16; bin[14] = a->d[2] >> 8; bin[15] = a->d[2]; - bin[16] = a->d[1] >> 56; bin[17] = a->d[1] >> 48; bin[18] = a->d[1] >> 40; bin[19] = a->d[1] >> 32; bin[20] = a->d[1] >> 24; bin[21] = a->d[1] >> 16; bin[22] = a->d[1] >> 8; bin[23] = a->d[1]; - bin[24] = a->d[0] >> 56; bin[25] = a->d[0] >> 48; bin[26] = a->d[0] >> 40; bin[27] = a->d[0] >> 32; bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; -} - -SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { - return (a->d[0] | a->d[1] | a->d[2] | a->d[3]) == 0; -} - -static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { - uint64_t nonzero = 0xFFFFFFFFFFFFFFFFULL * (secp256k1_scalar_is_zero(a) == 0); - uint128_t t = (uint128_t)(~a->d[0]) + SECP256K1_N_0 + 1; - r->d[0] = t & nonzero; t >>= 64; - t += (uint128_t)(~a->d[1]) + SECP256K1_N_1; - r->d[1] = t & nonzero; t >>= 64; - t += (uint128_t)(~a->d[2]) + SECP256K1_N_2; - r->d[2] = t & nonzero; t >>= 64; - t += (uint128_t)(~a->d[3]) + SECP256K1_N_3; - r->d[3] = t & nonzero; -} - -SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { - return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3]) == 0; -} - -static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { - int yes = 0; - int no = 0; - no |= (a->d[3] < SECP256K1_N_H_3); - yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; - no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; /* No need for a > check. */ - no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; - yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; - yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; - return yes; -} - -static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { - /* If we are flag = 0, mask = 00...00 and this is a no-op; - * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ - uint64_t mask = !flag - 1; - uint64_t nonzero = (secp256k1_scalar_is_zero(r) != 0) - 1; - uint128_t t = (uint128_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); - r->d[0] = t & nonzero; t >>= 64; - t += (uint128_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); - r->d[1] = t & nonzero; t >>= 64; - t += (uint128_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); - r->d[2] = t & nonzero; t >>= 64; - t += (uint128_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); - r->d[3] = t & nonzero; - return 2 * (mask == 0) - 1; -} - -/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ - -/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ -#define muladd(a,b) { \ - uint64_t tl, th; \ - { \ - uint128_t t = (uint128_t)a * b; \ - th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ - tl = t; \ - } \ - c0 += tl; /* overflow is handled on the next line */ \ - th += (c0 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \ - c1 += th; /* overflow is handled on the next line */ \ - c2 += (c1 < th); /* never overflows by contract (verified in the next line) */ \ - VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ -} - -/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ -#define muladd_fast(a,b) { \ - uint64_t tl, th; \ - { \ - uint128_t t = (uint128_t)a * b; \ - th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ - tl = t; \ - } \ - c0 += tl; /* overflow is handled on the next line */ \ - th += (c0 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \ - c1 += th; /* never overflows by contract (verified in the next line) */ \ - VERIFY_CHECK(c1 >= th); \ -} - -/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ -#define sumadd(a) { \ - unsigned int over; \ - c0 += (a); /* overflow is handled on the next line */ \ - over = (c0 < (a)); \ - c1 += over; /* overflow is handled on the next line */ \ - c2 += (c1 < over); /* never overflows by contract */ \ -} - -/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ -#define sumadd_fast(a) { \ - c0 += (a); /* overflow is handled on the next line */ \ - c1 += (c0 < (a)); /* never overflows by contract (verified the next line) */ \ - VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ - VERIFY_CHECK(c2 == 0); \ -} - -/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. */ -#define extract(n) { \ - (n) = c0; \ - c0 = c1; \ - c1 = c2; \ - c2 = 0; \ -} - -/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. c2 is required to be zero. */ -#define extract_fast(n) { \ - (n) = c0; \ - c0 = c1; \ - c1 = 0; \ - VERIFY_CHECK(c2 == 0); \ -} - -static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) { -#ifdef USE_ASM_X86_64 - /* Reduce 512 bits into 385. */ - uint64_t m0, m1, m2, m3, m4, m5, m6; - uint64_t p0, p1, p2, p3, p4; - uint64_t c; - - __asm__ __volatile__( - /* Preload. */ - "movq 32(%%rsi), %%r11\n" - "movq 40(%%rsi), %%r12\n" - "movq 48(%%rsi), %%r13\n" - "movq 56(%%rsi), %%r14\n" - /* Initialize r8,r9,r10 */ - "movq 0(%%rsi), %%r8\n" - "xorq %%r9, %%r9\n" - "xorq %%r10, %%r10\n" - /* (r8,r9) += n0 * c0 */ - "movq %8, %%rax\n" - "mulq %%r11\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - /* extract m0 */ - "movq %%r8, %q0\n" - "xorq %%r8, %%r8\n" - /* (r9,r10) += l1 */ - "addq 8(%%rsi), %%r9\n" - "adcq $0, %%r10\n" - /* (r9,r10,r8) += n1 * c0 */ - "movq %8, %%rax\n" - "mulq %%r12\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* (r9,r10,r8) += n0 * c1 */ - "movq %9, %%rax\n" - "mulq %%r11\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* extract m1 */ - "movq %%r9, %q1\n" - "xorq %%r9, %%r9\n" - /* (r10,r8,r9) += l2 */ - "addq 16(%%rsi), %%r10\n" - "adcq $0, %%r8\n" - "adcq $0, %%r9\n" - /* (r10,r8,r9) += n2 * c0 */ - "movq %8, %%rax\n" - "mulq %%r13\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - /* (r10,r8,r9) += n1 * c1 */ - "movq %9, %%rax\n" - "mulq %%r12\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - /* (r10,r8,r9) += n0 */ - "addq %%r11, %%r10\n" - "adcq $0, %%r8\n" - "adcq $0, %%r9\n" - /* extract m2 */ - "movq %%r10, %q2\n" - "xorq %%r10, %%r10\n" - /* (r8,r9,r10) += l3 */ - "addq 24(%%rsi), %%r8\n" - "adcq $0, %%r9\n" - "adcq $0, %%r10\n" - /* (r8,r9,r10) += n3 * c0 */ - "movq %8, %%rax\n" - "mulq %%r14\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - /* (r8,r9,r10) += n2 * c1 */ - "movq %9, %%rax\n" - "mulq %%r13\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - /* (r8,r9,r10) += n1 */ - "addq %%r12, %%r8\n" - "adcq $0, %%r9\n" - "adcq $0, %%r10\n" - /* extract m3 */ - "movq %%r8, %q3\n" - "xorq %%r8, %%r8\n" - /* (r9,r10,r8) += n3 * c1 */ - "movq %9, %%rax\n" - "mulq %%r14\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* (r9,r10,r8) += n2 */ - "addq %%r13, %%r9\n" - "adcq $0, %%r10\n" - "adcq $0, %%r8\n" - /* extract m4 */ - "movq %%r9, %q4\n" - /* (r10,r8) += n3 */ - "addq %%r14, %%r10\n" - "adcq $0, %%r8\n" - /* extract m5 */ - "movq %%r10, %q5\n" - /* extract m6 */ - "movq %%r8, %q6\n" - : "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) - : "S"(l), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1) - : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc"); - - /* Reduce 385 bits into 258. */ - __asm__ __volatile__( - /* Preload */ - "movq %q9, %%r11\n" - "movq %q10, %%r12\n" - "movq %q11, %%r13\n" - /* Initialize (r8,r9,r10) */ - "movq %q5, %%r8\n" - "xorq %%r9, %%r9\n" - "xorq %%r10, %%r10\n" - /* (r8,r9) += m4 * c0 */ - "movq %12, %%rax\n" - "mulq %%r11\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - /* extract p0 */ - "movq %%r8, %q0\n" - "xorq %%r8, %%r8\n" - /* (r9,r10) += m1 */ - "addq %q6, %%r9\n" - "adcq $0, %%r10\n" - /* (r9,r10,r8) += m5 * c0 */ - "movq %12, %%rax\n" - "mulq %%r12\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* (r9,r10,r8) += m4 * c1 */ - "movq %13, %%rax\n" - "mulq %%r11\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* extract p1 */ - "movq %%r9, %q1\n" - "xorq %%r9, %%r9\n" - /* (r10,r8,r9) += m2 */ - "addq %q7, %%r10\n" - "adcq $0, %%r8\n" - "adcq $0, %%r9\n" - /* (r10,r8,r9) += m6 * c0 */ - "movq %12, %%rax\n" - "mulq %%r13\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - /* (r10,r8,r9) += m5 * c1 */ - "movq %13, %%rax\n" - "mulq %%r12\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - /* (r10,r8,r9) += m4 */ - "addq %%r11, %%r10\n" - "adcq $0, %%r8\n" - "adcq $0, %%r9\n" - /* extract p2 */ - "movq %%r10, %q2\n" - /* (r8,r9) += m3 */ - "addq %q8, %%r8\n" - "adcq $0, %%r9\n" - /* (r8,r9) += m6 * c1 */ - "movq %13, %%rax\n" - "mulq %%r13\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - /* (r8,r9) += m5 */ - "addq %%r12, %%r8\n" - "adcq $0, %%r9\n" - /* extract p3 */ - "movq %%r8, %q3\n" - /* (r9) += m6 */ - "addq %%r13, %%r9\n" - /* extract p4 */ - "movq %%r9, %q4\n" - : "=&g"(p0), "=&g"(p1), "=&g"(p2), "=g"(p3), "=g"(p4) - : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1) - : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc"); - - /* Reduce 258 bits into 256. */ - __asm__ __volatile__( - /* Preload */ - "movq %q5, %%r10\n" - /* (rax,rdx) = p4 * c0 */ - "movq %7, %%rax\n" - "mulq %%r10\n" - /* (rax,rdx) += p0 */ - "addq %q1, %%rax\n" - "adcq $0, %%rdx\n" - /* extract r0 */ - "movq %%rax, 0(%q6)\n" - /* Move to (r8,r9) */ - "movq %%rdx, %%r8\n" - "xorq %%r9, %%r9\n" - /* (r8,r9) += p1 */ - "addq %q2, %%r8\n" - "adcq $0, %%r9\n" - /* (r8,r9) += p4 * c1 */ - "movq %8, %%rax\n" - "mulq %%r10\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - /* Extract r1 */ - "movq %%r8, 8(%q6)\n" - "xorq %%r8, %%r8\n" - /* (r9,r8) += p4 */ - "addq %%r10, %%r9\n" - "adcq $0, %%r8\n" - /* (r9,r8) += p2 */ - "addq %q3, %%r9\n" - "adcq $0, %%r8\n" - /* Extract r2 */ - "movq %%r9, 16(%q6)\n" - "xorq %%r9, %%r9\n" - /* (r8,r9) += p3 */ - "addq %q4, %%r8\n" - "adcq $0, %%r9\n" - /* Extract r3 */ - "movq %%r8, 24(%q6)\n" - /* Extract c */ - "movq %%r9, %q0\n" - : "=g"(c) - : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1) - : "rax", "rdx", "r8", "r9", "r10", "cc", "memory"); -#else - uint128_t c; - uint64_t c0, c1, c2; - uint64_t n0 = l[4], n1 = l[5], n2 = l[6], n3 = l[7]; - uint64_t m0, m1, m2, m3, m4, m5; - uint32_t m6; - uint64_t p0, p1, p2, p3; - uint32_t p4; - - /* Reduce 512 bits into 385. */ - /* m[0..6] = l[0..3] + n[0..3] * SECP256K1_N_C. */ - c0 = l[0]; c1 = 0; c2 = 0; - muladd_fast(n0, SECP256K1_N_C_0); - extract_fast(m0); - sumadd_fast(l[1]); - muladd(n1, SECP256K1_N_C_0); - muladd(n0, SECP256K1_N_C_1); - extract(m1); - sumadd(l[2]); - muladd(n2, SECP256K1_N_C_0); - muladd(n1, SECP256K1_N_C_1); - sumadd(n0); - extract(m2); - sumadd(l[3]); - muladd(n3, SECP256K1_N_C_0); - muladd(n2, SECP256K1_N_C_1); - sumadd(n1); - extract(m3); - muladd(n3, SECP256K1_N_C_1); - sumadd(n2); - extract(m4); - sumadd_fast(n3); - extract_fast(m5); - VERIFY_CHECK(c0 <= 1); - m6 = c0; - - /* Reduce 385 bits into 258. */ - /* p[0..4] = m[0..3] + m[4..6] * SECP256K1_N_C. */ - c0 = m0; c1 = 0; c2 = 0; - muladd_fast(m4, SECP256K1_N_C_0); - extract_fast(p0); - sumadd_fast(m1); - muladd(m5, SECP256K1_N_C_0); - muladd(m4, SECP256K1_N_C_1); - extract(p1); - sumadd(m2); - muladd(m6, SECP256K1_N_C_0); - muladd(m5, SECP256K1_N_C_1); - sumadd(m4); - extract(p2); - sumadd_fast(m3); - muladd_fast(m6, SECP256K1_N_C_1); - sumadd_fast(m5); - extract_fast(p3); - p4 = c0 + m6; - VERIFY_CHECK(p4 <= 2); - - /* Reduce 258 bits into 256. */ - /* r[0..3] = p[0..3] + p[4] * SECP256K1_N_C. */ - c = p0 + (uint128_t)SECP256K1_N_C_0 * p4; - r->d[0] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; - c += p1 + (uint128_t)SECP256K1_N_C_1 * p4; - r->d[1] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; - c += p2 + (uint128_t)p4; - r->d[2] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; - c += p3; - r->d[3] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; -#endif - - /* Final reduction of r. */ - secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); -} - -static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, const secp256k1_scalar *b) { -#ifdef USE_ASM_X86_64 - const uint64_t *pb = b->d; - __asm__ __volatile__( - /* Preload */ - "movq 0(%%rdi), %%r15\n" - "movq 8(%%rdi), %%rbx\n" - "movq 16(%%rdi), %%rcx\n" - "movq 0(%%rdx), %%r11\n" - "movq 8(%%rdx), %%r12\n" - "movq 16(%%rdx), %%r13\n" - "movq 24(%%rdx), %%r14\n" - /* (rax,rdx) = a0 * b0 */ - "movq %%r15, %%rax\n" - "mulq %%r11\n" - /* Extract l0 */ - "movq %%rax, 0(%%rsi)\n" - /* (r8,r9,r10) = (rdx) */ - "movq %%rdx, %%r8\n" - "xorq %%r9, %%r9\n" - "xorq %%r10, %%r10\n" - /* (r8,r9,r10) += a0 * b1 */ - "movq %%r15, %%rax\n" - "mulq %%r12\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - /* (r8,r9,r10) += a1 * b0 */ - "movq %%rbx, %%rax\n" - "mulq %%r11\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - /* Extract l1 */ - "movq %%r8, 8(%%rsi)\n" - "xorq %%r8, %%r8\n" - /* (r9,r10,r8) += a0 * b2 */ - "movq %%r15, %%rax\n" - "mulq %%r13\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* (r9,r10,r8) += a1 * b1 */ - "movq %%rbx, %%rax\n" - "mulq %%r12\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* (r9,r10,r8) += a2 * b0 */ - "movq %%rcx, %%rax\n" - "mulq %%r11\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* Extract l2 */ - "movq %%r9, 16(%%rsi)\n" - "xorq %%r9, %%r9\n" - /* (r10,r8,r9) += a0 * b3 */ - "movq %%r15, %%rax\n" - "mulq %%r14\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - /* Preload a3 */ - "movq 24(%%rdi), %%r15\n" - /* (r10,r8,r9) += a1 * b2 */ - "movq %%rbx, %%rax\n" - "mulq %%r13\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - /* (r10,r8,r9) += a2 * b1 */ - "movq %%rcx, %%rax\n" - "mulq %%r12\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - /* (r10,r8,r9) += a3 * b0 */ - "movq %%r15, %%rax\n" - "mulq %%r11\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - /* Extract l3 */ - "movq %%r10, 24(%%rsi)\n" - "xorq %%r10, %%r10\n" - /* (r8,r9,r10) += a1 * b3 */ - "movq %%rbx, %%rax\n" - "mulq %%r14\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - /* (r8,r9,r10) += a2 * b2 */ - "movq %%rcx, %%rax\n" - "mulq %%r13\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - /* (r8,r9,r10) += a3 * b1 */ - "movq %%r15, %%rax\n" - "mulq %%r12\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - /* Extract l4 */ - "movq %%r8, 32(%%rsi)\n" - "xorq %%r8, %%r8\n" - /* (r9,r10,r8) += a2 * b3 */ - "movq %%rcx, %%rax\n" - "mulq %%r14\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* (r9,r10,r8) += a3 * b2 */ - "movq %%r15, %%rax\n" - "mulq %%r13\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* Extract l5 */ - "movq %%r9, 40(%%rsi)\n" - /* (r10,r8) += a3 * b3 */ - "movq %%r15, %%rax\n" - "mulq %%r14\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - /* Extract l6 */ - "movq %%r10, 48(%%rsi)\n" - /* Extract l7 */ - "movq %%r8, 56(%%rsi)\n" - : "+d"(pb) - : "S"(l), "D"(a->d) - : "rax", "rbx", "rcx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "cc", "memory"); -#else - /* 160 bit accumulator. */ - uint64_t c0 = 0, c1 = 0; - uint32_t c2 = 0; - - /* l[0..7] = a[0..3] * b[0..3]. */ - muladd_fast(a->d[0], b->d[0]); - extract_fast(l[0]); - muladd(a->d[0], b->d[1]); - muladd(a->d[1], b->d[0]); - extract(l[1]); - muladd(a->d[0], b->d[2]); - muladd(a->d[1], b->d[1]); - muladd(a->d[2], b->d[0]); - extract(l[2]); - muladd(a->d[0], b->d[3]); - muladd(a->d[1], b->d[2]); - muladd(a->d[2], b->d[1]); - muladd(a->d[3], b->d[0]); - extract(l[3]); - muladd(a->d[1], b->d[3]); - muladd(a->d[2], b->d[2]); - muladd(a->d[3], b->d[1]); - extract(l[4]); - muladd(a->d[2], b->d[3]); - muladd(a->d[3], b->d[2]); - extract(l[5]); - muladd_fast(a->d[3], b->d[3]); - extract_fast(l[6]); - VERIFY_CHECK(c1 == 0); - l[7] = c0; -#endif -} - -#undef sumadd -#undef sumadd_fast -#undef muladd -#undef muladd_fast -#undef extract -#undef extract_fast - -static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { - uint64_t l[8]; - secp256k1_scalar_mul_512(l, a, b); - secp256k1_scalar_reduce_512(r, l); -} - -static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { - int ret; - VERIFY_CHECK(n > 0); - VERIFY_CHECK(n < 16); - ret = r->d[0] & ((1 << n) - 1); - r->d[0] = (r->d[0] >> n) + (r->d[1] << (64 - n)); - r->d[1] = (r->d[1] >> n) + (r->d[2] << (64 - n)); - r->d[2] = (r->d[2] >> n) + (r->d[3] << (64 - n)); - r->d[3] = (r->d[3] >> n); - return ret; -} - -static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { - r1->d[0] = k->d[0]; - r1->d[1] = k->d[1]; - r1->d[2] = 0; - r1->d[3] = 0; - r2->d[0] = k->d[2]; - r2->d[1] = k->d[3]; - r2->d[2] = 0; - r2->d[3] = 0; -} - -SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { - return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3])) == 0; -} - -SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { - uint64_t l[8]; - unsigned int shiftlimbs; - unsigned int shiftlow; - unsigned int shifthigh; - VERIFY_CHECK(shift >= 256); - secp256k1_scalar_mul_512(l, a, b); - shiftlimbs = shift >> 6; - shiftlow = shift & 0x3F; - shifthigh = 64 - shiftlow; - r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; - r->d[1] = shift < 448 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; - r->d[2] = shift < 384 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; - r->d[3] = shift < 320 ? (l[3 + shiftlimbs] >> shiftlow) : 0; - secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); -} - -static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { - uint64_t mask0, mask1; - VG_CHECK_VERIFY(r->d, sizeof(r->d)); - mask0 = flag + ~((uint64_t)0); - mask1 = ~mask0; - r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1); - r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1); - r->d[2] = (r->d[2] & mask0) | (a->d[2] & mask1); - r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1); -} - -#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) -#define QUARTERROUND(a,b,c,d) \ - a += b; d = ROTL32(d ^ a, 16); \ - c += d; b = ROTL32(b ^ c, 12); \ - a += b; d = ROTL32(d ^ a, 8); \ - c += d; b = ROTL32(b ^ c, 7); - -#if defined(SECP256K1_BIG_ENDIAN) -#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#elif defined(SECP256K1_LITTLE_ENDIAN) -#define LE32(p) (p) -#endif - -static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { - size_t n; - size_t over_count = 0; - uint32_t seed32[8]; - uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - int over1, over2; - - memcpy((void *) seed32, (const void *) seed, 32); - do { - x0 = 0x61707865; - x1 = 0x3320646e; - x2 = 0x79622d32; - x3 = 0x6b206574; - x4 = LE32(seed32[0]); - x5 = LE32(seed32[1]); - x6 = LE32(seed32[2]); - x7 = LE32(seed32[3]); - x8 = LE32(seed32[4]); - x9 = LE32(seed32[5]); - x10 = LE32(seed32[6]); - x11 = LE32(seed32[7]); - x12 = idx; - x13 = idx >> 32; - x14 = 0; - x15 = over_count; - - n = 10; - while (n--) { - QUARTERROUND(x0, x4, x8,x12) - QUARTERROUND(x1, x5, x9,x13) - QUARTERROUND(x2, x6,x10,x14) - QUARTERROUND(x3, x7,x11,x15) - QUARTERROUND(x0, x5,x10,x15) - QUARTERROUND(x1, x6,x11,x12) - QUARTERROUND(x2, x7, x8,x13) - QUARTERROUND(x3, x4, x9,x14) - } - - x0 += 0x61707865; - x1 += 0x3320646e; - x2 += 0x79622d32; - x3 += 0x6b206574; - x4 += LE32(seed32[0]); - x5 += LE32(seed32[1]); - x6 += LE32(seed32[2]); - x7 += LE32(seed32[3]); - x8 += LE32(seed32[4]); - x9 += LE32(seed32[5]); - x10 += LE32(seed32[6]); - x11 += LE32(seed32[7]); - x12 += idx; - x13 += idx >> 32; - x14 += 0; - x15 += over_count; - - r1->d[3] = (((uint64_t) x0) << 32) | x1; - r1->d[2] = (((uint64_t) x2) << 32) | x3; - r1->d[1] = (((uint64_t) x4) << 32) | x5; - r1->d[0] = (((uint64_t) x6) << 32) | x7; - r2->d[3] = (((uint64_t) x8) << 32) | x9; - r2->d[2] = (((uint64_t) x10) << 32) | x11; - r2->d[1] = (((uint64_t) x12) << 32) | x13; - r2->d[0] = (((uint64_t) x14) << 32) | x15; - - over1 = secp256k1_scalar_check_overflow(r1); - over2 = secp256k1_scalar_check_overflow(r2); - over_count++; - } while (over1 | over2); -} - -#undef ROTL32 -#undef QUARTERROUND -#undef LE32 - -static void secp256k1_scalar_from_signed62(secp256k1_scalar *r, const secp256k1_modinv64_signed62 *a) { - const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4]; - - /* The output from secp256k1_modinv64{_var} should be normalized to range [0,modulus), and - * have limbs in [0,2^62). The modulus is < 2^256, so the top limb must be below 2^(256-62*4). - */ - VERIFY_CHECK(a0 >> 62 == 0); - VERIFY_CHECK(a1 >> 62 == 0); - VERIFY_CHECK(a2 >> 62 == 0); - VERIFY_CHECK(a3 >> 62 == 0); - VERIFY_CHECK(a4 >> 8 == 0); - - r->d[0] = a0 | a1 << 62; - r->d[1] = a1 >> 2 | a2 << 60; - r->d[2] = a2 >> 4 | a3 << 58; - r->d[3] = a3 >> 6 | a4 << 56; - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); -#endif -} - -static void secp256k1_scalar_to_signed62(secp256k1_modinv64_signed62 *r, const secp256k1_scalar *a) { - const uint64_t M62 = UINT64_MAX >> 2; - const uint64_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3]; - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_scalar_check_overflow(a) == 0); -#endif - - r->v[0] = a0 & M62; - r->v[1] = (a0 >> 62 | a1 << 2) & M62; - r->v[2] = (a1 >> 60 | a2 << 4) & M62; - r->v[3] = (a2 >> 58 | a3 << 6) & M62; - r->v[4] = a3 >> 56; -} - -static const secp256k1_modinv64_modinfo secp256k1_const_modinfo_scalar = { - {{0x3FD25E8CD0364141LL, 0x2ABB739ABD2280EELL, -0x15LL, 0, 256}}, - 0x34F20099AA774EC1LL -}; - -static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { - secp256k1_modinv64_signed62 s; -#ifdef VERIFY - int zero_in = secp256k1_scalar_is_zero(x); -#endif - secp256k1_scalar_to_signed62(&s, x); - secp256k1_modinv64(&s, &secp256k1_const_modinfo_scalar); - secp256k1_scalar_from_signed62(r, &s); - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in); -#endif -} - -static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { - secp256k1_modinv64_signed62 s; -#ifdef VERIFY - int zero_in = secp256k1_scalar_is_zero(x); -#endif - secp256k1_scalar_to_signed62(&s, x); - secp256k1_modinv64_var(&s, &secp256k1_const_modinfo_scalar); - secp256k1_scalar_from_signed62(r, &s); - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in); -#endif -} - -SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { - return !(a->d[0] & 1); -} - -#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/libwallet/musig/scalar_8x32.h b/libwallet/musig/scalar_8x32.h deleted file mode 100644 index 17863ef9..00000000 --- a/libwallet/musig/scalar_8x32.h +++ /dev/null @@ -1,19 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_SCALAR_REPR_H -#define SECP256K1_SCALAR_REPR_H - -#include - -/** A scalar modulo the group order of the secp256k1 curve. */ -typedef struct { - uint32_t d[8]; -} secp256k1_scalar; - -#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}} - -#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/libwallet/musig/scalar_8x32_impl.h b/libwallet/musig/scalar_8x32_impl.h deleted file mode 100644 index acd5ef7d..00000000 --- a/libwallet/musig/scalar_8x32_impl.h +++ /dev/null @@ -1,843 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_SCALAR_REPR_IMPL_H -#define SECP256K1_SCALAR_REPR_IMPL_H - -#include - -#include "modinv32_impl.h" - -/* Limbs of the secp256k1 order. */ -#define SECP256K1_N_0 ((uint32_t)0xD0364141UL) -#define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL) -#define SECP256K1_N_2 ((uint32_t)0xAF48A03BUL) -#define SECP256K1_N_3 ((uint32_t)0xBAAEDCE6UL) -#define SECP256K1_N_4 ((uint32_t)0xFFFFFFFEUL) -#define SECP256K1_N_5 ((uint32_t)0xFFFFFFFFUL) -#define SECP256K1_N_6 ((uint32_t)0xFFFFFFFFUL) -#define SECP256K1_N_7 ((uint32_t)0xFFFFFFFFUL) - -/* Limbs of 2^256 minus the secp256k1 order. */ -#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) -#define SECP256K1_N_C_1 (~SECP256K1_N_1) -#define SECP256K1_N_C_2 (~SECP256K1_N_2) -#define SECP256K1_N_C_3 (~SECP256K1_N_3) -#define SECP256K1_N_C_4 (1) - -/* Limbs of half the secp256k1 order. */ -#define SECP256K1_N_H_0 ((uint32_t)0x681B20A0UL) -#define SECP256K1_N_H_1 ((uint32_t)0xDFE92F46UL) -#define SECP256K1_N_H_2 ((uint32_t)0x57A4501DUL) -#define SECP256K1_N_H_3 ((uint32_t)0x5D576E73UL) -#define SECP256K1_N_H_4 ((uint32_t)0xFFFFFFFFUL) -#define SECP256K1_N_H_5 ((uint32_t)0xFFFFFFFFUL) -#define SECP256K1_N_H_6 ((uint32_t)0xFFFFFFFFUL) -#define SECP256K1_N_H_7 ((uint32_t)0x7FFFFFFFUL) - -SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { - r->d[0] = 0; - r->d[1] = 0; - r->d[2] = 0; - r->d[3] = 0; - r->d[4] = 0; - r->d[5] = 0; - r->d[6] = 0; - r->d[7] = 0; -} - -SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { - r->d[0] = v; - r->d[1] = 0; - r->d[2] = 0; - r->d[3] = 0; - r->d[4] = 0; - r->d[5] = 0; - r->d[6] = 0; - r->d[7] = 0; -} - -SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { - r->d[0] = v; - r->d[1] = v >> 32; - r->d[2] = 0; - r->d[3] = 0; - r->d[4] = 0; - r->d[5] = 0; - r->d[6] = 0; - r->d[7] = 0; -} - -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { - VERIFY_CHECK((offset + count - 1) >> 5 == offset >> 5); - return (a->d[offset >> 5] >> (offset & 0x1F)) & ((1 << count) - 1); -} - -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { - VERIFY_CHECK(count < 32); - VERIFY_CHECK(offset + count <= 256); - if ((offset + count - 1) >> 5 == offset >> 5) { - return secp256k1_scalar_get_bits(a, offset, count); - } else { - VERIFY_CHECK((offset >> 5) + 1 < 8); - return ((a->d[offset >> 5] >> (offset & 0x1F)) | (a->d[(offset >> 5) + 1] << (32 - (offset & 0x1F)))) & ((((uint32_t)1) << count) - 1); - } -} - -SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { - int yes = 0; - int no = 0; - no |= (a->d[7] < SECP256K1_N_7); /* No need for a > check. */ - no |= (a->d[6] < SECP256K1_N_6); /* No need for a > check. */ - no |= (a->d[5] < SECP256K1_N_5); /* No need for a > check. */ - no |= (a->d[4] < SECP256K1_N_4); - yes |= (a->d[4] > SECP256K1_N_4) & ~no; - no |= (a->d[3] < SECP256K1_N_3) & ~yes; - yes |= (a->d[3] > SECP256K1_N_3) & ~no; - no |= (a->d[2] < SECP256K1_N_2) & ~yes; - yes |= (a->d[2] > SECP256K1_N_2) & ~no; - no |= (a->d[1] < SECP256K1_N_1) & ~yes; - yes |= (a->d[1] > SECP256K1_N_1) & ~no; - yes |= (a->d[0] >= SECP256K1_N_0) & ~no; - return yes; -} - -SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, uint32_t overflow) { - uint64_t t; - VERIFY_CHECK(overflow <= 1); - t = (uint64_t)r->d[0] + overflow * SECP256K1_N_C_0; - r->d[0] = t & 0xFFFFFFFFUL; t >>= 32; - t += (uint64_t)r->d[1] + overflow * SECP256K1_N_C_1; - r->d[1] = t & 0xFFFFFFFFUL; t >>= 32; - t += (uint64_t)r->d[2] + overflow * SECP256K1_N_C_2; - r->d[2] = t & 0xFFFFFFFFUL; t >>= 32; - t += (uint64_t)r->d[3] + overflow * SECP256K1_N_C_3; - r->d[3] = t & 0xFFFFFFFFUL; t >>= 32; - t += (uint64_t)r->d[4] + overflow * SECP256K1_N_C_4; - r->d[4] = t & 0xFFFFFFFFUL; t >>= 32; - t += (uint64_t)r->d[5]; - r->d[5] = t & 0xFFFFFFFFUL; t >>= 32; - t += (uint64_t)r->d[6]; - r->d[6] = t & 0xFFFFFFFFUL; t >>= 32; - t += (uint64_t)r->d[7]; - r->d[7] = t & 0xFFFFFFFFUL; - return overflow; -} - -static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { - int overflow; - uint64_t t = (uint64_t)a->d[0] + b->d[0]; - r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)a->d[1] + b->d[1]; - r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)a->d[2] + b->d[2]; - r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)a->d[3] + b->d[3]; - r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)a->d[4] + b->d[4]; - r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)a->d[5] + b->d[5]; - r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)a->d[6] + b->d[6]; - r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)a->d[7] + b->d[7]; - r->d[7] = t & 0xFFFFFFFFULL; t >>= 32; - overflow = t + secp256k1_scalar_check_overflow(r); - VERIFY_CHECK(overflow == 0 || overflow == 1); - secp256k1_scalar_reduce(r, overflow); - return overflow; -} - -static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { - uint64_t t; - VERIFY_CHECK(bit < 256); - bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 5) > 7 makes this a noop */ - t = (uint64_t)r->d[0] + (((uint32_t)((bit >> 5) == 0)) << (bit & 0x1F)); - r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)r->d[1] + (((uint32_t)((bit >> 5) == 1)) << (bit & 0x1F)); - r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)r->d[2] + (((uint32_t)((bit >> 5) == 2)) << (bit & 0x1F)); - r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)r->d[3] + (((uint32_t)((bit >> 5) == 3)) << (bit & 0x1F)); - r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)r->d[4] + (((uint32_t)((bit >> 5) == 4)) << (bit & 0x1F)); - r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)r->d[5] + (((uint32_t)((bit >> 5) == 5)) << (bit & 0x1F)); - r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)r->d[6] + (((uint32_t)((bit >> 5) == 6)) << (bit & 0x1F)); - r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; - t += (uint64_t)r->d[7] + (((uint32_t)((bit >> 5) == 7)) << (bit & 0x1F)); - r->d[7] = t & 0xFFFFFFFFULL; -#ifdef VERIFY - VERIFY_CHECK((t >> 32) == 0); - VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); -#endif -} - -static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { - int over; - r->d[0] = (uint32_t)b32[31] | (uint32_t)b32[30] << 8 | (uint32_t)b32[29] << 16 | (uint32_t)b32[28] << 24; - r->d[1] = (uint32_t)b32[27] | (uint32_t)b32[26] << 8 | (uint32_t)b32[25] << 16 | (uint32_t)b32[24] << 24; - r->d[2] = (uint32_t)b32[23] | (uint32_t)b32[22] << 8 | (uint32_t)b32[21] << 16 | (uint32_t)b32[20] << 24; - r->d[3] = (uint32_t)b32[19] | (uint32_t)b32[18] << 8 | (uint32_t)b32[17] << 16 | (uint32_t)b32[16] << 24; - r->d[4] = (uint32_t)b32[15] | (uint32_t)b32[14] << 8 | (uint32_t)b32[13] << 16 | (uint32_t)b32[12] << 24; - r->d[5] = (uint32_t)b32[11] | (uint32_t)b32[10] << 8 | (uint32_t)b32[9] << 16 | (uint32_t)b32[8] << 24; - r->d[6] = (uint32_t)b32[7] | (uint32_t)b32[6] << 8 | (uint32_t)b32[5] << 16 | (uint32_t)b32[4] << 24; - r->d[7] = (uint32_t)b32[3] | (uint32_t)b32[2] << 8 | (uint32_t)b32[1] << 16 | (uint32_t)b32[0] << 24; - over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); - if (overflow) { - *overflow = over; - } -} - -static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { - bin[0] = a->d[7] >> 24; bin[1] = a->d[7] >> 16; bin[2] = a->d[7] >> 8; bin[3] = a->d[7]; - bin[4] = a->d[6] >> 24; bin[5] = a->d[6] >> 16; bin[6] = a->d[6] >> 8; bin[7] = a->d[6]; - bin[8] = a->d[5] >> 24; bin[9] = a->d[5] >> 16; bin[10] = a->d[5] >> 8; bin[11] = a->d[5]; - bin[12] = a->d[4] >> 24; bin[13] = a->d[4] >> 16; bin[14] = a->d[4] >> 8; bin[15] = a->d[4]; - bin[16] = a->d[3] >> 24; bin[17] = a->d[3] >> 16; bin[18] = a->d[3] >> 8; bin[19] = a->d[3]; - bin[20] = a->d[2] >> 24; bin[21] = a->d[2] >> 16; bin[22] = a->d[2] >> 8; bin[23] = a->d[2]; - bin[24] = a->d[1] >> 24; bin[25] = a->d[1] >> 16; bin[26] = a->d[1] >> 8; bin[27] = a->d[1]; - bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; -} - -SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { - return (a->d[0] | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; -} - -static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { - uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(a) == 0); - uint64_t t = (uint64_t)(~a->d[0]) + SECP256K1_N_0 + 1; - r->d[0] = t & nonzero; t >>= 32; - t += (uint64_t)(~a->d[1]) + SECP256K1_N_1; - r->d[1] = t & nonzero; t >>= 32; - t += (uint64_t)(~a->d[2]) + SECP256K1_N_2; - r->d[2] = t & nonzero; t >>= 32; - t += (uint64_t)(~a->d[3]) + SECP256K1_N_3; - r->d[3] = t & nonzero; t >>= 32; - t += (uint64_t)(~a->d[4]) + SECP256K1_N_4; - r->d[4] = t & nonzero; t >>= 32; - t += (uint64_t)(~a->d[5]) + SECP256K1_N_5; - r->d[5] = t & nonzero; t >>= 32; - t += (uint64_t)(~a->d[6]) + SECP256K1_N_6; - r->d[6] = t & nonzero; t >>= 32; - t += (uint64_t)(~a->d[7]) + SECP256K1_N_7; - r->d[7] = t & nonzero; -} - -SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { - return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; -} - -static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { - int yes = 0; - int no = 0; - no |= (a->d[7] < SECP256K1_N_H_7); - yes |= (a->d[7] > SECP256K1_N_H_7) & ~no; - no |= (a->d[6] < SECP256K1_N_H_6) & ~yes; /* No need for a > check. */ - no |= (a->d[5] < SECP256K1_N_H_5) & ~yes; /* No need for a > check. */ - no |= (a->d[4] < SECP256K1_N_H_4) & ~yes; /* No need for a > check. */ - no |= (a->d[3] < SECP256K1_N_H_3) & ~yes; - yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; - no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; - yes |= (a->d[2] > SECP256K1_N_H_2) & ~no; - no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; - yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; - yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; - return yes; -} - -static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { - /* If we are flag = 0, mask = 00...00 and this is a no-op; - * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ - uint32_t mask = !flag - 1; - uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(r) == 0); - uint64_t t = (uint64_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); - r->d[0] = t & nonzero; t >>= 32; - t += (uint64_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); - r->d[1] = t & nonzero; t >>= 32; - t += (uint64_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); - r->d[2] = t & nonzero; t >>= 32; - t += (uint64_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); - r->d[3] = t & nonzero; t >>= 32; - t += (uint64_t)(r->d[4] ^ mask) + (SECP256K1_N_4 & mask); - r->d[4] = t & nonzero; t >>= 32; - t += (uint64_t)(r->d[5] ^ mask) + (SECP256K1_N_5 & mask); - r->d[5] = t & nonzero; t >>= 32; - t += (uint64_t)(r->d[6] ^ mask) + (SECP256K1_N_6 & mask); - r->d[6] = t & nonzero; t >>= 32; - t += (uint64_t)(r->d[7] ^ mask) + (SECP256K1_N_7 & mask); - r->d[7] = t & nonzero; - return 2 * (mask == 0) - 1; -} - - -/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ - -/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ -#define muladd(a,b) { \ - uint32_t tl, th; \ - { \ - uint64_t t = (uint64_t)a * b; \ - th = t >> 32; /* at most 0xFFFFFFFE */ \ - tl = t; \ - } \ - c0 += tl; /* overflow is handled on the next line */ \ - th += (c0 < tl); /* at most 0xFFFFFFFF */ \ - c1 += th; /* overflow is handled on the next line */ \ - c2 += (c1 < th); /* never overflows by contract (verified in the next line) */ \ - VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ -} - -/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ -#define muladd_fast(a,b) { \ - uint32_t tl, th; \ - { \ - uint64_t t = (uint64_t)a * b; \ - th = t >> 32; /* at most 0xFFFFFFFE */ \ - tl = t; \ - } \ - c0 += tl; /* overflow is handled on the next line */ \ - th += (c0 < tl); /* at most 0xFFFFFFFF */ \ - c1 += th; /* never overflows by contract (verified in the next line) */ \ - VERIFY_CHECK(c1 >= th); \ -} - -/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ -#define sumadd(a) { \ - unsigned int over; \ - c0 += (a); /* overflow is handled on the next line */ \ - over = (c0 < (a)); \ - c1 += over; /* overflow is handled on the next line */ \ - c2 += (c1 < over); /* never overflows by contract */ \ -} - -/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ -#define sumadd_fast(a) { \ - c0 += (a); /* overflow is handled on the next line */ \ - c1 += (c0 < (a)); /* never overflows by contract (verified the next line) */ \ - VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ - VERIFY_CHECK(c2 == 0); \ -} - -/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. */ -#define extract(n) { \ - (n) = c0; \ - c0 = c1; \ - c1 = c2; \ - c2 = 0; \ -} - -/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. c2 is required to be zero. */ -#define extract_fast(n) { \ - (n) = c0; \ - c0 = c1; \ - c1 = 0; \ - VERIFY_CHECK(c2 == 0); \ -} - -static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint32_t *l) { - uint64_t c; - uint32_t n0 = l[8], n1 = l[9], n2 = l[10], n3 = l[11], n4 = l[12], n5 = l[13], n6 = l[14], n7 = l[15]; - uint32_t m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12; - uint32_t p0, p1, p2, p3, p4, p5, p6, p7, p8; - - /* 96 bit accumulator. */ - uint32_t c0, c1, c2; - - /* Reduce 512 bits into 385. */ - /* m[0..12] = l[0..7] + n[0..7] * SECP256K1_N_C. */ - c0 = l[0]; c1 = 0; c2 = 0; - muladd_fast(n0, SECP256K1_N_C_0); - extract_fast(m0); - sumadd_fast(l[1]); - muladd(n1, SECP256K1_N_C_0); - muladd(n0, SECP256K1_N_C_1); - extract(m1); - sumadd(l[2]); - muladd(n2, SECP256K1_N_C_0); - muladd(n1, SECP256K1_N_C_1); - muladd(n0, SECP256K1_N_C_2); - extract(m2); - sumadd(l[3]); - muladd(n3, SECP256K1_N_C_0); - muladd(n2, SECP256K1_N_C_1); - muladd(n1, SECP256K1_N_C_2); - muladd(n0, SECP256K1_N_C_3); - extract(m3); - sumadd(l[4]); - muladd(n4, SECP256K1_N_C_0); - muladd(n3, SECP256K1_N_C_1); - muladd(n2, SECP256K1_N_C_2); - muladd(n1, SECP256K1_N_C_3); - sumadd(n0); - extract(m4); - sumadd(l[5]); - muladd(n5, SECP256K1_N_C_0); - muladd(n4, SECP256K1_N_C_1); - muladd(n3, SECP256K1_N_C_2); - muladd(n2, SECP256K1_N_C_3); - sumadd(n1); - extract(m5); - sumadd(l[6]); - muladd(n6, SECP256K1_N_C_0); - muladd(n5, SECP256K1_N_C_1); - muladd(n4, SECP256K1_N_C_2); - muladd(n3, SECP256K1_N_C_3); - sumadd(n2); - extract(m6); - sumadd(l[7]); - muladd(n7, SECP256K1_N_C_0); - muladd(n6, SECP256K1_N_C_1); - muladd(n5, SECP256K1_N_C_2); - muladd(n4, SECP256K1_N_C_3); - sumadd(n3); - extract(m7); - muladd(n7, SECP256K1_N_C_1); - muladd(n6, SECP256K1_N_C_2); - muladd(n5, SECP256K1_N_C_3); - sumadd(n4); - extract(m8); - muladd(n7, SECP256K1_N_C_2); - muladd(n6, SECP256K1_N_C_3); - sumadd(n5); - extract(m9); - muladd(n7, SECP256K1_N_C_3); - sumadd(n6); - extract(m10); - sumadd_fast(n7); - extract_fast(m11); - VERIFY_CHECK(c0 <= 1); - m12 = c0; - - /* Reduce 385 bits into 258. */ - /* p[0..8] = m[0..7] + m[8..12] * SECP256K1_N_C. */ - c0 = m0; c1 = 0; c2 = 0; - muladd_fast(m8, SECP256K1_N_C_0); - extract_fast(p0); - sumadd_fast(m1); - muladd(m9, SECP256K1_N_C_0); - muladd(m8, SECP256K1_N_C_1); - extract(p1); - sumadd(m2); - muladd(m10, SECP256K1_N_C_0); - muladd(m9, SECP256K1_N_C_1); - muladd(m8, SECP256K1_N_C_2); - extract(p2); - sumadd(m3); - muladd(m11, SECP256K1_N_C_0); - muladd(m10, SECP256K1_N_C_1); - muladd(m9, SECP256K1_N_C_2); - muladd(m8, SECP256K1_N_C_3); - extract(p3); - sumadd(m4); - muladd(m12, SECP256K1_N_C_0); - muladd(m11, SECP256K1_N_C_1); - muladd(m10, SECP256K1_N_C_2); - muladd(m9, SECP256K1_N_C_3); - sumadd(m8); - extract(p4); - sumadd(m5); - muladd(m12, SECP256K1_N_C_1); - muladd(m11, SECP256K1_N_C_2); - muladd(m10, SECP256K1_N_C_3); - sumadd(m9); - extract(p5); - sumadd(m6); - muladd(m12, SECP256K1_N_C_2); - muladd(m11, SECP256K1_N_C_3); - sumadd(m10); - extract(p6); - sumadd_fast(m7); - muladd_fast(m12, SECP256K1_N_C_3); - sumadd_fast(m11); - extract_fast(p7); - p8 = c0 + m12; - VERIFY_CHECK(p8 <= 2); - - /* Reduce 258 bits into 256. */ - /* r[0..7] = p[0..7] + p[8] * SECP256K1_N_C. */ - c = p0 + (uint64_t)SECP256K1_N_C_0 * p8; - r->d[0] = c & 0xFFFFFFFFUL; c >>= 32; - c += p1 + (uint64_t)SECP256K1_N_C_1 * p8; - r->d[1] = c & 0xFFFFFFFFUL; c >>= 32; - c += p2 + (uint64_t)SECP256K1_N_C_2 * p8; - r->d[2] = c & 0xFFFFFFFFUL; c >>= 32; - c += p3 + (uint64_t)SECP256K1_N_C_3 * p8; - r->d[3] = c & 0xFFFFFFFFUL; c >>= 32; - c += p4 + (uint64_t)p8; - r->d[4] = c & 0xFFFFFFFFUL; c >>= 32; - c += p5; - r->d[5] = c & 0xFFFFFFFFUL; c >>= 32; - c += p6; - r->d[6] = c & 0xFFFFFFFFUL; c >>= 32; - c += p7; - r->d[7] = c & 0xFFFFFFFFUL; c >>= 32; - - /* Final reduction of r. */ - secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); -} - -static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, const secp256k1_scalar *b) { - /* 96 bit accumulator. */ - uint32_t c0 = 0, c1 = 0, c2 = 0; - - /* l[0..15] = a[0..7] * b[0..7]. */ - muladd_fast(a->d[0], b->d[0]); - extract_fast(l[0]); - muladd(a->d[0], b->d[1]); - muladd(a->d[1], b->d[0]); - extract(l[1]); - muladd(a->d[0], b->d[2]); - muladd(a->d[1], b->d[1]); - muladd(a->d[2], b->d[0]); - extract(l[2]); - muladd(a->d[0], b->d[3]); - muladd(a->d[1], b->d[2]); - muladd(a->d[2], b->d[1]); - muladd(a->d[3], b->d[0]); - extract(l[3]); - muladd(a->d[0], b->d[4]); - muladd(a->d[1], b->d[3]); - muladd(a->d[2], b->d[2]); - muladd(a->d[3], b->d[1]); - muladd(a->d[4], b->d[0]); - extract(l[4]); - muladd(a->d[0], b->d[5]); - muladd(a->d[1], b->d[4]); - muladd(a->d[2], b->d[3]); - muladd(a->d[3], b->d[2]); - muladd(a->d[4], b->d[1]); - muladd(a->d[5], b->d[0]); - extract(l[5]); - muladd(a->d[0], b->d[6]); - muladd(a->d[1], b->d[5]); - muladd(a->d[2], b->d[4]); - muladd(a->d[3], b->d[3]); - muladd(a->d[4], b->d[2]); - muladd(a->d[5], b->d[1]); - muladd(a->d[6], b->d[0]); - extract(l[6]); - muladd(a->d[0], b->d[7]); - muladd(a->d[1], b->d[6]); - muladd(a->d[2], b->d[5]); - muladd(a->d[3], b->d[4]); - muladd(a->d[4], b->d[3]); - muladd(a->d[5], b->d[2]); - muladd(a->d[6], b->d[1]); - muladd(a->d[7], b->d[0]); - extract(l[7]); - muladd(a->d[1], b->d[7]); - muladd(a->d[2], b->d[6]); - muladd(a->d[3], b->d[5]); - muladd(a->d[4], b->d[4]); - muladd(a->d[5], b->d[3]); - muladd(a->d[6], b->d[2]); - muladd(a->d[7], b->d[1]); - extract(l[8]); - muladd(a->d[2], b->d[7]); - muladd(a->d[3], b->d[6]); - muladd(a->d[4], b->d[5]); - muladd(a->d[5], b->d[4]); - muladd(a->d[6], b->d[3]); - muladd(a->d[7], b->d[2]); - extract(l[9]); - muladd(a->d[3], b->d[7]); - muladd(a->d[4], b->d[6]); - muladd(a->d[5], b->d[5]); - muladd(a->d[6], b->d[4]); - muladd(a->d[7], b->d[3]); - extract(l[10]); - muladd(a->d[4], b->d[7]); - muladd(a->d[5], b->d[6]); - muladd(a->d[6], b->d[5]); - muladd(a->d[7], b->d[4]); - extract(l[11]); - muladd(a->d[5], b->d[7]); - muladd(a->d[6], b->d[6]); - muladd(a->d[7], b->d[5]); - extract(l[12]); - muladd(a->d[6], b->d[7]); - muladd(a->d[7], b->d[6]); - extract(l[13]); - muladd_fast(a->d[7], b->d[7]); - extract_fast(l[14]); - VERIFY_CHECK(c1 == 0); - l[15] = c0; -} - -#undef sumadd -#undef sumadd_fast -#undef muladd -#undef muladd_fast -#undef extract -#undef extract_fast - -static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { - uint32_t l[16]; - secp256k1_scalar_mul_512(l, a, b); - secp256k1_scalar_reduce_512(r, l); -} - -static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { - int ret; - VERIFY_CHECK(n > 0); - VERIFY_CHECK(n < 16); - ret = r->d[0] & ((1 << n) - 1); - r->d[0] = (r->d[0] >> n) + (r->d[1] << (32 - n)); - r->d[1] = (r->d[1] >> n) + (r->d[2] << (32 - n)); - r->d[2] = (r->d[2] >> n) + (r->d[3] << (32 - n)); - r->d[3] = (r->d[3] >> n) + (r->d[4] << (32 - n)); - r->d[4] = (r->d[4] >> n) + (r->d[5] << (32 - n)); - r->d[5] = (r->d[5] >> n) + (r->d[6] << (32 - n)); - r->d[6] = (r->d[6] >> n) + (r->d[7] << (32 - n)); - r->d[7] = (r->d[7] >> n); - return ret; -} - -static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { - r1->d[0] = k->d[0]; - r1->d[1] = k->d[1]; - r1->d[2] = k->d[2]; - r1->d[3] = k->d[3]; - r1->d[4] = 0; - r1->d[5] = 0; - r1->d[6] = 0; - r1->d[7] = 0; - r2->d[0] = k->d[4]; - r2->d[1] = k->d[5]; - r2->d[2] = k->d[6]; - r2->d[3] = k->d[7]; - r2->d[4] = 0; - r2->d[5] = 0; - r2->d[6] = 0; - r2->d[7] = 0; -} - -SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { - return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3]) | (a->d[4] ^ b->d[4]) | (a->d[5] ^ b->d[5]) | (a->d[6] ^ b->d[6]) | (a->d[7] ^ b->d[7])) == 0; -} - -SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { - uint32_t l[16]; - unsigned int shiftlimbs; - unsigned int shiftlow; - unsigned int shifthigh; - VERIFY_CHECK(shift >= 256); - secp256k1_scalar_mul_512(l, a, b); - shiftlimbs = shift >> 5; - shiftlow = shift & 0x1F; - shifthigh = 32 - shiftlow; - r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 480 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; - r->d[1] = shift < 480 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; - r->d[2] = shift < 448 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 416 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; - r->d[3] = shift < 416 ? (l[3 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[4 + shiftlimbs] << shifthigh) : 0)) : 0; - r->d[4] = shift < 384 ? (l[4 + shiftlimbs] >> shiftlow | (shift < 352 && shiftlow ? (l[5 + shiftlimbs] << shifthigh) : 0)) : 0; - r->d[5] = shift < 352 ? (l[5 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[6 + shiftlimbs] << shifthigh) : 0)) : 0; - r->d[6] = shift < 320 ? (l[6 + shiftlimbs] >> shiftlow | (shift < 288 && shiftlow ? (l[7 + shiftlimbs] << shifthigh) : 0)) : 0; - r->d[7] = shift < 288 ? (l[7 + shiftlimbs] >> shiftlow) : 0; - secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); -} - -static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { - uint32_t mask0, mask1; - VG_CHECK_VERIFY(r->d, sizeof(r->d)); - mask0 = flag + ~((uint32_t)0); - mask1 = ~mask0; - r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1); - r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1); - r->d[2] = (r->d[2] & mask0) | (a->d[2] & mask1); - r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1); - r->d[4] = (r->d[4] & mask0) | (a->d[4] & mask1); - r->d[5] = (r->d[5] & mask0) | (a->d[5] & mask1); - r->d[6] = (r->d[6] & mask0) | (a->d[6] & mask1); - r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1); -} - -#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) -#define QUARTERROUND(a,b,c,d) \ - a += b; d = ROTL32(d ^ a, 16); \ - c += d; b = ROTL32(b ^ c, 12); \ - a += b; d = ROTL32(d ^ a, 8); \ - c += d; b = ROTL32(b ^ c, 7); - -#if defined(SECP256K1_BIG_ENDIAN) -#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#elif defined(SECP256K1_LITTLE_ENDIAN) -#define LE32(p) (p) -#endif - -static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { - size_t n; - size_t over_count = 0; - uint32_t seed32[8]; - uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - int over1, over2; - - memcpy((void *) seed32, (const void *) seed, 32); - do { - x0 = 0x61707865; - x1 = 0x3320646e; - x2 = 0x79622d32; - x3 = 0x6b206574; - x4 = LE32(seed32[0]); - x5 = LE32(seed32[1]); - x6 = LE32(seed32[2]); - x7 = LE32(seed32[3]); - x8 = LE32(seed32[4]); - x9 = LE32(seed32[5]); - x10 = LE32(seed32[6]); - x11 = LE32(seed32[7]); - x12 = idx; - x13 = idx >> 32; - x14 = 0; - x15 = over_count; - - n = 10; - while (n--) { - QUARTERROUND(x0, x4, x8,x12) - QUARTERROUND(x1, x5, x9,x13) - QUARTERROUND(x2, x6,x10,x14) - QUARTERROUND(x3, x7,x11,x15) - QUARTERROUND(x0, x5,x10,x15) - QUARTERROUND(x1, x6,x11,x12) - QUARTERROUND(x2, x7, x8,x13) - QUARTERROUND(x3, x4, x9,x14) - } - - x0 += 0x61707865; - x1 += 0x3320646e; - x2 += 0x79622d32; - x3 += 0x6b206574; - x4 += LE32(seed32[0]); - x5 += LE32(seed32[1]); - x6 += LE32(seed32[2]); - x7 += LE32(seed32[3]); - x8 += LE32(seed32[4]); - x9 += LE32(seed32[5]); - x10 += LE32(seed32[6]); - x11 += LE32(seed32[7]); - x12 += idx; - x13 += idx >> 32; - x14 += 0; - x15 += over_count; - - r1->d[7] = x0; - r1->d[6] = x1; - r1->d[5] = x2; - r1->d[4] = x3; - r1->d[3] = x4; - r1->d[2] = x5; - r1->d[1] = x6; - r1->d[0] = x7; - r2->d[7] = x8; - r2->d[6] = x9; - r2->d[5] = x10; - r2->d[4] = x11; - r2->d[3] = x12; - r2->d[2] = x13; - r2->d[1] = x14; - r2->d[0] = x15; - - over1 = secp256k1_scalar_check_overflow(r1); - over2 = secp256k1_scalar_check_overflow(r2); - over_count++; - } while (over1 | over2); -} - -#undef ROTL32 -#undef QUARTERROUND -#undef LE32 - -static void secp256k1_scalar_from_signed30(secp256k1_scalar *r, const secp256k1_modinv32_signed30 *a) { - const uint32_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4], - a5 = a->v[5], a6 = a->v[6], a7 = a->v[7], a8 = a->v[8]; - - /* The output from secp256k1_modinv32{_var} should be normalized to range [0,modulus), and - * have limbs in [0,2^30). The modulus is < 2^256, so the top limb must be below 2^(256-30*8). - */ - VERIFY_CHECK(a0 >> 30 == 0); - VERIFY_CHECK(a1 >> 30 == 0); - VERIFY_CHECK(a2 >> 30 == 0); - VERIFY_CHECK(a3 >> 30 == 0); - VERIFY_CHECK(a4 >> 30 == 0); - VERIFY_CHECK(a5 >> 30 == 0); - VERIFY_CHECK(a6 >> 30 == 0); - VERIFY_CHECK(a7 >> 30 == 0); - VERIFY_CHECK(a8 >> 16 == 0); - - r->d[0] = a0 | a1 << 30; - r->d[1] = a1 >> 2 | a2 << 28; - r->d[2] = a2 >> 4 | a3 << 26; - r->d[3] = a3 >> 6 | a4 << 24; - r->d[4] = a4 >> 8 | a5 << 22; - r->d[5] = a5 >> 10 | a6 << 20; - r->d[6] = a6 >> 12 | a7 << 18; - r->d[7] = a7 >> 14 | a8 << 16; - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); -#endif -} - -static void secp256k1_scalar_to_signed30(secp256k1_modinv32_signed30 *r, const secp256k1_scalar *a) { - const uint32_t M30 = UINT32_MAX >> 2; - const uint32_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3], - a4 = a->d[4], a5 = a->d[5], a6 = a->d[6], a7 = a->d[7]; - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_scalar_check_overflow(a) == 0); -#endif - - r->v[0] = a0 & M30; - r->v[1] = (a0 >> 30 | a1 << 2) & M30; - r->v[2] = (a1 >> 28 | a2 << 4) & M30; - r->v[3] = (a2 >> 26 | a3 << 6) & M30; - r->v[4] = (a3 >> 24 | a4 << 8) & M30; - r->v[5] = (a4 >> 22 | a5 << 10) & M30; - r->v[6] = (a5 >> 20 | a6 << 12) & M30; - r->v[7] = (a6 >> 18 | a7 << 14) & M30; - r->v[8] = a7 >> 16; -} - -static const secp256k1_modinv32_modinfo secp256k1_const_modinfo_scalar = { - {{0x10364141L, 0x3F497A33L, 0x348A03BBL, 0x2BB739ABL, -0x146L, 0, 0, 0, 65536}}, - 0x2A774EC1L -}; - -static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { - secp256k1_modinv32_signed30 s; -#ifdef VERIFY - int zero_in = secp256k1_scalar_is_zero(x); -#endif - secp256k1_scalar_to_signed30(&s, x); - secp256k1_modinv32(&s, &secp256k1_const_modinfo_scalar); - secp256k1_scalar_from_signed30(r, &s); - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in); -#endif -} - -static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { - secp256k1_modinv32_signed30 s; -#ifdef VERIFY - int zero_in = secp256k1_scalar_is_zero(x); -#endif - secp256k1_scalar_to_signed30(&s, x); - secp256k1_modinv32_var(&s, &secp256k1_const_modinfo_scalar); - secp256k1_scalar_from_signed30(r, &s); - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in); -#endif -} - -SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { - return !(a->d[0] & 1); -} - -#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/libwallet/musig/scalar_impl.h b/libwallet/musig/scalar_impl.h deleted file mode 100644 index e1244747..00000000 --- a/libwallet/musig/scalar_impl.h +++ /dev/null @@ -1,297 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_SCALAR_IMPL_H -#define SECP256K1_SCALAR_IMPL_H - -#ifdef VERIFY -#include -#endif - -#include "scalar.h" -#include "util.h" - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - -#if defined(EXHAUSTIVE_TEST_ORDER) -#include "scalar_low_impl.h" -#elif defined(SECP256K1_WIDEMUL_INT128) -#include "scalar_4x64_impl.h" -#elif defined(SECP256K1_WIDEMUL_INT64) -#include "scalar_8x32_impl.h" -#else -#error "Please select wide multiplication implementation" -#endif - -static const secp256k1_scalar secp256k1_scalar_one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); -static const secp256k1_scalar secp256k1_scalar_zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); - -static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin) { - int overflow; - secp256k1_scalar_set_b32(r, bin, &overflow); - return (!overflow) & (!secp256k1_scalar_is_zero(r)); -} - -/* These parameters are generated using sage/gen_exhaustive_groups.sage. */ -#if defined(EXHAUSTIVE_TEST_ORDER) -# if EXHAUSTIVE_TEST_ORDER == 13 -# define EXHAUSTIVE_TEST_LAMBDA 9 -# elif EXHAUSTIVE_TEST_ORDER == 199 -# define EXHAUSTIVE_TEST_LAMBDA 92 -# else -# error No known lambda for the specified exhaustive test group order. -# endif - -/** - * Find r1 and r2 given k, such that r1 + r2 * lambda == k mod n; unlike in the - * full case we don't bother making r1 and r2 be small, we just want them to be - * nontrivial to get full test coverage for the exhaustive tests. We therefore - * (arbitrarily) set r2 = k + 5 (mod n) and r1 = k - r2 * lambda (mod n). - */ -static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { - *r2 = (*k + 5) % EXHAUSTIVE_TEST_ORDER; - *r1 = (*k + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER; -} -#else -/** - * The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where - * lambda is: */ -static const secp256k1_scalar secp256k1_const_lambda = SECP256K1_SCALAR_CONST( - 0x5363AD4CUL, 0xC05C30E0UL, 0xA5261C02UL, 0x8812645AUL, - 0x122E22EAUL, 0x20816678UL, 0xDF02967CUL, 0x1B23BD72UL -); - -#ifdef VERIFY -static void secp256k1_scalar_split_lambda_verify(const secp256k1_scalar *r1, const secp256k1_scalar *r2, const secp256k1_scalar *k); -#endif - -/* - * Both lambda and beta are primitive cube roots of unity. That is lamba^3 == 1 mod n and - * beta^3 == 1 mod p, where n is the curve order and p is the field order. - * - * Futhermore, because (X^3 - 1) = (X - 1)(X^2 + X + 1), the primitive cube roots of unity are - * roots of X^2 + X + 1. Therefore lambda^2 + lamba == -1 mod n and beta^2 + beta == -1 mod p. - * (The other primitive cube roots of unity are lambda^2 and beta^2 respectively.) - * - * Let l = -1/2 + i*sqrt(3)/2, the complex root of X^2 + X + 1. We can define a ring - * homomorphism phi : Z[l] -> Z_n where phi(a + b*l) == a + b*lambda mod n. The kernel of phi - * is a lattice over Z[l] (considering Z[l] as a Z-module). This lattice is generated by a - * reduced basis {a1 + b1*l, a2 + b2*l} where - * - * - a1 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} - * - b1 = -{0xe4,0x43,0x7e,0xd6,0x01,0x0e,0x88,0x28,0x6f,0x54,0x7f,0xa9,0x0a,0xbf,0xe4,0xc3} - * - a2 = {0x01,0x14,0xca,0x50,0xf7,0xa8,0xe2,0xf3,0xf6,0x57,0xc1,0x10,0x8d,0x9d,0x44,0xcf,0xd8} - * - b2 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} - * - * "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm - * (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1 - * and k2 are small in absolute value. - * - * The algorithm computes c1 = round(b2 * k / n) and c2 = round((-b1) * k / n), and gives - * k1 = k - (c1*a1 + c2*a2) and k2 = -(c1*b1 + c2*b2). Instead, we use modular arithmetic, and - * compute r2 = k2 mod n, and r1 = k1 mod n = (k - r2 * lambda) mod n, avoiding the need for - * the constants a1 and a2. - * - * g1, g2 are precomputed constants used to replace division with a rounded multiplication - * when decomposing the scalar for an endomorphism-based point multiplication. - * - * The possibility of using precomputed estimates is mentioned in "Guide to Elliptic Curve - * Cryptography" (Hankerson, Menezes, Vanstone) in section 3.5. - * - * The derivation is described in the paper "Efficient Software Implementation of Public-Key - * Cryptography on Sensor Networks Using the MSP430X Microcontroller" (Gouvea, Oliveira, Lopez), - * Section 4.3 (here we use a somewhat higher-precision estimate): - * d = a1*b2 - b1*a2 - * g1 = round(2^384 * b2/d) - * g2 = round(2^384 * (-b1)/d) - * - * (Note that d is also equal to the curve order, n, here because [a1,b1] and [a2,b2] - * can be found as outputs of the Extended Euclidean Algorithm on inputs n and lambda). - * - * The function below splits k into r1 and r2, such that - * - r1 + lambda * r2 == k (mod n) - * - either r1 < 2^128 or -r1 mod n < 2^128 - * - either r2 < 2^128 or -r2 mod n < 2^128 - * - * See proof below. - */ -static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { - secp256k1_scalar c1, c2; - static const secp256k1_scalar minus_b1 = SECP256K1_SCALAR_CONST( - 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, - 0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C3UL - ); - static const secp256k1_scalar minus_b2 = SECP256K1_SCALAR_CONST( - 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, - 0x8A280AC5UL, 0x0774346DUL, 0xD765CDA8UL, 0x3DB1562CUL - ); - static const secp256k1_scalar g1 = SECP256K1_SCALAR_CONST( - 0x3086D221UL, 0xA7D46BCDUL, 0xE86C90E4UL, 0x9284EB15UL, - 0x3DAA8A14UL, 0x71E8CA7FUL, 0xE893209AUL, 0x45DBB031UL - ); - static const secp256k1_scalar g2 = SECP256K1_SCALAR_CONST( - 0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C4UL, - 0x221208ACUL, 0x9DF506C6UL, 0x1571B4AEUL, 0x8AC47F71UL - ); - VERIFY_CHECK(r1 != k); - VERIFY_CHECK(r2 != k); - /* these _var calls are constant time since the shift amount is constant */ - secp256k1_scalar_mul_shift_var(&c1, k, &g1, 384); - secp256k1_scalar_mul_shift_var(&c2, k, &g2, 384); - secp256k1_scalar_mul(&c1, &c1, &minus_b1); - secp256k1_scalar_mul(&c2, &c2, &minus_b2); - secp256k1_scalar_add(r2, &c1, &c2); - secp256k1_scalar_mul(r1, r2, &secp256k1_const_lambda); - secp256k1_scalar_negate(r1, r1); - secp256k1_scalar_add(r1, r1, k); - -#ifdef VERIFY - secp256k1_scalar_split_lambda_verify(r1, r2, k); -#endif -} - -#ifdef VERIFY -/* - * Proof for secp256k1_scalar_split_lambda's bounds. - * - * Let - * - epsilon1 = 2^256 * |g1/2^384 - b2/d| - * - epsilon2 = 2^256 * |g2/2^384 - (-b1)/d| - * - c1 = round(k*g1/2^384) - * - c2 = round(k*g2/2^384) - * - * Lemma 1: |c1 - k*b2/d| < 2^-1 + epsilon1 - * - * |c1 - k*b2/d| - * = - * |c1 - k*g1/2^384 + k*g1/2^384 - k*b2/d| - * <= {triangle inequality} - * |c1 - k*g1/2^384| + |k*g1/2^384 - k*b2/d| - * = - * |c1 - k*g1/2^384| + k*|g1/2^384 - b2/d| - * < {rounding in c1 and 0 <= k < 2^256} - * 2^-1 + 2^256 * |g1/2^384 - b2/d| - * = {definition of epsilon1} - * 2^-1 + epsilon1 - * - * Lemma 2: |c2 - k*(-b1)/d| < 2^-1 + epsilon2 - * - * |c2 - k*(-b1)/d| - * = - * |c2 - k*g2/2^384 + k*g2/2^384 - k*(-b1)/d| - * <= {triangle inequality} - * |c2 - k*g2/2^384| + |k*g2/2^384 - k*(-b1)/d| - * = - * |c2 - k*g2/2^384| + k*|g2/2^384 - (-b1)/d| - * < {rounding in c2 and 0 <= k < 2^256} - * 2^-1 + 2^256 * |g2/2^384 - (-b1)/d| - * = {definition of epsilon2} - * 2^-1 + epsilon2 - * - * Let - * - k1 = k - c1*a1 - c2*a2 - * - k2 = - c1*b1 - c2*b2 - * - * Lemma 3: |k1| < (a1 + a2 + 1)/2 < 2^128 - * - * |k1| - * = {definition of k1} - * |k - c1*a1 - c2*a2| - * = {(a1*b2 - b1*a2)/n = 1} - * |k*(a1*b2 - b1*a2)/n - c1*a1 - c2*a2| - * = - * |a1*(k*b2/n - c1) + a2*(k*(-b1)/n - c2)| - * <= {triangle inequality} - * a1*|k*b2/n - c1| + a2*|k*(-b1)/n - c2| - * < {Lemma 1 and Lemma 2} - * a1*(2^-1 + epslion1) + a2*(2^-1 + epsilon2) - * < {rounding up to an integer} - * (a1 + a2 + 1)/2 - * < {rounding up to a power of 2} - * 2^128 - * - * Lemma 4: |k2| < (-b1 + b2)/2 + 1 < 2^128 - * - * |k2| - * = {definition of k2} - * |- c1*a1 - c2*a2| - * = {(b1*b2 - b1*b2)/n = 0} - * |k*(b1*b2 - b1*b2)/n - c1*b1 - c2*b2| - * = - * |b1*(k*b2/n - c1) + b2*(k*(-b1)/n - c2)| - * <= {triangle inequality} - * (-b1)*|k*b2/n - c1| + b2*|k*(-b1)/n - c2| - * < {Lemma 1 and Lemma 2} - * (-b1)*(2^-1 + epslion1) + b2*(2^-1 + epsilon2) - * < {rounding up to an integer} - * (-b1 + b2)/2 + 1 - * < {rounding up to a power of 2} - * 2^128 - * - * Let - * - r2 = k2 mod n - * - r1 = k - r2*lambda mod n. - * - * Notice that r1 is defined such that r1 + r2 * lambda == k (mod n). - * - * Lemma 5: r1 == k1 mod n. - * - * r1 - * == {definition of r1 and r2} - * k - k2*lambda - * == {definition of k2} - * k - (- c1*b1 - c2*b2)*lambda - * == - * k + c1*b1*lambda + c2*b2*lambda - * == {a1 + b1*lambda == 0 mod n and a2 + b2*lambda == 0 mod n} - * k - c1*a1 - c2*a2 - * == {definition of k1} - * k1 - * - * From Lemma 3, Lemma 4, Lemma 5 and the definition of r2, we can conclude that - * - * - either r1 < 2^128 or -r1 mod n < 2^128 - * - either r2 < 2^128 or -r2 mod n < 2^128. - * - * Q.E.D. - */ -static void secp256k1_scalar_split_lambda_verify(const secp256k1_scalar *r1, const secp256k1_scalar *r2, const secp256k1_scalar *k) { - secp256k1_scalar s; - unsigned char buf1[32]; - unsigned char buf2[32]; - - /* (a1 + a2 + 1)/2 is 0xa2a8918ca85bafe22016d0b917e4dd77 */ - static const unsigned char k1_bound[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xa2, 0xa8, 0x91, 0x8c, 0xa8, 0x5b, 0xaf, 0xe2, 0x20, 0x16, 0xd0, 0xb9, 0x17, 0xe4, 0xdd, 0x77 - }; - - /* (-b1 + b2)/2 + 1 is 0x8a65287bd47179fb2be08846cea267ed */ - static const unsigned char k2_bound[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x8a, 0x65, 0x28, 0x7b, 0xd4, 0x71, 0x79, 0xfb, 0x2b, 0xe0, 0x88, 0x46, 0xce, 0xa2, 0x67, 0xed - }; - - secp256k1_scalar_mul(&s, &secp256k1_const_lambda, r2); - secp256k1_scalar_add(&s, &s, r1); - VERIFY_CHECK(secp256k1_scalar_eq(&s, k)); - - secp256k1_scalar_negate(&s, r1); - secp256k1_scalar_get_b32(buf1, r1); - secp256k1_scalar_get_b32(buf2, &s); - VERIFY_CHECK(secp256k1_memcmp_var(buf1, k1_bound, 32) < 0 || secp256k1_memcmp_var(buf2, k1_bound, 32) < 0); - - secp256k1_scalar_negate(&s, r2); - secp256k1_scalar_get_b32(buf1, r2); - secp256k1_scalar_get_b32(buf2, &s); - VERIFY_CHECK(secp256k1_memcmp_var(buf1, k2_bound, 32) < 0 || secp256k1_memcmp_var(buf2, k2_bound, 32) < 0); -} -#endif /* VERIFY */ -#endif /* !defined(EXHAUSTIVE_TEST_ORDER) */ - -#endif /* SECP256K1_SCALAR_IMPL_H */ diff --git a/libwallet/musig/scalar_low.h b/libwallet/musig/scalar_low.h deleted file mode 100644 index 67051bd3..00000000 --- a/libwallet/musig/scalar_low.h +++ /dev/null @@ -1,17 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2015 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_SCALAR_REPR_H -#define SECP256K1_SCALAR_REPR_H - -#include - -/** A scalar modulo the group order of the secp256k1 curve. */ -typedef uint32_t secp256k1_scalar; - -#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) (d0) - -#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/libwallet/musig/scalar_low_impl.h b/libwallet/musig/scalar_low_impl.h deleted file mode 100644 index 47001fcc..00000000 --- a/libwallet/musig/scalar_low_impl.h +++ /dev/null @@ -1,145 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2015 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_SCALAR_REPR_IMPL_H -#define SECP256K1_SCALAR_REPR_IMPL_H - -#include "scalar.h" - -#include - -SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { - return !(*a & 1); -} - -SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { *r = 0; } -SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { *r = v; } -SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { *r = v % EXHAUSTIVE_TEST_ORDER; } - -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { - if (offset < 32) - return ((*a >> offset) & ((((uint32_t)1) << count) - 1)); - else - return 0; -} - -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { - return secp256k1_scalar_get_bits(a, offset, count); -} - -SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { return *a >= EXHAUSTIVE_TEST_ORDER; } - -static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { - *r = (*a + *b) % EXHAUSTIVE_TEST_ORDER; - return *r < *b; -} - -static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { - if (flag && bit < 32) - *r += ((uint32_t)1 << bit); -#ifdef VERIFY - VERIFY_CHECK(bit < 32); - /* Verify that adding (1 << bit) will not overflow any in-range scalar *r by overflowing the underlying uint32_t. */ - VERIFY_CHECK(((uint32_t)1 << bit) - 1 <= UINT32_MAX - EXHAUSTIVE_TEST_ORDER); - VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); -#endif -} - -static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { - int i; - int over = 0; - *r = 0; - for (i = 0; i < 32; i++) { - *r = (*r * 0x100) + b32[i]; - if (*r >= EXHAUSTIVE_TEST_ORDER) { - over = 1; - *r %= EXHAUSTIVE_TEST_ORDER; - } - } - if (overflow) *overflow = over; -} - -static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { - memset(bin, 0, 32); - bin[28] = *a >> 24; bin[29] = *a >> 16; bin[30] = *a >> 8; bin[31] = *a; -} - -SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { - return *a == 0; -} - -static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { - if (*a == 0) { - *r = 0; - } else { - *r = EXHAUSTIVE_TEST_ORDER - *a; - } -} - -SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { - return *a == 1; -} - -static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { - return *a > EXHAUSTIVE_TEST_ORDER / 2; -} - -static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { - if (flag) secp256k1_scalar_negate(r, r); - return flag ? -1 : 1; -} - -static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { - *r = (*a * *b) % EXHAUSTIVE_TEST_ORDER; -} - -static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { - int ret; - VERIFY_CHECK(n > 0); - VERIFY_CHECK(n < 16); - ret = *r & ((1 << n) - 1); - *r >>= n; - return ret; -} - -static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { - *r1 = *a; - *r2 = 0; -} - -SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { - return *a == *b; -} - -static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { - uint32_t mask0, mask1; - VG_CHECK_VERIFY(r, sizeof(*r)); - mask0 = flag + ~((uint32_t)0); - mask1 = ~mask0; - *r = (*r & mask0) | (*a & mask1); -} - -SECP256K1_INLINE static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t n) { - *r1 = (seed[0] + n) % EXHAUSTIVE_TEST_ORDER; - *r2 = (seed[1] + n) % EXHAUSTIVE_TEST_ORDER; -} - -static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { - int i; - *r = 0; - for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) - if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1) - *r = i; - /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus - * have a composite group order; fix it in exhaustive_tests.c). */ - VERIFY_CHECK(*r != 0); -} - -static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { - secp256k1_scalar_inverse(r, x); -} - -#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/libwallet/musig/schnorrsig_main_impl.h b/libwallet/musig/schnorrsig_main_impl.h deleted file mode 100644 index 5be5ae9c..00000000 --- a/libwallet/musig/schnorrsig_main_impl.h +++ /dev/null @@ -1,256 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_MODULE_SCHNORRSIG_MAIN_H -#define SECP256K1_MODULE_SCHNORRSIG_MAIN_H - -#include "secp256k1.h" -#include "secp256k1_schnorrsig.h" -#include "hash.h" - -/* Initializes SHA256 with fixed midstate. This midstate was computed by applying - * SHA256 to SHA256("BIP0340/nonce")||SHA256("BIP0340/nonce"). */ -static void secp256k1_nonce_function_bip340_sha256_tagged(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - sha->s[0] = 0x46615b35ul; - sha->s[1] = 0xf4bfbff7ul; - sha->s[2] = 0x9f8dc671ul; - sha->s[3] = 0x83627ab3ul; - sha->s[4] = 0x60217180ul; - sha->s[5] = 0x57358661ul; - sha->s[6] = 0x21a29e54ul; - sha->s[7] = 0x68b07b4cul; - - sha->bytes = 64; -} - -/* Initializes SHA256 with fixed midstate. This midstate was computed by applying - * SHA256 to SHA256("BIP0340/aux")||SHA256("BIP0340/aux"). */ -static void secp256k1_nonce_function_bip340_sha256_tagged_aux(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - sha->s[0] = 0x24dd3219ul; - sha->s[1] = 0x4eba7e70ul; - sha->s[2] = 0xca0fabb9ul; - sha->s[3] = 0x0fa3166dul; - sha->s[4] = 0x3afbe4b1ul; - sha->s[5] = 0x4c44df97ul; - sha->s[6] = 0x4aac2739ul; - sha->s[7] = 0x249e850aul; - - sha->bytes = 64; -} - -/* algo argument for nonce_function_bip340 to derive the nonce exactly as stated in BIP-340 - * by using the correct tagged hash function. */ -static const unsigned char bip340_algo[13] = "BIP0340/nonce"; - -static const unsigned char schnorrsig_extraparams_magic[4] = SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC; - -static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { - secp256k1_sha256 sha; - unsigned char masked_key[32]; - int i; - - if (algo == NULL) { - return 0; - } - - if (data != NULL) { - secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha); - secp256k1_sha256_write(&sha, data, 32); - secp256k1_sha256_finalize(&sha, masked_key); - for (i = 0; i < 32; i++) { - masked_key[i] ^= key32[i]; - } - } - - /* Tag the hash with algo which is important to avoid nonce reuse across - * algorithms. If this nonce function is used in BIP-340 signing as defined - * in the spec, an optimized tagging implementation is used. */ - if (algolen == sizeof(bip340_algo) - && secp256k1_memcmp_var(algo, bip340_algo, algolen) == 0) { - secp256k1_nonce_function_bip340_sha256_tagged(&sha); - } else { - secp256k1_sha256_initialize_tagged(&sha, algo, algolen); - } - - /* Hash (masked-)key||pk||msg using the tagged hash as per the spec */ - if (data != NULL) { - secp256k1_sha256_write(&sha, masked_key, 32); - } else { - secp256k1_sha256_write(&sha, key32, 32); - } - secp256k1_sha256_write(&sha, xonly_pk32, 32); - secp256k1_sha256_write(&sha, msg, msglen); - secp256k1_sha256_finalize(&sha, nonce32); - return 1; -} - -const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340 = nonce_function_bip340; - -/* Initializes SHA256 with fixed midstate. This midstate was computed by applying - * SHA256 to SHA256("BIP0340/challenge")||SHA256("BIP0340/challenge"). */ -static void secp256k1_schnorrsig_sha256_tagged(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - sha->s[0] = 0x9cecba11ul; - sha->s[1] = 0x23925381ul; - sha->s[2] = 0x11679112ul; - sha->s[3] = 0xd1627e0ful; - sha->s[4] = 0x97c87550ul; - sha->s[5] = 0x003cc765ul; - sha->s[6] = 0x90f61164ul; - sha->s[7] = 0x33e9b66aul; - sha->bytes = 64; -} - -static void secp256k1_schnorrsig_challenge(secp256k1_scalar* e, const unsigned char *r32, const unsigned char *msg, size_t msglen, const unsigned char *pubkey32) -{ - unsigned char buf[32]; - secp256k1_sha256 sha; - - /* tagged hash(r.x, pk.x, msg) */ - secp256k1_schnorrsig_sha256_tagged(&sha); - secp256k1_sha256_write(&sha, r32, 32); - secp256k1_sha256_write(&sha, pubkey32, 32); - secp256k1_sha256_write(&sha, msg, msglen); - secp256k1_sha256_finalize(&sha, buf); - /* Set scalar e to the challenge hash modulo the curve order as per - * BIP340. */ - secp256k1_scalar_set_b32(e, buf, NULL); -} - -int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) { - secp256k1_scalar sk; - secp256k1_scalar e; - secp256k1_scalar k; - secp256k1_gej rj; - secp256k1_ge pk; - secp256k1_ge r; - unsigned char buf[32] = { 0 }; - unsigned char pk_buf[32]; - unsigned char seckey[32]; - int ret = 1; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(sig64 != NULL); - ARG_CHECK(msg != NULL || msglen == 0); - ARG_CHECK(keypair != NULL); - - if (noncefp == NULL) { - noncefp = secp256k1_nonce_function_bip340; - } - - ret &= secp256k1_keypair_load(ctx, &sk, &pk, keypair); - /* Because we are signing for a x-only pubkey, the secret key is negated - * before signing if the point corresponding to the secret key does not - * have an even Y. */ - if (secp256k1_fe_is_odd(&pk.y)) { - secp256k1_scalar_negate(&sk, &sk); - } - - secp256k1_scalar_get_b32(seckey, &sk); - secp256k1_fe_get_b32(pk_buf, &pk.x); - ret &= !!noncefp(buf, msg, msglen, seckey, pk_buf, bip340_algo, sizeof(bip340_algo), ndata); - secp256k1_scalar_set_b32(&k, buf, NULL); - ret &= !secp256k1_scalar_is_zero(&k); - secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret); - - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); - secp256k1_ge_set_gej(&r, &rj); - - /* We declassify r to allow using it as a branch point. This is fine - * because r is not a secret. */ - secp256k1_declassify(ctx, &r, sizeof(r)); - secp256k1_fe_normalize_var(&r.y); - if (secp256k1_fe_is_odd(&r.y)) { - secp256k1_scalar_negate(&k, &k); - } - secp256k1_fe_normalize_var(&r.x); - secp256k1_fe_get_b32(&sig64[0], &r.x); - - secp256k1_schnorrsig_challenge(&e, &sig64[0], msg, msglen, pk_buf); - secp256k1_scalar_mul(&e, &e, &sk); - secp256k1_scalar_add(&e, &e, &k); - secp256k1_scalar_get_b32(&sig64[32], &e); - - secp256k1_memczero(sig64, 64, !ret); - secp256k1_scalar_clear(&k); - secp256k1_scalar_clear(&sk); - memset(seckey, 0, sizeof(seckey)); - - return ret; -} - -int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, unsigned char *aux_rand32) { - return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg32, 32, keypair, secp256k1_nonce_function_bip340, aux_rand32); -} - -int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_schnorrsig_extraparams *extraparams) { - secp256k1_nonce_function_hardened noncefp = NULL; - void *ndata = NULL; - VERIFY_CHECK(ctx != NULL); - - if (extraparams != NULL) { - ARG_CHECK(secp256k1_memcmp_var(extraparams->magic, - schnorrsig_extraparams_magic, - sizeof(extraparams->magic)) == 0); - noncefp = extraparams->noncefp; - ndata = extraparams->ndata; - } - return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msglen, keypair, noncefp, ndata); -} - -int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey) { - secp256k1_scalar s; - secp256k1_scalar e; - secp256k1_gej rj; - secp256k1_ge pk; - secp256k1_gej pkj; - secp256k1_fe rx; - secp256k1_ge r; - unsigned char buf[32]; - int overflow; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(sig64 != NULL); - ARG_CHECK(msg != NULL || msglen == 0); - ARG_CHECK(pubkey != NULL); - - if (!secp256k1_fe_set_b32(&rx, &sig64[0])) { - return 0; - } - - secp256k1_scalar_set_b32(&s, &sig64[32], &overflow); - if (overflow) { - return 0; - } - - if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) { - return 0; - } - - /* Compute e. */ - secp256k1_fe_get_b32(buf, &pk.x); - secp256k1_schnorrsig_challenge(&e, &sig64[0], msg, msglen, buf); - - /* Compute rj = s*G + (-e)*pkj */ - secp256k1_scalar_negate(&e, &e); - secp256k1_gej_set_ge(&pkj, &pk); - secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s); - - secp256k1_ge_set_gej_var(&r, &rj); - if (secp256k1_ge_is_infinity(&r)) { - return 0; - } - - secp256k1_fe_normalize_var(&r.y); - return !secp256k1_fe_is_odd(&r.y) && - secp256k1_fe_equal_var(&rx, &r.x); -} - -#endif diff --git a/libwallet/musig/scratch.h b/libwallet/musig/scratch.h deleted file mode 100644 index 9dcb7581..00000000 --- a/libwallet/musig/scratch.h +++ /dev/null @@ -1,42 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2017 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_SCRATCH_H -#define SECP256K1_SCRATCH_H - -/* The typedef is used internally; the struct name is used in the public API - * (where it is exposed as a different typedef) */ -typedef struct secp256k1_scratch_space_struct { - /** guard against interpreting this object as other types */ - unsigned char magic[8]; - /** actual allocated data */ - void *data; - /** amount that has been allocated (i.e. `data + offset` is the next - * available pointer) */ - size_t alloc_size; - /** maximum size available to allocate */ - size_t max_size; -} secp256k1_scratch; - -static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size); - -static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch); - -/** Returns an opaque object used to "checkpoint" a scratch space. Used - * with `secp256k1_scratch_apply_checkpoint` to undo allocations. */ -static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch); - -/** Applies a check point received from `secp256k1_scratch_checkpoint`, - * undoing all allocations since that point. */ -static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint); - -/** Returns the maximum allocation the scratch space will allow */ -static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t n_objects); - -/** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */ -static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t n); - -#endif diff --git a/libwallet/musig/scratch_impl.h b/libwallet/musig/scratch_impl.h deleted file mode 100644 index 688e18eb..00000000 --- a/libwallet/musig/scratch_impl.h +++ /dev/null @@ -1,99 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2017 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_SCRATCH_IMPL_H -#define SECP256K1_SCRATCH_IMPL_H - -#include "util.h" -#include "scratch.h" - -static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t size) { - const size_t base_alloc = ROUND_TO_ALIGN(sizeof(secp256k1_scratch)); - void *alloc = checked_malloc(error_callback, base_alloc + size); - secp256k1_scratch* ret = (secp256k1_scratch *)alloc; - if (ret != NULL) { - memset(ret, 0, sizeof(*ret)); - memcpy(ret->magic, "scratch", 8); - ret->data = (void *) ((char *) alloc + base_alloc); - ret->max_size = size; - } - return ret; -} - -static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) { - if (scratch != NULL) { - VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */ - if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { - secp256k1_callback_call(error_callback, "invalid scratch space"); - return; - } - memset(scratch->magic, 0, sizeof(scratch->magic)); - free(scratch); - } -} - -static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch) { - if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { - secp256k1_callback_call(error_callback, "invalid scratch space"); - return 0; - } - return scratch->alloc_size; -} - -static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint) { - if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { - secp256k1_callback_call(error_callback, "invalid scratch space"); - return; - } - if (checkpoint > scratch->alloc_size) { - secp256k1_callback_call(error_callback, "invalid checkpoint"); - return; - } - scratch->alloc_size = checkpoint; -} - -static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t objects) { - if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { - secp256k1_callback_call(error_callback, "invalid scratch space"); - return 0; - } - /* Ensure that multiplication will not wrap around */ - if (ALIGNMENT > 1 && objects > SIZE_MAX/(ALIGNMENT - 1)) { - return 0; - } - if (scratch->max_size - scratch->alloc_size <= objects * (ALIGNMENT - 1)) { - return 0; - } - return scratch->max_size - scratch->alloc_size - objects * (ALIGNMENT - 1); -} - -static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t size) { - void *ret; - size_t rounded_size; - - rounded_size = ROUND_TO_ALIGN(size); - /* Check that rounding did not wrap around */ - if (rounded_size < size) { - return NULL; - } - size = rounded_size; - - if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { - secp256k1_callback_call(error_callback, "invalid scratch space"); - return NULL; - } - - if (size > scratch->max_size - scratch->alloc_size) { - return NULL; - } - ret = (void *) ((char *) scratch->data + scratch->alloc_size); - memset(ret, 0, size); - scratch->alloc_size += size; - - return ret; -} - -#endif diff --git a/libwallet/musig/secp256k1.h b/libwallet/musig/secp256k1.h deleted file mode 100644 index 7be7fd57..00000000 --- a/libwallet/musig/secp256k1.h +++ /dev/null @@ -1,825 +0,0 @@ -#ifndef SECP256K1_H -#define SECP256K1_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* Unless explicitly stated all pointer arguments must not be NULL. - * - * The following rules specify the order of arguments in API calls: - * - * 1. Context pointers go first, followed by output arguments, combined - * output/input arguments, and finally input-only arguments. - * 2. Array lengths always immediately follow the argument whose length - * they describe, even if this violates rule 1. - * 3. Within the OUT/OUTIN/IN groups, pointers to data that is typically generated - * later go first. This means: signatures, public nonces, secret nonces, - * messages, public keys, secret keys, tweaks. - * 4. Arguments that are not data pointers go last, from more complex to less - * complex: function pointers, algorithm names, messages, void pointers, - * counts, flags, booleans. - * 5. Opaque data pointers follow the function pointer they are to be passed to. - */ - -/** Opaque data structure that holds context information (precomputed tables etc.). - * - * The purpose of context structures is to cache large precomputed data tables - * that are expensive to construct, and also to maintain the randomization data - * for blinding. - * - * Do not create a new context object for each operation, as construction is - * far slower than all other API calls (~100 times slower than an ECDSA - * verification). - * - * A constructed context can safely be used from multiple threads - * simultaneously, but API calls that take a non-const pointer to a context - * need exclusive access to it. In particular this is the case for - * secp256k1_context_destroy, secp256k1_context_preallocated_destroy, - * and secp256k1_context_randomize. - * - * Regarding randomization, either do it once at creation time (in which case - * you do not need any locking for the other calls), or use a read-write lock. - */ -typedef struct secp256k1_context_struct secp256k1_context; - -/** Opaque data structure that holds rewriteable "scratch space" - * - * The purpose of this structure is to replace dynamic memory allocations, - * because we target architectures where this may not be available. It is - * essentially a resizable (within specified parameters) block of bytes, - * which is initially created either by memory allocation or TODO as a pointer - * into some fixed rewritable space. - * - * Unlike the context object, this cannot safely be shared between threads - * without additional synchronization logic. - */ -typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space; - -/** Opaque data structure that holds a parsed and valid public key. - * - * The exact representation of data inside is implementation defined and not - * guaranteed to be portable between different platforms or versions. It is - * however guaranteed to be 64 bytes in size, and can be safely copied/moved. - * If you need to convert to a format suitable for storage or transmission, - * use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse. To - * compare keys, use secp256k1_ec_pubkey_cmp. - */ -typedef struct { - unsigned char data[64]; -} secp256k1_pubkey; - -/** Opaque data structured that holds a parsed ECDSA signature. - * - * The exact representation of data inside is implementation defined and not - * guaranteed to be portable between different platforms or versions. It is - * however guaranteed to be 64 bytes in size, and can be safely copied/moved. - * If you need to convert to a format suitable for storage, transmission, or - * comparison, use the secp256k1_ecdsa_signature_serialize_* and - * secp256k1_ecdsa_signature_parse_* functions. - */ -typedef struct { - unsigned char data[64]; -} secp256k1_ecdsa_signature; - -/** A pointer to a function to deterministically generate a nonce. - * - * Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail. - * Out: nonce32: pointer to a 32-byte array to be filled by the function. - * In: msg32: the 32-byte message hash being verified (will not be NULL) - * key32: pointer to a 32-byte secret key (will not be NULL) - * algo16: pointer to a 16-byte array describing the signature - * algorithm (will be NULL for ECDSA for compatibility). - * data: Arbitrary data pointer that is passed through. - * attempt: how many iterations we have tried to find a nonce. - * This will almost always be 0, but different attempt values - * are required to result in a different nonce. - * - * Except for test cases, this function should compute some cryptographic hash of - * the message, the algorithm, the key and the attempt. - */ -typedef int (*secp256k1_nonce_function)( - unsigned char *nonce32, - const unsigned char *msg32, - const unsigned char *key32, - const unsigned char *algo16, - void *data, - unsigned int attempt -); - -# if !defined(SECP256K1_GNUC_PREREQ) -# if defined(__GNUC__)&&defined(__GNUC_MINOR__) -# define SECP256K1_GNUC_PREREQ(_maj,_min) \ - ((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min)) -# else -# define SECP256K1_GNUC_PREREQ(_maj,_min) 0 -# endif -# endif - -# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) -# if SECP256K1_GNUC_PREREQ(2,7) -# define SECP256K1_INLINE __inline__ -# elif (defined(_MSC_VER)) -# define SECP256K1_INLINE __inline -# else -# define SECP256K1_INLINE -# endif -# else -# define SECP256K1_INLINE inline -# endif - -/** When this header is used at build-time the SECP256K1_BUILD define needs to be set - * to correctly setup export attributes and nullness checks. This is normally done - * by secp256k1.c but to guard against this header being included before secp256k1.c - * has had a chance to set the define (e.g. via test harnesses that just includes - * secp256k1.c) we set SECP256K1_NO_BUILD when this header is processed without the - * BUILD define so this condition can be caught. - */ -#ifndef SECP256K1_BUILD -# define SECP256K1_NO_BUILD -#endif - -#ifndef SECP256K1_API -# if defined(_WIN32) -# ifdef SECP256K1_BUILD -# define SECP256K1_API __declspec(dllexport) -# else -# define SECP256K1_API -# endif -# elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD) -# define SECP256K1_API __attribute__ ((visibility ("default"))) -# else -# define SECP256K1_API -# endif -#endif - -/**Warning attributes - * NONNULL is not used if SECP256K1_BUILD is set to avoid the compiler optimizing out - * some paranoid null checks. */ -# if defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) -# define SECP256K1_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__)) -# else -# define SECP256K1_WARN_UNUSED_RESULT -# endif -# if !defined(SECP256K1_BUILD) && defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) -# define SECP256K1_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x))) -# else -# define SECP256K1_ARG_NONNULL(_x) -# endif - -/** All flags' lower 8 bits indicate what they're for. Do not use directly. */ -#define SECP256K1_FLAGS_TYPE_MASK ((1 << 8) - 1) -#define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0) -#define SECP256K1_FLAGS_TYPE_COMPRESSION (1 << 1) -/** The higher bits contain the actual data. Do not use directly. */ -#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8) -#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9) -#define SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY (1 << 10) -#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8) - -/** Flags to pass to secp256k1_context_create, secp256k1_context_preallocated_size, and - * secp256k1_context_preallocated_create. */ -#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) -#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN) -#define SECP256K1_CONTEXT_DECLASSIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY) -#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT) - -/** Flag to pass to secp256k1_ec_pubkey_serialize. */ -#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION) -#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION) - -/** Prefix byte used to tag various encoded curvepoints for specific purposes */ -#define SECP256K1_TAG_PUBKEY_EVEN 0x02 -#define SECP256K1_TAG_PUBKEY_ODD 0x03 -#define SECP256K1_TAG_PUBKEY_UNCOMPRESSED 0x04 -#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06 -#define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07 - -/** A simple secp256k1 context object with no precomputed tables. These are useful for - * type serialization/parsing functions which require a context object to maintain - * API consistency, but currently do not require expensive precomputations or dynamic - * allocations. - */ -SECP256K1_API extern const secp256k1_context *secp256k1_context_no_precomp; - -/** Create a secp256k1 context object (in dynamically allocated memory). - * - * This function uses malloc to allocate memory. It is guaranteed that malloc is - * called at most once for every call of this function. If you need to avoid dynamic - * memory allocation entirely, see the functions in secp256k1_preallocated.h. - * - * Returns: a newly created context object. - * In: flags: which parts of the context to initialize. - * - * See also secp256k1_context_randomize. - */ -SECP256K1_API secp256k1_context* secp256k1_context_create( - unsigned int flags -) SECP256K1_WARN_UNUSED_RESULT; - -/** Copy a secp256k1 context object (into dynamically allocated memory). - * - * This function uses malloc to allocate memory. It is guaranteed that malloc is - * called at most once for every call of this function. If you need to avoid dynamic - * memory allocation entirely, see the functions in secp256k1_preallocated.h. - * - * Returns: a newly created context object. - * Args: ctx: an existing context to copy (cannot be NULL) - */ -SECP256K1_API secp256k1_context* secp256k1_context_clone( - const secp256k1_context* ctx -) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; - -/** Destroy a secp256k1 context object (created in dynamically allocated memory). - * - * The context pointer may not be used afterwards. - * - * The context to destroy must have been created using secp256k1_context_create - * or secp256k1_context_clone. If the context has instead been created using - * secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone, the - * behaviour is undefined. In that case, secp256k1_context_preallocated_destroy must - * be used instead. - * - * Args: ctx: an existing context to destroy, constructed using - * secp256k1_context_create or secp256k1_context_clone - */ -SECP256K1_API void secp256k1_context_destroy( - secp256k1_context* ctx -); - -/** Set a callback function to be called when an illegal argument is passed to - * an API call. It will only trigger for violations that are mentioned - * explicitly in the header. - * - * The philosophy is that these shouldn't be dealt with through a - * specific return value, as calling code should not have branches to deal with - * the case that this code itself is broken. - * - * On the other hand, during debug stage, one would want to be informed about - * such mistakes, and the default (crashing) may be inadvisable. - * When this callback is triggered, the API function called is guaranteed not - * to cause a crash, though its return value and output arguments are - * undefined. - * - * When this function has not been called (or called with fn==NULL), then the - * default handler will be used. The library provides a default handler which - * writes the message to stderr and calls abort. This default handler can be - * replaced at link time if the preprocessor macro - * USE_EXTERNAL_DEFAULT_CALLBACKS is defined, which is the case if the build - * has been configured with --enable-external-default-callbacks. Then the - * following two symbols must be provided to link against: - * - void secp256k1_default_illegal_callback_fn(const char* message, void* data); - * - void secp256k1_default_error_callback_fn(const char* message, void* data); - * The library can call these default handlers even before a proper callback data - * pointer could have been set using secp256k1_context_set_illegal_callback or - * secp256k1_context_set_error_callback, e.g., when the creation of a context - * fails. In this case, the corresponding default handler will be called with - * the data pointer argument set to NULL. - * - * Args: ctx: an existing context object (cannot be NULL) - * In: fun: a pointer to a function to call when an illegal argument is - * passed to the API, taking a message and an opaque pointer. - * (NULL restores the default handler.) - * data: the opaque pointer to pass to fun above. - * - * See also secp256k1_context_set_error_callback. - */ -SECP256K1_API void secp256k1_context_set_illegal_callback( - secp256k1_context* ctx, - void (*fun)(const char* message, void* data), - const void* data -) SECP256K1_ARG_NONNULL(1); - -/** Set a callback function to be called when an internal consistency check - * fails. The default is crashing. - * - * This can only trigger in case of a hardware failure, miscompilation, - * memory corruption, serious bug in the library, or other error would can - * otherwise result in undefined behaviour. It will not trigger due to mere - * incorrect usage of the API (see secp256k1_context_set_illegal_callback - * for that). After this callback returns, anything may happen, including - * crashing. - * - * Args: ctx: an existing context object (cannot be NULL) - * In: fun: a pointer to a function to call when an internal error occurs, - * taking a message and an opaque pointer (NULL restores the - * default handler, see secp256k1_context_set_illegal_callback - * for details). - * data: the opaque pointer to pass to fun above. - * - * See also secp256k1_context_set_illegal_callback. - */ -SECP256K1_API void secp256k1_context_set_error_callback( - secp256k1_context* ctx, - void (*fun)(const char* message, void* data), - const void* data -) SECP256K1_ARG_NONNULL(1); - -/** Create a secp256k1 scratch space object. - * - * Returns: a newly created scratch space. - * Args: ctx: an existing context object (cannot be NULL) - * In: size: amount of memory to be available as scratch space. Some extra - * (<100 bytes) will be allocated for extra accounting. - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create( - const secp256k1_context* ctx, - size_t size -) SECP256K1_ARG_NONNULL(1); - -/** Destroy a secp256k1 scratch space. - * - * The pointer may not be used afterwards. - * Args: ctx: a secp256k1 context object. - * scratch: space to destroy - */ -SECP256K1_API void secp256k1_scratch_space_destroy( - const secp256k1_context* ctx, - secp256k1_scratch_space* scratch -) SECP256K1_ARG_NONNULL(1); - -/** Parse a variable-length public key into the pubkey object. - * - * Returns: 1 if the public key was fully valid. - * 0 if the public key could not be parsed or is invalid. - * Args: ctx: a secp256k1 context object. - * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a - * parsed version of input. If not, its value is undefined. - * In: input: pointer to a serialized public key - * inputlen: length of the array pointed to by input - * - * This function supports parsing compressed (33 bytes, header byte 0x02 or - * 0x03), uncompressed (65 bytes, header byte 0x04), or hybrid (65 bytes, header - * byte 0x06 or 0x07) format public keys. - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse( - const secp256k1_context* ctx, - secp256k1_pubkey* pubkey, - const unsigned char *input, - size_t inputlen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Serialize a pubkey object into a serialized byte sequence. - * - * Returns: 1 always. - * Args: ctx: a secp256k1 context object. - * Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if - * compressed==1) byte array to place the serialized key - * in. - * In/Out: outputlen: a pointer to an integer which is initially set to the - * size of output, and is overwritten with the written - * size. - * In: pubkey: a pointer to a secp256k1_pubkey containing an - * initialized public key. - * flags: SECP256K1_EC_COMPRESSED if serialization should be in - * compressed format, otherwise SECP256K1_EC_UNCOMPRESSED. - */ -SECP256K1_API int secp256k1_ec_pubkey_serialize( - const secp256k1_context* ctx, - unsigned char *output, - size_t *outputlen, - const secp256k1_pubkey* pubkey, - unsigned int flags -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** Compare two public keys using lexicographic (of compressed serialization) order - * - * Returns: <0 if the first public key is less than the second - * >0 if the first public key is greater than the second - * 0 if the two public keys are equal - * Args: ctx: a secp256k1 context object. - * In: pubkey1: first public key to compare - * pubkey2: second public key to compare - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_cmp( - const secp256k1_context* ctx, - const secp256k1_pubkey* pubkey1, - const secp256k1_pubkey* pubkey2 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Parse an ECDSA signature in compact (64 bytes) format. - * - * Returns: 1 when the signature could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: sig: a pointer to a signature object - * In: input64: a pointer to the 64-byte array to parse - * - * The signature must consist of a 32-byte big endian R value, followed by a - * 32-byte big endian S value. If R or S fall outside of [0..order-1], the - * encoding is invalid. R and S with value 0 are allowed in the encoding. - * - * After the call, sig will always be initialized. If parsing failed or R or - * S are zero, the resulting sig value is guaranteed to fail validation for any - * message and public key. - */ -SECP256K1_API int secp256k1_ecdsa_signature_parse_compact( - const secp256k1_context* ctx, - secp256k1_ecdsa_signature* sig, - const unsigned char *input64 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Parse a DER ECDSA signature. - * - * Returns: 1 when the signature could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: sig: a pointer to a signature object - * In: input: a pointer to the signature to be parsed - * inputlen: the length of the array pointed to be input - * - * This function will accept any valid DER encoded signature, even if the - * encoded numbers are out of range. - * - * After the call, sig will always be initialized. If parsing failed or the - * encoded numbers are out of range, signature validation with it is - * guaranteed to fail for every message and public key. - */ -SECP256K1_API int secp256k1_ecdsa_signature_parse_der( - const secp256k1_context* ctx, - secp256k1_ecdsa_signature* sig, - const unsigned char *input, - size_t inputlen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Serialize an ECDSA signature in DER format. - * - * Returns: 1 if enough space was available to serialize, 0 otherwise - * Args: ctx: a secp256k1 context object - * Out: output: a pointer to an array to store the DER serialization - * In/Out: outputlen: a pointer to a length integer. Initially, this integer - * should be set to the length of output. After the call - * it will be set to the length of the serialization (even - * if 0 was returned). - * In: sig: a pointer to an initialized signature object - */ -SECP256K1_API int secp256k1_ecdsa_signature_serialize_der( - const secp256k1_context* ctx, - unsigned char *output, - size_t *outputlen, - const secp256k1_ecdsa_signature* sig -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** Serialize an ECDSA signature in compact (64 byte) format. - * - * Returns: 1 - * Args: ctx: a secp256k1 context object - * Out: output64: a pointer to a 64-byte array to store the compact serialization - * In: sig: a pointer to an initialized signature object - * - * See secp256k1_ecdsa_signature_parse_compact for details about the encoding. - */ -SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact( - const secp256k1_context* ctx, - unsigned char *output64, - const secp256k1_ecdsa_signature* sig -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Verify an ECDSA signature. - * - * Returns: 1: correct signature - * 0: incorrect or unparseable signature - * Args: ctx: a secp256k1 context object, initialized for verification. - * In: sig: the signature being verified (cannot be NULL) - * msghash32: the 32-byte message hash being verified (cannot be NULL). - * The verifier must make sure to apply a cryptographic - * hash function to the message by itself and not accept an - * msghash32 value directly. Otherwise, it would be easy to - * create a "valid" signature without knowledge of the - * secret key. See also - * https://bitcoin.stackexchange.com/a/81116/35586 for more - * background on this topic. - * pubkey: pointer to an initialized public key to verify with (cannot be NULL) - * - * To avoid accepting malleable signatures, only ECDSA signatures in lower-S - * form are accepted. - * - * If you need to accept ECDSA signatures from sources that do not obey this - * rule, apply secp256k1_ecdsa_signature_normalize to the signature prior to - * validation, but be aware that doing so results in malleable signatures. - * - * For details, see the comments for that function. - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( - const secp256k1_context* ctx, - const secp256k1_ecdsa_signature *sig, - const unsigned char *msghash32, - const secp256k1_pubkey *pubkey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** Convert a signature to a normalized lower-S form. - * - * Returns: 1 if sigin was not normalized, 0 if it already was. - * Args: ctx: a secp256k1 context object - * Out: sigout: a pointer to a signature to fill with the normalized form, - * or copy if the input was already normalized. (can be NULL if - * you're only interested in whether the input was already - * normalized). - * In: sigin: a pointer to a signature to check/normalize (cannot be NULL, - * can be identical to sigout) - * - * With ECDSA a third-party can forge a second distinct signature of the same - * message, given a single initial signature, but without knowing the key. This - * is done by negating the S value modulo the order of the curve, 'flipping' - * the sign of the random point R which is not included in the signature. - * - * Forgery of the same message isn't universally problematic, but in systems - * where message malleability or uniqueness of signatures is important this can - * cause issues. This forgery can be blocked by all verifiers forcing signers - * to use a normalized form. - * - * The lower-S form reduces the size of signatures slightly on average when - * variable length encodings (such as DER) are used and is cheap to verify, - * making it a good choice. Security of always using lower-S is assured because - * anyone can trivially modify a signature after the fact to enforce this - * property anyway. - * - * The lower S value is always between 0x1 and - * 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, - * inclusive. - * - * No other forms of ECDSA malleability are known and none seem likely, but - * there is no formal proof that ECDSA, even with this additional restriction, - * is free of other malleability. Commonly used serialization schemes will also - * accept various non-unique encodings, so care should be taken when this - * property is required for an application. - * - * The secp256k1_ecdsa_sign function will by default create signatures in the - * lower-S form, and secp256k1_ecdsa_verify will not accept others. In case - * signatures come from a system that cannot enforce this property, - * secp256k1_ecdsa_signature_normalize must be called before verification. - */ -SECP256K1_API int secp256k1_ecdsa_signature_normalize( - const secp256k1_context* ctx, - secp256k1_ecdsa_signature *sigout, - const secp256k1_ecdsa_signature *sigin -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3); - -/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function. - * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of - * extra entropy. - */ -SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; - -/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ -SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; - -/** Create an ECDSA signature. - * - * Returns: 1: signature created - * 0: the nonce generation function failed, or the secret key was invalid. - * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) - * In: msghash32: the 32-byte message hash being signed (cannot be NULL) - * seckey: pointer to a 32-byte secret key (cannot be NULL) - * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used - * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) - * - * The created signature is always in lower-S form. See - * secp256k1_ecdsa_signature_normalize for more details. - */ -SECP256K1_API int secp256k1_ecdsa_sign( - const secp256k1_context* ctx, - secp256k1_ecdsa_signature *sig, - const unsigned char *msghash32, - const unsigned char *seckey, - secp256k1_nonce_function noncefp, - const void *ndata -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** Verify an ECDSA secret key. - * - * A secret key is valid if it is not 0 and less than the secp256k1 curve order - * when interpreted as an integer (most significant byte first). The - * probability of choosing a 32-byte string uniformly at random which is an - * invalid secret key is negligible. - * - * Returns: 1: secret key is valid - * 0: secret key is invalid - * Args: ctx: pointer to a context object (cannot be NULL) - * In: seckey: pointer to a 32-byte secret key (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify( - const secp256k1_context* ctx, - const unsigned char *seckey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); - -/** Compute the public key for a secret key. - * - * Returns: 1: secret was valid, public key stores - * 0: secret was invalid, try again - * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * Out: pubkey: pointer to the created public key (cannot be NULL) - * In: seckey: pointer to a 32-byte secret key (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create( - const secp256k1_context* ctx, - secp256k1_pubkey *pubkey, - const unsigned char *seckey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Negates a secret key in place. - * - * Returns: 0 if the given secret key is invalid according to - * secp256k1_ec_seckey_verify. 1 otherwise - * Args: ctx: pointer to a context object - * In/Out: seckey: pointer to the 32-byte secret key to be negated. If the - * secret key is invalid according to - * secp256k1_ec_seckey_verify, this function returns 0 and - * seckey will be set to some unspecified value. (cannot be - * NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_negate( - const secp256k1_context* ctx, - unsigned char *seckey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); - -/** Same as secp256k1_ec_seckey_negate, but DEPRECATED. Will be removed in - * future versions. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate( - const secp256k1_context* ctx, - unsigned char *seckey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); - -/** Negates a public key in place. - * - * Returns: 1 always - * Args: ctx: pointer to a context object - * In/Out: pubkey: pointer to the public key to be negated (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate( - const secp256k1_context* ctx, - secp256k1_pubkey *pubkey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); - -/** Tweak a secret key by adding tweak to it. - * - * Returns: 0 if the arguments are invalid or the resulting secret key would be - * invalid (only when the tweak is the negation of the secret key). 1 - * otherwise. - * Args: ctx: pointer to a context object (cannot be NULL). - * In/Out: seckey: pointer to a 32-byte secret key. If the secret key is - * invalid according to secp256k1_ec_seckey_verify, this - * function returns 0. seckey will be set to some unspecified - * value if this function returns 0. (cannot be NULL) - * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to - * secp256k1_ec_seckey_verify, this function returns 0. For - * uniformly random 32-byte arrays the chance of being invalid - * is negligible (around 1 in 2^128) (cannot be NULL). - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add( - const secp256k1_context* ctx, - unsigned char *seckey, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Same as secp256k1_ec_seckey_tweak_add, but DEPRECATED. Will be removed in - * future versions. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( - const secp256k1_context* ctx, - unsigned char *seckey, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Tweak a public key by adding tweak times the generator to it. - * - * Returns: 0 if the arguments are invalid or the resulting public key would be - * invalid (only when the tweak is the negation of the corresponding - * secret key). 1 otherwise. - * Args: ctx: pointer to a context object initialized for validation - * (cannot be NULL). - * In/Out: pubkey: pointer to a public key object. pubkey will be set to an - * invalid value if this function returns 0 (cannot be NULL). - * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to - * secp256k1_ec_seckey_verify, this function returns 0. For - * uniformly random 32-byte arrays the chance of being invalid - * is negligible (around 1 in 2^128) (cannot be NULL). - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add( - const secp256k1_context* ctx, - secp256k1_pubkey *pubkey, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Tweak a secret key by multiplying it by a tweak. - * - * Returns: 0 if the arguments are invalid. 1 otherwise. - * Args: ctx: pointer to a context object (cannot be NULL). - * In/Out: seckey: pointer to a 32-byte secret key. If the secret key is - * invalid according to secp256k1_ec_seckey_verify, this - * function returns 0. seckey will be set to some unspecified - * value if this function returns 0. (cannot be NULL) - * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to - * secp256k1_ec_seckey_verify, this function returns 0. For - * uniformly random 32-byte arrays the chance of being invalid - * is negligible (around 1 in 2^128) (cannot be NULL). - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul( - const secp256k1_context* ctx, - unsigned char *seckey, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Same as secp256k1_ec_seckey_tweak_mul, but DEPRECATED. Will be removed in - * future versions. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( - const secp256k1_context* ctx, - unsigned char *seckey, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Tweak a public key by multiplying it by a tweak value. - * - * Returns: 0 if the arguments are invalid. 1 otherwise. - * Args: ctx: pointer to a context object initialized for validation - * (cannot be NULL). - * In/Out: pubkey: pointer to a public key object. pubkey will be set to an - * invalid value if this function returns 0 (cannot be NULL). - * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to - * secp256k1_ec_seckey_verify, this function returns 0. For - * uniformly random 32-byte arrays the chance of being invalid - * is negligible (around 1 in 2^128) (cannot be NULL). - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( - const secp256k1_context* ctx, - secp256k1_pubkey *pubkey, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Updates the context randomization to protect against side-channel leakage. - * Returns: 1: randomization successfully updated or nothing to randomize - * 0: error - * Args: ctx: pointer to a context object (cannot be NULL) - * In: seed32: pointer to a 32-byte random seed (NULL resets to initial state) - * - * While secp256k1 code is written to be constant-time no matter what secret - * values are, it's possible that a future compiler may output code which isn't, - * and also that the CPU may not emit the same radio frequencies or draw the same - * amount power for all values. - * - * This function provides a seed which is combined into the blinding value: that - * blinding value is added before each multiplication (and removed afterwards) so - * that it does not affect function results, but shields against attacks which - * rely on any input-dependent behaviour. - * - * This function has currently an effect only on contexts initialized for signing - * because randomization is currently used only for signing. However, this is not - * guaranteed and may change in the future. It is safe to call this function on - * contexts not initialized for signing; then it will have no effect and return 1. - * - * You should call this after secp256k1_context_create or - * secp256k1_context_clone (and secp256k1_context_preallocated_create or - * secp256k1_context_clone, resp.), and you may call this repeatedly afterwards. - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( - secp256k1_context* ctx, - const unsigned char *seed32 -) SECP256K1_ARG_NONNULL(1); - -/** Add a number of public keys together. - * - * Returns: 1: the sum of the public keys is valid. - * 0: the sum of the public keys is not valid. - * Args: ctx: pointer to a context object - * Out: out: pointer to a public key object for placing the resulting public key - * (cannot be NULL) - * In: ins: pointer to array of pointers to public keys (cannot be NULL) - * n: the number of public keys to add together (must be at least 1) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( - const secp256k1_context* ctx, - secp256k1_pubkey *out, - const secp256k1_pubkey * const * ins, - size_t n -) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Compute a tagged hash as defined in BIP-340. - * - * This is useful for creating a message hash and achieving domain separation - * through an application-specific tag. This function returns - * SHA256(SHA256(tag)||SHA256(tag)||msg). Therefore, tagged hash - * implementations optimized for a specific tag can precompute the SHA256 state - * after hashing the tag hashes. - * - * Returns 0 if the arguments are invalid and 1 otherwise. - * Args: ctx: pointer to a context object - * Out: hash32: pointer to a 32-byte array to store the resulting hash - * In: tag: pointer to an array containing the tag - * taglen: length of the tag array - * msg: pointer to an array containing the message - * msglen: length of the message array - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_tagged_sha256( - const secp256k1_context* ctx, - unsigned char *hash32, - const unsigned char *tag, - size_t taglen, - const unsigned char *msg, - size_t msglen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); - -#ifdef __cplusplus -} -#endif - -#endif /* SECP256K1_H */ diff --git a/libwallet/musig/secp256k1.k b/libwallet/musig/secp256k1.k deleted file mode 100644 index 108fc1a1..00000000 --- a/libwallet/musig/secp256k1.k +++ /dev/null @@ -1,901 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013-2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#define SECP256K1_BUILD - -#include "secp256k1.h" -#include "secp256k1_preallocated.h" - -#include "assumptions.h" -#include "util.h" -#include "field_impl.h" -#include "scalar_impl.h" -#include "group_impl.h" -#include "eccommit_impl.h" -#include "ecmult_impl.h" -#include "ecmult_const_impl.h" -#include "ecmult_gen_impl.h" -#include "ecdsa_impl.h" -#include "eckey_impl.h" -#include "hash_impl.h" -#include "scratch_impl.h" -#include "selftest.h" - -#ifdef SECP256K1_NO_BUILD -# error "secp256k1.h processed without SECP256K1_BUILD defined while building secp256k1.c" -#endif - -#if defined(VALGRIND) -# include -#endif - -#ifdef ENABLE_MODULE_GENERATOR -# include "secp256k1_generator.h" -#endif - -#ifdef ENABLE_MODULE_RANGEPROOF -# include "secp256k1_rangeproof.h" -# include "pedersen.h" -# include "rangeproof.h" -#endif - -#ifdef ENABLE_MODULE_ECDSA_S2C -# include "secp256k1_ecdsa_s2c.h" -static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge); -#else -typedef void secp256k1_ecdsa_s2c_opening; -static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge) { - (void) opening; - (void) ge; - VERIFY_CHECK(0); -} -#endif - -#define ARG_CHECK(cond) do { \ - if (EXPECT(!(cond), 0)) { \ - secp256k1_callback_call(&ctx->illegal_callback, #cond); \ - return 0; \ - } \ -} while(0) - -#define ARG_CHECK_NO_RETURN(cond) do { \ - if (EXPECT(!(cond), 0)) { \ - secp256k1_callback_call(&ctx->illegal_callback, #cond); \ - } \ -} while(0) - -#ifndef USE_EXTERNAL_DEFAULT_CALLBACKS -#include -#include -static void secp256k1_default_illegal_callback_fn(const char* str, void* data) { - (void)data; - fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str); - abort(); -} -static void secp256k1_default_error_callback_fn(const char* str, void* data) { - (void)data; - fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); - abort(); -} -#else -void secp256k1_default_illegal_callback_fn(const char* str, void* data); -void secp256k1_default_error_callback_fn(const char* str, void* data); -#endif - -static const secp256k1_callback default_illegal_callback = { - secp256k1_default_illegal_callback_fn, - NULL -}; - -static const secp256k1_callback default_error_callback = { - secp256k1_default_error_callback_fn, - NULL -}; - -struct secp256k1_context_struct { - secp256k1_ecmult_context ecmult_ctx; - secp256k1_ecmult_gen_context ecmult_gen_ctx; - secp256k1_callback illegal_callback; - secp256k1_callback error_callback; - int declassify; -}; - -static const secp256k1_context secp256k1_context_no_precomp_ = { - { 0 }, - { 0 }, - { secp256k1_default_illegal_callback_fn, 0 }, - { secp256k1_default_error_callback_fn, 0 }, - 0 -}; -const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_no_precomp_; - -size_t secp256k1_context_preallocated_size(unsigned int flags) { - size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context)); - /* A return value of 0 is reserved as an indicator for errors when we call this function internally. */ - VERIFY_CHECK(ret != 0); - - if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { - secp256k1_callback_call(&default_illegal_callback, - "Invalid flags"); - return 0; - } - - if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { - ret += SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE; - } - if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) { - ret += SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE; - } - return ret; -} - -size_t secp256k1_context_preallocated_clone_size(const secp256k1_context* ctx) { - size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context)); - VERIFY_CHECK(ctx != NULL); - if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) { - ret += SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE; - } - if (secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)) { - ret += SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE; - } - return ret; -} - -secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigned int flags) { - void* const base = prealloc; - size_t prealloc_size; - secp256k1_context* ret; - - if (!secp256k1_selftest()) { - secp256k1_callback_call(&default_error_callback, "self test failed"); - } - - prealloc_size = secp256k1_context_preallocated_size(flags); - if (prealloc_size == 0) { - return NULL; - } - VERIFY_CHECK(prealloc != NULL); - ret = (secp256k1_context*)manual_alloc(&prealloc, sizeof(secp256k1_context), base, prealloc_size); - ret->illegal_callback = default_illegal_callback; - ret->error_callback = default_error_callback; - - secp256k1_ecmult_context_init(&ret->ecmult_ctx); - secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); - - /* Flags have been checked by secp256k1_context_preallocated_size. */ - VERIFY_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_CONTEXT); - if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { - secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &prealloc); - } - if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) { - secp256k1_ecmult_context_build(&ret->ecmult_ctx, &prealloc); - } - ret->declassify = !!(flags & SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY); - - return (secp256k1_context*) ret; -} - -secp256k1_context* secp256k1_context_create(unsigned int flags) { - size_t const prealloc_size = secp256k1_context_preallocated_size(flags); - secp256k1_context* ctx = (secp256k1_context*)checked_malloc(&default_error_callback, prealloc_size); - if (EXPECT(secp256k1_context_preallocated_create(ctx, flags) == NULL, 0)) { - free(ctx); - return NULL; - } - - return ctx; -} - -secp256k1_context* secp256k1_context_preallocated_clone(const secp256k1_context* ctx, void* prealloc) { - size_t prealloc_size; - secp256k1_context* ret; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(prealloc != NULL); - - prealloc_size = secp256k1_context_preallocated_clone_size(ctx); - ret = (secp256k1_context*)prealloc; - memcpy(ret, ctx, prealloc_size); - secp256k1_ecmult_gen_context_finalize_memcpy(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx); - secp256k1_ecmult_context_finalize_memcpy(&ret->ecmult_ctx, &ctx->ecmult_ctx); - return ret; -} - -secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { - secp256k1_context* ret; - size_t prealloc_size; - - VERIFY_CHECK(ctx != NULL); - prealloc_size = secp256k1_context_preallocated_clone_size(ctx); - ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, prealloc_size); - ret = secp256k1_context_preallocated_clone(ctx, ret); - return ret; -} - -void secp256k1_context_preallocated_destroy(secp256k1_context* ctx) { - ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp); - if (ctx != NULL) { - secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); - secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); - } -} - -void secp256k1_context_destroy(secp256k1_context* ctx) { - if (ctx != NULL) { - secp256k1_context_preallocated_destroy(ctx); - free(ctx); - } -} - -void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { - ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp); - if (fun == NULL) { - fun = secp256k1_default_illegal_callback_fn; - } - ctx->illegal_callback.fn = fun; - ctx->illegal_callback.data = data; -} - -void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { - ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp); - if (fun == NULL) { - fun = secp256k1_default_error_callback_fn; - } - ctx->error_callback.fn = fun; - ctx->error_callback.data = data; -} - -secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context* ctx, size_t max_size) { - VERIFY_CHECK(ctx != NULL); - return secp256k1_scratch_create(&ctx->error_callback, max_size); -} - -void secp256k1_scratch_space_destroy(const secp256k1_context *ctx, secp256k1_scratch_space* scratch) { - VERIFY_CHECK(ctx != NULL); - secp256k1_scratch_destroy(&ctx->error_callback, scratch); -} - -/* Mark memory as no-longer-secret for the purpose of analysing constant-time behaviour - * of the software. This is setup for use with valgrind but could be substituted with - * the appropriate instrumentation for other analysis tools. - */ -static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, const void *p, size_t len) { -#if defined(VALGRIND) - if (EXPECT(ctx->declassify,0)) VALGRIND_MAKE_MEM_DEFINED(p, len); -#else - (void)ctx; - (void)p; - (void)len; -#endif -} - -static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { - if (sizeof(secp256k1_ge_storage) == 64) { - /* When the secp256k1_ge_storage type is exactly 64 byte, use its - * representation inside secp256k1_pubkey, as conversion is very fast. - * Note that secp256k1_pubkey_save must use the same representation. */ - secp256k1_ge_storage s; - memcpy(&s, &pubkey->data[0], sizeof(s)); - secp256k1_ge_from_storage(ge, &s); - } else { - /* Otherwise, fall back to 32-byte big endian for X and Y. */ - secp256k1_fe x, y; - secp256k1_fe_set_b32(&x, pubkey->data); - secp256k1_fe_set_b32(&y, pubkey->data + 32); - secp256k1_ge_set_xy(ge, &x, &y); - } - ARG_CHECK(!secp256k1_fe_is_zero(&ge->x)); - return 1; -} - -static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { - if (sizeof(secp256k1_ge_storage) == 64) { - secp256k1_ge_storage s; - secp256k1_ge_to_storage(&s, ge); - memcpy(&pubkey->data[0], &s, sizeof(s)); - } else { - VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); - secp256k1_fe_normalize_var(&ge->x); - secp256k1_fe_normalize_var(&ge->y); - secp256k1_fe_get_b32(pubkey->data, &ge->x); - secp256k1_fe_get_b32(pubkey->data + 32, &ge->y); - } -} - -int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) { - secp256k1_ge Q; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(pubkey != NULL); - memset(pubkey, 0, sizeof(*pubkey)); - ARG_CHECK(input != NULL); - if (!secp256k1_eckey_pubkey_parse(&Q, input, inputlen)) { - return 0; - } - if (!secp256k1_ge_is_in_correct_subgroup(&Q)) { - return 0; - } - secp256k1_pubkey_save(pubkey, &Q); - secp256k1_ge_clear(&Q); - return 1; -} - -int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey* pubkey, unsigned int flags) { - secp256k1_ge Q; - size_t len; - int ret = 0; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(outputlen != NULL); - ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33u : 65u)); - len = *outputlen; - *outputlen = 0; - ARG_CHECK(output != NULL); - memset(output, 0, len); - ARG_CHECK(pubkey != NULL); - ARG_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_COMPRESSION); - if (secp256k1_pubkey_load(ctx, &Q, pubkey)) { - ret = secp256k1_eckey_pubkey_serialize(&Q, output, &len, flags & SECP256K1_FLAGS_BIT_COMPRESSION); - if (ret) { - *outputlen = len; - } - } - return ret; -} - -int secp256k1_ec_pubkey_cmp(const secp256k1_context* ctx, const secp256k1_pubkey* pubkey0, const secp256k1_pubkey* pubkey1) { - unsigned char out[2][33]; - const secp256k1_pubkey* pk[2]; - int i; - - VERIFY_CHECK(ctx != NULL); - pk[0] = pubkey0; pk[1] = pubkey1; - for (i = 0; i < 2; i++) { - size_t out_size = sizeof(out[i]); - /* If the public key is NULL or invalid, ec_pubkey_serialize will call - * the illegal_callback and return 0. In that case we will serialize the - * key as all zeros which is less than any valid public key. This - * results in consistent comparisons even if NULL or invalid pubkeys are - * involved and prevents edge cases such as sorting algorithms that use - * this function and do not terminate as a result. */ - if (!secp256k1_ec_pubkey_serialize(ctx, out[i], &out_size, pk[i], SECP256K1_EC_COMPRESSED)) { - /* Note that ec_pubkey_serialize should already set the output to - * zero in that case, but it's not guaranteed by the API, we can't - * test it and writing a VERIFY_CHECK is more complex than - * explicitly memsetting (again). */ - memset(out[i], 0, sizeof(out[i])); - } - } - return secp256k1_memcmp_var(out[0], out[1], sizeof(out[0])); -} - -static void secp256k1_ecdsa_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_ecdsa_signature* sig) { - (void)ctx; - if (sizeof(secp256k1_scalar) == 32) { - /* When the secp256k1_scalar type is exactly 32 byte, use its - * representation inside secp256k1_ecdsa_signature, as conversion is very fast. - * Note that secp256k1_ecdsa_signature_save must use the same representation. */ - memcpy(r, &sig->data[0], 32); - memcpy(s, &sig->data[32], 32); - } else { - secp256k1_scalar_set_b32(r, &sig->data[0], NULL); - secp256k1_scalar_set_b32(s, &sig->data[32], NULL); - } -} - -static void secp256k1_ecdsa_signature_save(secp256k1_ecdsa_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s) { - if (sizeof(secp256k1_scalar) == 32) { - memcpy(&sig->data[0], r, 32); - memcpy(&sig->data[32], s, 32); - } else { - secp256k1_scalar_get_b32(&sig->data[0], r); - secp256k1_scalar_get_b32(&sig->data[32], s); - } -} - -int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { - secp256k1_scalar r, s; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig != NULL); - ARG_CHECK(input != NULL); - - if (secp256k1_ecdsa_sig_parse(&r, &s, input, inputlen)) { - secp256k1_ecdsa_signature_save(sig, &r, &s); - return 1; - } else { - memset(sig, 0, sizeof(*sig)); - return 0; - } -} - -int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input64) { - secp256k1_scalar r, s; - int ret = 1; - int overflow = 0; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig != NULL); - ARG_CHECK(input64 != NULL); - - secp256k1_scalar_set_b32(&r, &input64[0], &overflow); - ret &= !overflow; - secp256k1_scalar_set_b32(&s, &input64[32], &overflow); - ret &= !overflow; - if (ret) { - secp256k1_ecdsa_signature_save(sig, &r, &s); - } else { - memset(sig, 0, sizeof(*sig)); - } - return ret; -} - -int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig) { - secp256k1_scalar r, s; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(output != NULL); - ARG_CHECK(outputlen != NULL); - ARG_CHECK(sig != NULL); - - secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); - return secp256k1_ecdsa_sig_serialize(output, outputlen, &r, &s); -} - -int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig) { - secp256k1_scalar r, s; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(output64 != NULL); - ARG_CHECK(sig != NULL); - - secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); - secp256k1_scalar_get_b32(&output64[0], &r); - secp256k1_scalar_get_b32(&output64[32], &s); - return 1; -} - -int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin) { - secp256k1_scalar r, s; - int ret = 0; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sigin != NULL); - - secp256k1_ecdsa_signature_load(ctx, &r, &s, sigin); - ret = secp256k1_scalar_is_high(&s); - if (sigout != NULL) { - if (ret) { - secp256k1_scalar_negate(&s, &s); - } - secp256k1_ecdsa_signature_save(sigout, &r, &s); - } - - return ret; -} - -int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msghash32, const secp256k1_pubkey *pubkey) { - secp256k1_ge q; - secp256k1_scalar r, s; - secp256k1_scalar m; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(msghash32 != NULL); - ARG_CHECK(sig != NULL); - ARG_CHECK(pubkey != NULL); - - secp256k1_scalar_set_b32(&m, msghash32, NULL); - secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); - return (!secp256k1_scalar_is_high(&s) && - secp256k1_pubkey_load(ctx, &q, pubkey) && - secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m)); -} - -static SECP256K1_INLINE void buffer_append(unsigned char *buf, unsigned int *offset, const void *data, unsigned int len) { - memcpy(buf + *offset, data, len); - *offset += len; -} - -static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { - unsigned char keydata[112]; - unsigned int offset = 0; - secp256k1_rfc6979_hmac_sha256 rng; - unsigned int i; - /* We feed a byte array to the PRNG as input, consisting of: - * - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d. - * - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data. - * - optionally 16 extra bytes with the algorithm name. - * Because the arguments have distinct fixed lengths it is not possible for - * different argument mixtures to emulate each other and result in the same - * nonces. - */ - buffer_append(keydata, &offset, key32, 32); - buffer_append(keydata, &offset, msg32, 32); - if (data != NULL) { - buffer_append(keydata, &offset, data, 32); - } - if (algo16 != NULL) { - buffer_append(keydata, &offset, algo16, 16); - } - secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, offset); - memset(keydata, 0, sizeof(keydata)); - for (i = 0; i <= counter; i++) { - secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); - } - secp256k1_rfc6979_hmac_sha256_finalize(&rng); - return 1; -} - -const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; -const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; - -static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, secp256k1_sha256* s2c_sha, secp256k1_ecdsa_s2c_opening *s2c_opening, const unsigned char* s2c_data32, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { - secp256k1_scalar sec, non, msg; - int ret = 0; - int is_sec_valid; - unsigned char nonce32[32]; - unsigned int count = 0; - /* Default initialization here is important so we won't pass uninit values to the cmov in the end */ - *r = secp256k1_scalar_zero; - *s = secp256k1_scalar_zero; - if (recid) { - *recid = 0; - } - if (noncefp == NULL) { - noncefp = secp256k1_nonce_function_default; - } - /* sign-to-contract commitments only work with the default nonce function, - * because we need to ensure that s2c_data is actually hashed into the nonce and - * not just ignored. Otherwise an attacker can exfiltrate the secret key by - * signing the same message thrice with different commitments. */ - VERIFY_CHECK(s2c_data32 == NULL || noncefp == secp256k1_nonce_function_default); - - /* Fail if the secret key is invalid. */ - is_sec_valid = secp256k1_scalar_set_b32_seckey(&sec, seckey); - secp256k1_scalar_cmov(&sec, &secp256k1_scalar_one, !is_sec_valid); - secp256k1_scalar_set_b32(&msg, msg32, NULL); - while (1) { - int is_nonce_valid; - ret = !!noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); - if (!ret) { - break; - } - is_nonce_valid = secp256k1_scalar_set_b32_seckey(&non, nonce32); - /* The nonce is still secret here, but it being invalid is is less likely than 1:2^255. */ - secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid)); - if (is_nonce_valid) { - if (s2c_data32 != NULL) { - secp256k1_gej nonce_pj; - secp256k1_ge nonce_p; - - /* Compute original nonce commitment/pubkey */ - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_pj, &non); - secp256k1_ge_set_gej(&nonce_p, &nonce_pj); - if (s2c_opening != NULL) { - secp256k1_ecdsa_s2c_opening_save(s2c_opening, &nonce_p); - } - - /* Because the nonce is valid, the nonce point isn't the point - * at infinity and we can declassify that information to be able to - * serialize the point. */ - secp256k1_declassify(ctx, &nonce_p.infinity, sizeof(nonce_p.infinity)); - - /* Tweak nonce with s2c commitment. */ - ret = secp256k1_ec_commit_seckey(&non, &nonce_p, s2c_sha, s2c_data32, 32); - secp256k1_declassify(ctx, &ret, sizeof(ret)); /* may be secret that the tweak falied, but happens with negligible probability */ - if (!ret) { - break; - } - } - - ret = secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, r, s, &sec, &msg, &non, recid); - /* The final signature is no longer a secret, nor is the fact that we were successful or not. */ - secp256k1_declassify(ctx, &ret, sizeof(ret)); - if (ret) { - break; - } - } - count++; - } - /* We don't want to declassify is_sec_valid and therefore the range of - * seckey. As a result is_sec_valid is included in ret only after ret was - * used as a branching variable. */ - ret &= is_sec_valid; - memset(nonce32, 0, 32); - secp256k1_scalar_clear(&msg); - secp256k1_scalar_clear(&non); - secp256k1_scalar_clear(&sec); - secp256k1_scalar_cmov(r, &secp256k1_scalar_zero, !ret); - secp256k1_scalar_cmov(s, &secp256k1_scalar_zero, !ret); - if (recid) { - const int zero = 0; - secp256k1_int_cmov(recid, &zero, !ret); - } - return ret; -} - -int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msghash32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { - secp256k1_scalar r, s; - int ret; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(msghash32 != NULL); - ARG_CHECK(signature != NULL); - ARG_CHECK(seckey != NULL); - - ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, NULL, NULL, NULL, msghash32, seckey, noncefp, noncedata); - secp256k1_ecdsa_signature_save(signature, &r, &s); - return ret; -} - -int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char *seckey) { - secp256k1_scalar sec; - int ret; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(seckey != NULL); - - ret = secp256k1_scalar_set_b32_seckey(&sec, seckey); - secp256k1_scalar_clear(&sec); - return ret; -} - -static int secp256k1_ec_pubkey_create_helper(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_scalar *seckey_scalar, secp256k1_ge *p, const unsigned char *seckey) { - secp256k1_gej pj; - int ret; - - ret = secp256k1_scalar_set_b32_seckey(seckey_scalar, seckey); - secp256k1_scalar_cmov(seckey_scalar, &secp256k1_scalar_one, !ret); - - secp256k1_ecmult_gen(ecmult_gen_ctx, &pj, seckey_scalar); - secp256k1_ge_set_gej(p, &pj); - return ret; -} - -int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) { - secp256k1_ge p; - secp256k1_scalar seckey_scalar; - int ret = 0; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(pubkey != NULL); - memset(pubkey, 0, sizeof(*pubkey)); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(seckey != NULL); - - ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey); - secp256k1_pubkey_save(pubkey, &p); - secp256k1_memczero(pubkey, sizeof(*pubkey), !ret); - - secp256k1_scalar_clear(&seckey_scalar); - return ret; -} - -int secp256k1_ec_seckey_negate(const secp256k1_context* ctx, unsigned char *seckey) { - secp256k1_scalar sec; - int ret = 0; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(seckey != NULL); - - ret = secp256k1_scalar_set_b32_seckey(&sec, seckey); - secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret); - secp256k1_scalar_negate(&sec, &sec); - secp256k1_scalar_get_b32(seckey, &sec); - - secp256k1_scalar_clear(&sec); - return ret; -} - -int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *seckey) { - return secp256k1_ec_seckey_negate(ctx, seckey); -} - -int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *pubkey) { - int ret = 0; - secp256k1_ge p; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(pubkey != NULL); - - ret = secp256k1_pubkey_load(ctx, &p, pubkey); - memset(pubkey, 0, sizeof(*pubkey)); - if (ret) { - secp256k1_ge_neg(&p, &p); - secp256k1_pubkey_save(pubkey, &p); - } - return ret; -} - - -static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak32) { - secp256k1_scalar term; - int overflow = 0; - int ret = 0; - - secp256k1_scalar_set_b32(&term, tweak32, &overflow); - ret = (!overflow) & secp256k1_eckey_privkey_tweak_add(sec, &term); - secp256k1_scalar_clear(&term); - return ret; -} - -int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) { - secp256k1_scalar sec; - int ret = 0; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(seckey != NULL); - ARG_CHECK(tweak32 != NULL); - - ret = secp256k1_scalar_set_b32_seckey(&sec, seckey); - ret &= secp256k1_ec_seckey_tweak_add_helper(&sec, tweak32); - secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret); - secp256k1_scalar_get_b32(seckey, &sec); - - secp256k1_scalar_clear(&sec); - return ret; -} - -int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) { - return secp256k1_ec_seckey_tweak_add(ctx, seckey, tweak32); -} - -static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak32) { - secp256k1_scalar term; - int overflow = 0; - secp256k1_scalar_set_b32(&term, tweak32, &overflow); - return !overflow && secp256k1_eckey_pubkey_tweak_add(ecmult_ctx, p, &term); -} - -int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak32) { - secp256k1_ge p; - int ret = 0; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(pubkey != NULL); - ARG_CHECK(tweak32 != NULL); - - ret = secp256k1_pubkey_load(ctx, &p, pubkey); - memset(pubkey, 0, sizeof(*pubkey)); - ret = ret && secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &p, tweak32); - if (ret) { - secp256k1_pubkey_save(pubkey, &p); - } - - return ret; -} - -int secp256k1_ec_seckey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) { - secp256k1_scalar factor; - secp256k1_scalar sec; - int ret = 0; - int overflow = 0; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(seckey != NULL); - ARG_CHECK(tweak32 != NULL); - - secp256k1_scalar_set_b32(&factor, tweak32, &overflow); - ret = secp256k1_scalar_set_b32_seckey(&sec, seckey); - ret &= (!overflow) & secp256k1_eckey_privkey_tweak_mul(&sec, &factor); - secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret); - secp256k1_scalar_get_b32(seckey, &sec); - - secp256k1_scalar_clear(&sec); - secp256k1_scalar_clear(&factor); - return ret; -} - -int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) { - return secp256k1_ec_seckey_tweak_mul(ctx, seckey, tweak32); -} - -int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak32) { - secp256k1_ge p; - secp256k1_scalar factor; - int ret = 0; - int overflow = 0; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(pubkey != NULL); - ARG_CHECK(tweak32 != NULL); - - secp256k1_scalar_set_b32(&factor, tweak32, &overflow); - ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); - memset(pubkey, 0, sizeof(*pubkey)); - if (ret) { - if (secp256k1_eckey_pubkey_tweak_mul(&ctx->ecmult_ctx, &p, &factor)) { - secp256k1_pubkey_save(pubkey, &p); - } else { - ret = 0; - } - } - - return ret; -} - -int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) { - VERIFY_CHECK(ctx != NULL); - if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) { - secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); - } - return 1; -} - -int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, const secp256k1_pubkey * const *pubnonces, size_t n) { - size_t i; - secp256k1_gej Qj; - secp256k1_ge Q; - - ARG_CHECK(pubnonce != NULL); - memset(pubnonce, 0, sizeof(*pubnonce)); - ARG_CHECK(n >= 1); - ARG_CHECK(pubnonces != NULL); - - secp256k1_gej_set_infinity(&Qj); - - for (i = 0; i < n; i++) { - secp256k1_pubkey_load(ctx, &Q, pubnonces[i]); - secp256k1_gej_add_ge(&Qj, &Qj, &Q); - } - if (secp256k1_gej_is_infinity(&Qj)) { - return 0; - } - secp256k1_ge_set_gej(&Q, &Qj); - secp256k1_pubkey_save(pubnonce, &Q); - return 1; -} - -int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, const unsigned char *tag, size_t taglen, const unsigned char *msg, size_t msglen) { - secp256k1_sha256 sha; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(hash32 != NULL); - ARG_CHECK(tag != NULL); - ARG_CHECK(msg != NULL); - - secp256k1_sha256_initialize_tagged(&sha, tag, taglen); - secp256k1_sha256_write(&sha, msg, msglen); - secp256k1_sha256_finalize(&sha, hash32); - return 1; -} - -#ifdef ENABLE_MODULE_ECDH -# include "main_impl.h" -#endif - -#ifdef ENABLE_MODULE_RECOVERY -# include "main_impl.h" -#endif - -#ifdef ENABLE_MODULE_EXTRAKEYS -# include "main_impl.h" -#endif - -#ifdef ENABLE_MODULE_SCHNORRSIG -# include "main_impl.h" -#endif - -#ifdef ENABLE_MODULE_ECDSA_S2C -# include "modules/ecdsa_s2c/main_impl.h" -#endif - -#ifdef ENABLE_MODULE_ECDSA_ADAPTOR -# include "modules/ecdsa_adaptor/main_impl.h" -#endif - -#ifdef ENABLE_MODULE_MUSIG -# include "main_impl.h" -#endif - -#ifdef ENABLE_MODULE_GENERATOR -# include "main_impl.h" -#endif - -#ifdef ENABLE_MODULE_RANGEPROOF -# include "main_impl.h" -#endif - -#ifdef ENABLE_MODULE_WHITELIST -# include "main_impl.h" -#endif - -#ifdef ENABLE_MODULE_SURJECTIONPROOF -# include "main_impl.h" -#endif - diff --git a/libwallet/musig/secp256k1_extrakeys.h b/libwallet/musig/secp256k1_extrakeys.h deleted file mode 100644 index 4a3ca0b1..00000000 --- a/libwallet/musig/secp256k1_extrakeys.h +++ /dev/null @@ -1,278 +0,0 @@ -#ifndef SECP256K1_EXTRAKEYS_H -#define SECP256K1_EXTRAKEYS_H - -#include "secp256k1.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** Opaque data structure that holds a parsed and valid "x-only" public key. - * An x-only pubkey encodes a point whose Y coordinate is even. It is - * serialized using only its X coordinate (32 bytes). See BIP-340 for more - * information about x-only pubkeys. - * - * The exact representation of data inside is implementation defined and not - * guaranteed to be portable between different platforms or versions. It is - * however guaranteed to be 64 bytes in size, and can be safely copied/moved. - * If you need to convert to a format suitable for storage, transmission, use - * use secp256k1_xonly_pubkey_serialize and secp256k1_xonly_pubkey_parse. To - * compare keys, use secp256k1_xonly_pubkey_cmp. - */ -typedef struct { - unsigned char data[64]; -} secp256k1_xonly_pubkey; - -/** Opaque data structure that holds a keypair consisting of a secret and a - * public key. - * - * The exact representation of data inside is implementation defined and not - * guaranteed to be portable between different platforms or versions. It is - * however guaranteed to be 96 bytes in size, and can be safely copied/moved. - */ -typedef struct { - unsigned char data[96]; -} secp256k1_keypair; - -/** Parse a 32-byte sequence into a xonly_pubkey object. - * - * Returns: 1 if the public key was fully valid. - * 0 if the public key could not be parsed or is invalid. - * - * Args: ctx: a secp256k1 context object (cannot be NULL). - * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a - * parsed version of input. If not, it's set to an invalid value. - * (cannot be NULL). - * In: input32: pointer to a serialized xonly_pubkey (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_parse( - const secp256k1_context* ctx, - secp256k1_xonly_pubkey* pubkey, - const unsigned char *input32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Serialize an xonly_pubkey object into a 32-byte sequence. - * - * Returns: 1 always. - * - * Args: ctx: a secp256k1 context object (cannot be NULL). - * Out: output32: a pointer to a 32-byte array to place the serialized key in - * (cannot be NULL). - * In: pubkey: a pointer to a secp256k1_xonly_pubkey containing an - * initialized public key (cannot be NULL). - */ -SECP256K1_API int secp256k1_xonly_pubkey_serialize( - const secp256k1_context* ctx, - unsigned char *output32, - const secp256k1_xonly_pubkey* pubkey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Compare two x-only public keys using lexicographic order - * - * Returns: <0 if the first public key is less than the second - * >0 if the first public key is greater than the second - * 0 if the two public keys are equal - * Args: ctx: a secp256k1 context object. - * In: pubkey1: first public key to compare - * pubkey2: second public key to compare - */ -SECP256K1_API int secp256k1_xonly_pubkey_cmp( - const secp256k1_context* ctx, - const secp256k1_xonly_pubkey* pk1, - const secp256k1_xonly_pubkey* pk2 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey. - * - * Returns: 1 if the public key was successfully converted - * 0 otherwise - * - * Args: ctx: pointer to a context object (cannot be NULL) - * Out: xonly_pubkey: pointer to an x-only public key object for placing the - * converted public key (cannot be NULL) - * pk_parity: pointer to an integer that will be set to 1 if the point - * encoded by xonly_pubkey is the negation of the pubkey and - * set to 0 otherwise. (can be NULL) - * In: pubkey: pointer to a public key that is converted (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubkey( - const secp256k1_context* ctx, - secp256k1_xonly_pubkey *xonly_pubkey, - int *pk_parity, - const secp256k1_pubkey *pubkey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); - -/** Tweak an x-only public key by adding the generator multiplied with tweak32 - * to it. - * - * Note that the resulting point can not in general be represented by an x-only - * pubkey because it may have an odd Y coordinate. Instead, the output_pubkey - * is a normal secp256k1_pubkey. - * - * Returns: 0 if the arguments are invalid or the resulting public key would be - * invalid (only when the tweak is the negation of the corresponding - * secret key). 1 otherwise. - * - * Args: ctx: pointer to a context object initialized for verification - * (cannot be NULL) - * Out: output_pubkey: pointer to a public key to store the result. Will be set - * to an invalid value if this function returns 0 (cannot - * be NULL) - * In: internal_pubkey: pointer to an x-only pubkey to apply the tweak to. - * (cannot be NULL). - * tweak32: pointer to a 32-byte tweak. If the tweak is invalid - * according to secp256k1_ec_seckey_verify, this function - * returns 0. For uniformly random 32-byte arrays the - * chance of being invalid is negligible (around 1 in - * 2^128) (cannot be NULL). - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add( - const secp256k1_context* ctx, - secp256k1_pubkey *output_pubkey, - const secp256k1_xonly_pubkey *internal_pubkey, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** Checks that a tweaked pubkey is the result of calling - * secp256k1_xonly_pubkey_tweak_add with internal_pubkey and tweak32. - * - * The tweaked pubkey is represented by its 32-byte x-only serialization and - * its pk_parity, which can both be obtained by converting the result of - * tweak_add to a secp256k1_xonly_pubkey. - * - * Note that this alone does _not_ verify that the tweaked pubkey is a - * commitment. If the tweak is not chosen in a specific way, the tweaked pubkey - * can easily be the result of a different internal_pubkey and tweak. - * - * Returns: 0 if the arguments are invalid or the tweaked pubkey is not the - * result of tweaking the internal_pubkey with tweak32. 1 otherwise. - * Args: ctx: pointer to a context object initialized for verification - * (cannot be NULL) - * In: tweaked_pubkey32: pointer to a serialized xonly_pubkey (cannot be NULL) - * tweaked_pk_parity: the parity of the tweaked pubkey (whose serialization - * is passed in as tweaked_pubkey32). This must match the - * pk_parity value that is returned when calling - * secp256k1_xonly_pubkey with the tweaked pubkey, or - * this function will fail. - * internal_pubkey: pointer to an x-only public key object to apply the - * tweak to (cannot be NULL) - * tweak32: pointer to a 32-byte tweak (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_check( - const secp256k1_context* ctx, - const unsigned char *tweaked_pubkey32, - int tweaked_pk_parity, - const secp256k1_xonly_pubkey *internal_pubkey, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); - -/** Sorts xonly public keys according to secp256k1_xonly_pubkey_cmp - * - * Returns: 0 if the arguments are invalid. 1 otherwise. - * - * Args: ctx: pointer to a context object - * In: pubkeys: array of pointers to pubkeys to sort - * n_pubkeys: number of elements in the pubkeys array - */ -SECP256K1_API int secp256k1_xonly_sort( - const secp256k1_context* ctx, - const secp256k1_xonly_pubkey **pubkeys, - size_t n_pubkeys -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); - -/** Compute the keypair for a secret key. - * - * Returns: 1: secret was valid, keypair is ready to use - * 0: secret was invalid, try again with a different secret - * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * Out: keypair: pointer to the created keypair (cannot be NULL) - * In: seckey: pointer to a 32-byte secret key (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create( - const secp256k1_context* ctx, - secp256k1_keypair *keypair, - const unsigned char *seckey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Get the secret key from a keypair. - * - * Returns: 0 if the arguments are invalid. 1 otherwise. - * Args: ctx: pointer to a context object (cannot be NULL) - * Out: seckey: pointer to a 32-byte buffer for the secret key (cannot be NULL) - * In: keypair: pointer to a keypair (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_sec( - const secp256k1_context* ctx, - unsigned char *seckey, - const secp256k1_keypair *keypair -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Get the public key from a keypair. - * - * Returns: 0 if the arguments are invalid. 1 otherwise. - * Args: ctx: pointer to a context object (cannot be NULL) - * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to - * the keypair public key. If not, it's set to an invalid value. - * (cannot be NULL) - * In: keypair: pointer to a keypair (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub( - const secp256k1_context* ctx, - secp256k1_pubkey *pubkey, - const secp256k1_keypair *keypair -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Get the x-only public key from a keypair. - * - * This is the same as calling secp256k1_keypair_pub and then - * secp256k1_xonly_pubkey_from_pubkey. - * - * Returns: 0 if the arguments are invalid. 1 otherwise. - * Args: ctx: pointer to a context object (cannot be NULL) - * Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set - * to the keypair public key after converting it to an - * xonly_pubkey. If not, it's set to an invalid value (cannot be - * NULL). - * pk_parity: pointer to an integer that will be set to the pk_parity - * argument of secp256k1_xonly_pubkey_from_pubkey (can be NULL). - * In: keypair: pointer to a keypair (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub( - const secp256k1_context* ctx, - secp256k1_xonly_pubkey *pubkey, - int *pk_parity, - const secp256k1_keypair *keypair -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); - -/** Tweak a keypair by adding tweak32 to the secret key and updating the public - * key accordingly. - * - * Calling this function and then secp256k1_keypair_pub results in the same - * public key as calling secp256k1_keypair_xonly_pub and then - * secp256k1_xonly_pubkey_tweak_add. - * - * Returns: 0 if the arguments are invalid or the resulting keypair would be - * invalid (only when the tweak is the negation of the keypair's - * secret key). 1 otherwise. - * - * Args: ctx: pointer to a context object initialized for verification - * (cannot be NULL) - * In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to - * an invalid value if this function returns 0 (cannot be - * NULL). - * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according - * to secp256k1_ec_seckey_verify, this function returns 0. For - * uniformly random 32-byte arrays the chance of being invalid - * is negligible (around 1 in 2^128) (cannot be NULL). - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add( - const secp256k1_context* ctx, - secp256k1_keypair *keypair, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -#ifdef __cplusplus -} -#endif - -#endif /* SECP256K1_EXTRAKEYS_H */ diff --git a/libwallet/musig/secp256k1_musig.h b/libwallet/musig/secp256k1_musig.h deleted file mode 100644 index dfda298d..00000000 --- a/libwallet/musig/secp256k1_musig.h +++ /dev/null @@ -1,497 +0,0 @@ -#ifndef SECP256K1_MUSIG_H -#define SECP256K1_MUSIG_H - -#include "secp256k1_extrakeys.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/** This module implements a Schnorr-based multi-signature scheme called MuSig2 - * (https://eprint.iacr.org/2020/1261). It is compatible with BIP-340 ("Schnorr"). - * There's an example C source file in the module's directory - * (src/modules/musig/example.c) that demonstrates how it can be used. - * - * The module also supports BIP-341 ("Taproot") public key tweaking and adaptor - * signatures as described in - * https://github.com/ElementsProject/scriptless-scripts/pull/24 - * - * It is recommended to read the documention in this include file carefully. - * Further notes on API usage can be found in src/modules/musig/musig.md - */ - -/** Opaque data structures - * - * The exact representation of data inside is implementation defined and not - * guaranteed to be portable between different platforms or versions. It can, - * however, be safely copied/moved. If you need to convert to a format suitable - * for storage, transmission, or comparison, use the corresponding - * serialization and parsing functions. - */ - -/** Opaque data structure that caches information about public key aggregation. - * - * Guaranteed to be 165 bytes in size. No serialization and parsing functions - * (yet). - */ -typedef struct { - unsigned char data[165]; -} secp256k1_musig_keyagg_cache; - -/** Opaque data structure that holds a signer's _secret_ nonce. - * - * Guaranteed to be 68 bytes in size. - * - * WARNING: This structure MUST NOT be copied or read or written to directly. A - * signer who is online throughout the whole process and can keep this - * structure in memory can use the provided API functions for a safe standard - * workflow. See - * https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ for - * more details about the risks associated with serializing or deserializing - * this structure. - * - * We repeat, copying this data structure can result in nonce reuse which will - * leak the secret signing key. - */ -typedef struct { - unsigned char data[68]; -} secp256k1_musig_secnonce; - -/** Opaque data structure that holds a signer's public nonce. -* -* Guaranteed to be 132 bytes in size. Serialized and parsed with -* `musig_pubnonce_serialize` and `musig_pubnonce_parse`. -*/ -typedef struct { - unsigned char data[132]; -} secp256k1_musig_pubnonce; - -/** Opaque data structure that holds an aggregate public nonce. - * - * Guaranteed to be 132 bytes in size. Serialized and parsed with - * `musig_aggnonce_serialize` and `musig_aggnonce_parse`. - */ -typedef struct { - unsigned char data[132]; -} secp256k1_musig_aggnonce; - -/** Opaque data structure that holds a cache for a MuSig session. - * - * This structure is not necessarily required to be kept secret. Guaranteed to - * be 133 bytes in size. No serialization and parsing functions (yet). - */ -typedef struct { - unsigned char data[133]; -} secp256k1_musig_session; - -/** Opaque data structure that holds a partial MuSig signature. - * - * Guaranteed to be 36 bytes in size. Serialized and parsed with - * `musig_partial_sig_serialize` and `musig_partial_sig_parse`. - */ -typedef struct { - unsigned char data[36]; -} secp256k1_musig_partial_sig; - -/** Parse a signers public nonce. - * - * Returns: 1 when the nonce could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: nonce: pointer to a nonce object - * In: in66: pointer to the 66-byte nonce to be parsed - */ -SECP256K1_API int secp256k1_musig_pubnonce_parse( - const secp256k1_context* ctx, - secp256k1_musig_pubnonce* nonce, - const unsigned char *in66 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Serialize a signer's public nonce - * - * Returns: 1 when the nonce could be serialized, 0 otherwise - * Args: ctx: a secp256k1 context object - * Out: out32: pointer to a 66-byte array to store the serialized nonce - * In: nonce: pointer to the nonce - */ -SECP256K1_API int secp256k1_musig_pubnonce_serialize( - const secp256k1_context* ctx, - unsigned char *out66, - const secp256k1_musig_pubnonce* nonce -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Parse an aggregate public nonce. - * - * Returns: 1 when the nonce could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: nonce: pointer to a nonce object - * In: in66: pointer to the 66-byte nonce to be parsed - */ -SECP256K1_API int secp256k1_musig_aggnonce_parse( - const secp256k1_context* ctx, - secp256k1_musig_aggnonce* nonce, - const unsigned char *in66 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Serialize an aggregate public nonce - * - * Returns: 1 when the nonce could be serialized, 0 otherwise - * Args: ctx: a secp256k1 context object - * Out: out32: pointer to a 66-byte array to store the serialized nonce - * In: nonce: pointer to the nonce - */ -SECP256K1_API int secp256k1_musig_aggnonce_serialize( - const secp256k1_context* ctx, - unsigned char *out66, - const secp256k1_musig_aggnonce* nonce -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Serialize a MuSig partial signature or adaptor signature - * - * Returns: 1 when the signature could be serialized, 0 otherwise - * Args: ctx: a secp256k1 context object - * Out: out32: pointer to a 32-byte array to store the serialized signature - * In: sig: pointer to the signature - */ -SECP256K1_API int secp256k1_musig_partial_sig_serialize( - const secp256k1_context* ctx, - unsigned char *out32, - const secp256k1_musig_partial_sig* sig -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Parse a MuSig partial signature. - * - * Returns: 1 when the signature could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: sig: pointer to a signature object - * In: in32: pointer to the 32-byte signature to be parsed - * - * After the call, sig will always be initialized. If parsing failed or the - * encoded numbers are out of range, signature verification with it is - * guaranteed to fail for every message and public key. - */ -SECP256K1_API int secp256k1_musig_partial_sig_parse( - const secp256k1_context* ctx, - secp256k1_musig_partial_sig* sig, - const unsigned char *in32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Computes a aggregate public key and the hash of the given public keys. - * - * Different orders of `pubkeys` result in different `agg_pk`s. - * - * The pubkeys can be sorted before combining with `secp256k1_xonly_sort` which - * ensures the same resulting `agg_pk` for the same multiset of pubkeys. - * This is useful to do before pubkey_agg, such that the order of pubkeys - * does not affect the aggregate public key. - * - * Returns: 1 if the public keys were successfully aggregated, 0 otherwise - * Args: ctx: pointer to a context object initialized for verification - * scratch: scratch space used to compute the aggregate pubkey by - * multiexponentiation. If NULL, an inefficient algorithm is used. - * Out: agg_pk: the MuSig-aggregated xonly public key. If you do not need it, - * this arg can be NULL. - * keyagg_cache: if non-NULL, pointer to a musig_keyagg_cache struct that - * is required for signing (or verifying the MuSig protocol). - * In: pubkeys: input array of pointers to public keys to aggregate. The order - * is important; a different order will result in a different - * aggregate public key - * n_pubkeys: length of pubkeys array. Must be greater than 0. - */ -SECP256K1_API int secp256k1_musig_pubkey_agg( - const secp256k1_context* ctx, - secp256k1_scratch_space *scratch, - secp256k1_xonly_pubkey *agg_pk, - secp256k1_musig_keyagg_cache *keyagg_cache, - const secp256k1_xonly_pubkey * const* pubkeys, - size_t n_pubkeys -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); - -/** Tweak an x-only public key by adding the generator multiplied with tweak32 - * to it. The resulting output_pubkey with the original agg_pk output of - * musig_pubkey_agg and tweak passes `secp256k1_xonly_pubkey_tweak_test`. - * - * This function is only useful before initializing a signing session. If you - * are only computing a public key, but not intending to create a signature for - * it, you can just use `secp256k1_xonly_pubkey_tweak_add`. Can only be called - * once with a given keyagg_cache. - * - * Returns: 0 if the arguments are invalid or the resulting public key would be - * invalid (only when the tweak is the negation of the corresponding - * secret key) or if the key has already been tweaked. 1 otherwise. - * Args: ctx: pointer to a context object initialized for verification - * Out: output_pubkey: pointer to a public key to store the result. Will be set - * to an invalid value if this function returns 0. If you - * do not need it, this arg can be NULL. - * tweak32: pointer to a 32-byte tweak. If the tweak is invalid - * according to secp256k1_ec_seckey_verify, this function - * returns 0. For uniformly random 32-byte arrays the - * chance of being invalid is negligible (around 1 in - * 2^128). - * In/Out: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized in - * `musig_pubkey_agg` - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_tweak_add( - const secp256k1_context* ctx, - secp256k1_pubkey *output_pubkey, - const unsigned char *tweak32, - secp256k1_musig_keyagg_cache *keyagg_cache -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** Starts a signing session by generating a nonce - * - * This function outputs a secret nonce that will be required for signing and a - * corresponding public nonce that is intended to be sent to other signers. - * - * MuSig differs from regular Schnorr signing in that implementers _must_ take - * special care to not reuse a nonce. This can be ensured by following these rules: - * - * 1. Always provide a unique session_id32. It is a "number used once". - * 2. If you already know the signing key, message or aggregate public key - * cache, they can be optionally provided to derive the nonce and increase - * misuse-resistance. The extra_input32 argument can be used to provide - * additional data that does not repeat in normal scenarios, such as the - * current time. - * 3. If you do not provide a seckey, session_id32 _must_ be UNIFORMLY RANDOM. - * If you do provide a seckey, session_id32 can instead be a counter (that - * must never repeat!). However, it is recommended to always choose - * session_id32 uniformly at random. Note that using the same seckey for - * multiple MuSig sessions is fine. - * 4. Avoid copying (or serializing) the secnonce. This reduces the possibility - * that it is used more than once for signing. - * - * Remember that nonce reuse will immediately leak the secret key! - * - * Returns: 0 if the arguments are invalid and 1 otherwise - * Args: ctx: pointer to a context object, initialized for signing - * Out: secnonce: pointer to a structure to store the secret nonce - * pubnonce: pointer to a structure to store the public nonce - * In: session_id32: a 32-byte session_id32 as explained above. Must be - * uniformly random unless you really know what you are - * doing. - * seckey: the 32-byte secret key that will be used for signing if - * already known (can be NULL) - * msg32: the 32-byte message that will be signed if already known - * (can be NULL) - * keyagg_cache: pointer to the keyagg_cache that was used to create the aggregate - * (and tweaked) public key if already known (can be NULL) - * extra_input32: an optional 32-byte array that is input to the nonce - * derivation function (can be NULL) - */ -SECP256K1_API int secp256k1_musig_nonce_gen( - const secp256k1_context* ctx, - secp256k1_musig_secnonce *secnonce, - secp256k1_musig_pubnonce *pubnonce, - const unsigned char *session_id32, - const unsigned char *seckey, - const unsigned char *msg32, - const secp256k1_musig_keyagg_cache *keyagg_cache, - const unsigned char *extra_input32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); - -/** Aggregates the nonces of every signer into a single nonce - * - * This can be done by an untrusted third party to reduce the communication - * between signers. Instead of everyone sending nonces to everyone else, there - * can be one party receiving all nonces, aggregating the nonces with this - * function and then sending only the aggregate nonce back to the signers. - * - * Returns: 0 if the arguments are invalid or if all signers sent invalid - * pubnonces, 1 otherwise - * Args: ctx: pointer to a context object - * Out: aggnonce: pointer to an the aggregate public nonce object for - * musig_nonce_process - * In: pubnonces: array of pointers to public nonces sent by the - * signers - * n_pubnonces: number of elements in the pubnonces array. Must be - * greater than 0. - */ -SECP256K1_API int secp256k1_musig_nonce_agg( - const secp256k1_context* ctx, - secp256k1_musig_aggnonce *aggnonce, - const secp256k1_musig_pubnonce * const* pubnonces, - size_t n_pubnonces -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Takes the public nonces of all signers and computes a session cache that is - * required for signing and verification of partial signatures and a signature - * template that is required for combining partial signatures. - * - * If the adaptor argument is non-NULL then the output of musig_partial_sig_agg - * will be an invalid Schnorr signature, until the signature is given to - * musig_adapt with the corresponding secret adaptor. - * - * Returns: 0 if the arguments are invalid or if all signers sent invalid - * pubnonces, 1 otherwise - * Args: ctx: pointer to a context object, initialized for verification - * Out: session: pointer to a struct to store the session - * In: aggnonce: pointer to an the aggregate public nonce object that is - * output of musig_nonce_agg - * msg32: the 32-byte message to sign - * keyagg_cache: pointer to the keyagg_cache that was used to create the - * aggregate (and tweaked) pubkey - * adaptor: optional pointer to an adaptor point encoded as a public - * key if this signing session is part of an adaptor - * signature protocol - */ -SECP256K1_API int secp256k1_musig_nonce_process( - const secp256k1_context* ctx, - secp256k1_musig_session *session, - const secp256k1_musig_aggnonce *aggnonce, - const unsigned char *msg32, - const secp256k1_musig_keyagg_cache *keyagg_cache, - const secp256k1_pubkey *adaptor -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); - -/** Produces a partial signature - * - * This function sets the given secnonce to 0 and will abort if given a - * secnonce that is 0. This is a best effort attempt to protect against nonce - * reuse. However, this is of course easily defeated if the secnonce has been - * copied (or serialized). Remember that nonce reuse will immediately leak the - * secret key! - * - * Returns: 0 if the arguments are invalid or the provided secnonce has already - * been used for signing, 1 otherwise - * Args: ctx: pointer to a context object - * Out: partial_sig: pointer to struct to store the partial signature - * In/Out: secnonce: pointer to the secnonce struct created in - * musig_nonce_gen - * In: keypair: pointer to keypair to sign the message with - * keyagg_cache: pointer to the keyagg_cache that was output when the - * aggregate public key for this session - * session: pointer to the session that was created with - * musig_nonce_process - */ -SECP256K1_API int secp256k1_musig_partial_sign( - const secp256k1_context* ctx, - secp256k1_musig_partial_sig *partial_sig, - secp256k1_musig_secnonce *secnonce, - const secp256k1_keypair *keypair, - const secp256k1_musig_keyagg_cache *keyagg_cache, - const secp256k1_musig_session *session -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); - -/** Checks that an individual partial signature verifies - * - * This function is essential when using protocols with adaptor signatures. - * However, it is not essential for regular MuSig's, in the sense that if any - * partial signatures does not verify, the full signature will also not verify, so the - * problem will be caught. But this function allows determining the specific party - * who produced an invalid signature, so that signing can be restarted without them. - * - * Returns: 0 if the arguments are invalid or the partial signature does not - * verify - * Args ctx: pointer to a context object, initialized for verification - * In: partial_sig: pointer to partial signature to verify - * pubnonce: public nonce sent by the signer who produced the - * signature - * pubkey: public key of the signer who produced the signature - * keyagg_cache: pointer to the keyagg_cache that was output when the - * aggregate public key for this session - * session: pointer to the session that was created with - * musig_nonce_process - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verify( - const secp256k1_context* ctx, - const secp256k1_musig_partial_sig *partial_sig, - const secp256k1_musig_pubnonce *pubnonce, - const secp256k1_xonly_pubkey *pubkey, - const secp256k1_musig_keyagg_cache *keyagg_cache, - const secp256k1_musig_session *session -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); - -/** Aggregates partial signatures - * - * Returns: 0 if the arguments are invalid or a partial_sig is out of range, 1 - * otherwise (which does NOT mean the resulting signature verifies). - * Args: ctx: pointer to a context object - * Out: sig64: complete Schnorr signature - * In: session: pointer to the session that was created with - * musig_nonce_process - * partial_sigs: array of pointers to partial signatures to aggregate - * n_sigs: number of elements in the partial_sigs array - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_agg( - const secp256k1_context* ctx, - unsigned char *sig64, - const secp256k1_musig_session *session, - const secp256k1_musig_partial_sig * const* partial_sigs, - size_t n_sigs -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** Extracts the nonce_parity bit from a session - * - * This is used for adaptor signatures. - * - * Returns: 0 if one of the arguments was NULL, and 1 otherwise. - * Args: ctx: pointer to a context object - * Out: nonce_parity: pointer to an integer that indicates the parity - * of the aggregate public nonce. Used for adaptor - * signatures. - * In: session: pointer to the session that was created with - * musig_nonce_process - */ -int secp256k1_musig_nonce_parity( - const secp256k1_context* ctx, - int *nonce_parity, - secp256k1_musig_session *session -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Converts a pre-signature that misses the adaptor into a full signature - * - * If the sec_adaptor32 argument is incorrect, the adapted signature will be - * invalid. This function does not verify the adapted signature. - * - * Returns: 1: signature and secret adaptor contained valid values (which does - * NOT mean the signature or the adaptor are valid!) - * 0: otherwise - * Args: ctx: pointer to a context object - * In/Out: sig64: 64-byte pre-signature that is adapted to a full signature - * In: sec_adaptor32: 32-byte secret adaptor to add to the partial signature - * nonce_parity: the output of `musig_nonce_parity` called with the - * session used for producing sig64 - */ -SECP256K1_API int secp256k1_musig_adapt( - const secp256k1_context* ctx, - unsigned char *sig64, - const unsigned char *sec_adaptor32, - int nonce_parity -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Extracts a secret adaptor from a MuSig pre-signature and corresponding - * signature - * - * This function will not fail unless given grossly invalid data; if it is - * merely given signatures that do not verify, the returned value will be - * nonsense. It is therefore important that all data be verified at earlier - * steps of any protocol that uses this function. In particular, this includes - * verifying all partial signatures that were aggregated into pre_sig64. - * - * Returns: 1: signatures contained valid data such that an adaptor could be - * extracted (which does NOT mean the signatures or the adaptor are - * valid!) - * 0: otherwise - * Args: ctx: pointer to a context object - * Out:sec_adaptor32: 32-byte secret adaptor - * In: sig64: complete, valid 64-byte signature - * pre_sig64: the pre-signature corresponding to sig64, i.e., the - * aggregate of partial signatures without the secret - * adaptor - * nonce_parity: the output of `musig_nonce_parity` called with the - * session used for producing sig64 - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_adaptor( - const secp256k1_context* ctx, - unsigned char *sec_adaptor32, - const unsigned char *sig64, - const unsigned char *pre_sig64, - int nonce_parity -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libwallet/musig/secp256k1_preallocated.h b/libwallet/musig/secp256k1_preallocated.h deleted file mode 100644 index a9ae15d5..00000000 --- a/libwallet/musig/secp256k1_preallocated.h +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef SECP256K1_PREALLOCATED_H -#define SECP256K1_PREALLOCATED_H - -#include "secp256k1.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* The module provided by this header file is intended for settings in which it - * is not possible or desirable to rely on dynamic memory allocation. It provides - * functions for creating, cloning, and destroying secp256k1 context objects in a - * contiguous fixed-size block of memory provided by the caller. - * - * Context objects created by functions in this module can be used like contexts - * objects created by functions in secp256k1.h, i.e., they can be passed to any - * API function that expects a context object (see secp256k1.h for details). The - * only exception is that context objects created by functions in this module - * must be destroyed using secp256k1_context_preallocated_destroy (in this - * module) instead of secp256k1_context_destroy (in secp256k1.h). - * - * It is guaranteed that functions in this module will not call malloc or its - * friends realloc, calloc, and free. - */ - -/** Determine the memory size of a secp256k1 context object to be created in - * caller-provided memory. - * - * The purpose of this function is to determine how much memory must be provided - * to secp256k1_context_preallocated_create. - * - * Returns: the required size of the caller-provided memory block - * In: flags: which parts of the context to initialize. - */ -SECP256K1_API size_t secp256k1_context_preallocated_size( - unsigned int flags -) SECP256K1_WARN_UNUSED_RESULT; - -/** Create a secp256k1 context object in caller-provided memory. - * - * The caller must provide a pointer to a rewritable contiguous block of memory - * of size at least secp256k1_context_preallocated_size(flags) bytes, suitably - * aligned to hold an object of any type. - * - * The block of memory is exclusively owned by the created context object during - * the lifetime of this context object, which begins with the call to this - * function and ends when a call to secp256k1_context_preallocated_destroy - * (which destroys the context object again) returns. During the lifetime of the - * context object, the caller is obligated not to access this block of memory, - * i.e., the caller may not read or write the memory, e.g., by copying the memory - * contents to a different location or trying to create a second context object - * in the memory. In simpler words, the prealloc pointer (or any pointer derived - * from it) should not be used during the lifetime of the context object. - * - * Returns: a newly created context object. - * In: prealloc: a pointer to a rewritable contiguous block of memory of - * size at least secp256k1_context_preallocated_size(flags) - * bytes, as detailed above (cannot be NULL) - * flags: which parts of the context to initialize. - * - * See also secp256k1_context_randomize (in secp256k1.h) - * and secp256k1_context_preallocated_destroy. - */ -SECP256K1_API secp256k1_context* secp256k1_context_preallocated_create( - void* prealloc, - unsigned int flags -) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; - -/** Determine the memory size of a secp256k1 context object to be copied into - * caller-provided memory. - * - * Returns: the required size of the caller-provided memory block. - * In: ctx: an existing context to copy (cannot be NULL) - */ -SECP256K1_API size_t secp256k1_context_preallocated_clone_size( - const secp256k1_context* ctx -) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; - -/** Copy a secp256k1 context object into caller-provided memory. - * - * The caller must provide a pointer to a rewritable contiguous block of memory - * of size at least secp256k1_context_preallocated_size(flags) bytes, suitably - * aligned to hold an object of any type. - * - * The block of memory is exclusively owned by the created context object during - * the lifetime of this context object, see the description of - * secp256k1_context_preallocated_create for details. - * - * Returns: a newly created context object. - * Args: ctx: an existing context to copy (cannot be NULL) - * In: prealloc: a pointer to a rewritable contiguous block of memory of - * size at least secp256k1_context_preallocated_size(flags) - * bytes, as detailed above (cannot be NULL) - */ -SECP256K1_API secp256k1_context* secp256k1_context_preallocated_clone( - const secp256k1_context* ctx, - void* prealloc -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_WARN_UNUSED_RESULT; - -/** Destroy a secp256k1 context object that has been created in - * caller-provided memory. - * - * The context pointer may not be used afterwards. - * - * The context to destroy must have been created using - * secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone. - * If the context has instead been created using secp256k1_context_create or - * secp256k1_context_clone, the behaviour is undefined. In that case, - * secp256k1_context_destroy must be used instead. - * - * If required, it is the responsibility of the caller to deallocate the block - * of memory properly after this function returns, e.g., by calling free on the - * preallocated pointer given to secp256k1_context_preallocated_create or - * secp256k1_context_preallocated_clone. - * - * Args: ctx: an existing context to destroy, constructed using - * secp256k1_context_preallocated_create or - * secp256k1_context_preallocated_clone (cannot be NULL) - */ -SECP256K1_API void secp256k1_context_preallocated_destroy( - secp256k1_context* ctx -); - -#ifdef __cplusplus -} -#endif - -#endif /* SECP256K1_PREALLOCATED_H */ diff --git a/libwallet/musig/secp256k1_schnorrsig.h b/libwallet/musig/secp256k1_schnorrsig.h deleted file mode 100644 index 74cbcac4..00000000 --- a/libwallet/musig/secp256k1_schnorrsig.h +++ /dev/null @@ -1,170 +0,0 @@ -#ifndef SECP256K1_SCHNORRSIG_H -#define SECP256K1_SCHNORRSIG_H - -#include "secp256k1.h" -#include "secp256k1_extrakeys.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** This module implements a variant of Schnorr signatures compliant with - * Bitcoin Improvement Proposal 340 "Schnorr Signatures for secp256k1" - * (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). - */ - -/** A pointer to a function to deterministically generate a nonce. - * - * Same as secp256k1_nonce function with the exception of accepting an - * additional pubkey argument and not requiring an attempt argument. The pubkey - * argument can protect signature schemes with key-prefixed challenge hash - * inputs against reusing the nonce when signing with the wrong precomputed - * pubkey. - * - * Returns: 1 if a nonce was successfully generated. 0 will cause signing to - * return an error. - * Out: nonce32: pointer to a 32-byte array to be filled by the function - * In: msg: the message being verified. Is NULL if and only if msglen - * is 0. - * msglen: the length of the message - * key32: pointer to a 32-byte secret key (will not be NULL) - * xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32 - * (will not be NULL) - * algo: pointer to an array describing the signature - * algorithm (will not be NULL) - * algolen: the length of the algo array - * data: arbitrary data pointer that is passed through - * - * Except for test cases, this function should compute some cryptographic hash of - * the message, the key, the pubkey, the algorithm description, and data. - */ -typedef int (*secp256k1_nonce_function_hardened)( - unsigned char *nonce32, - const unsigned char *msg, - size_t msglen, - const unsigned char *key32, - const unsigned char *xonly_pk32, - const unsigned char *algo, - size_t algolen, - void *data -); - -/** An implementation of the nonce generation function as defined in Bitcoin - * Improvement Proposal 340 "Schnorr Signatures for secp256k1" - * (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). - * - * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of - * auxiliary random data as defined in BIP-340. If the data pointer is NULL, - * the nonce derivation procedure follows BIP-340 by setting the auxiliary - * random data to zero. The algo argument must be non-NULL, otherwise the - * function will fail and return 0. The hash will be tagged with algo. - * Therefore, to create BIP-340 compliant signatures, algo must be set to - * "BIP0340/nonce" and algolen to 13. - */ -SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340; - -/** Data structure that contains additional arguments for schnorrsig_sign_custom. - * - * A schnorrsig_extraparams structure object can be initialized correctly by - * setting it to SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT. - * - * Members: - * magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization - * and has no other function than making sure the object is - * initialized. - * noncefp: pointer to a nonce generation function. If NULL, - * secp256k1_nonce_function_bip340 is used - * ndata: pointer to arbitrary data used by the nonce generation function - * (can be NULL). If it is non-NULL and - * secp256k1_nonce_function_bip340 is used, then ndata must be a - * pointer to 32-byte auxiliary randomness as per BIP-340. - */ -typedef struct { - unsigned char magic[4]; - secp256k1_nonce_function_hardened noncefp; - void* ndata; -} secp256k1_schnorrsig_extraparams; - -#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC { 0xda, 0x6f, 0xb3, 0x8c } -#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT {\ - SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC,\ - NULL,\ - NULL\ -} - -/** Create a Schnorr signature. - * - * Does _not_ strictly follow BIP-340 because it does not verify the resulting - * signature. Instead, you can manually use secp256k1_schnorrsig_verify and - * abort if it fails. - * - * This function only signs 32-byte messages. If you have messages of a - * different size (or the same size but without a context-specific tag - * prefix), it is recommended to create a 32-byte message hash with - * secp256k1_tagged_sha256 and then sign the hash. Tagged hashing allows - * providing an context-specific tag for domain separation. This prevents - * signatures from being valid in multiple contexts by accident. - * - * Returns 1 on success, 0 on failure. - * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * Out: sig64: pointer to a 64-byte array to store the serialized signature (cannot be NULL) - * In: msg32: the 32-byte message being signed (cannot be NULL) - * keypair: pointer to an initialized keypair (cannot be NULL) - * aux_rand32: 32 bytes of fresh randomness. While recommended to provide - * this, it is only supplemental to security and can be NULL. See - * BIP-340 "Default Signing" for a full explanation of this - * argument and for guidance if randomness is expensive. - */ -SECP256K1_API int secp256k1_schnorrsig_sign( - const secp256k1_context* ctx, - unsigned char *sig64, - const unsigned char *msg32, - const secp256k1_keypair *keypair, - unsigned char *aux_rand32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** Create a Schnorr signature with a more flexible API. - * - * Same arguments as secp256k1_schnorrsig_sign except that it allows signing - * variable length messages and accepts a pointer to an extraparams object that - * allows customizing signing by passing additional arguments. - * - * Creates the same signatures as schnorrsig_sign if msglen is 32 and the - * extraparams.ndata is the same as aux_rand32. - * - * In: msg: the message being signed. Can only be NULL if msglen is 0. - * msglen: length of the message - * extraparams: pointer to a extraparams object (can be NULL) - */ -SECP256K1_API int secp256k1_schnorrsig_sign_custom( - const secp256k1_context* ctx, - unsigned char *sig64, - const unsigned char *msg, - size_t msglen, - const secp256k1_keypair *keypair, - secp256k1_schnorrsig_extraparams *extraparams -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5); - -/** Verify a Schnorr signature. - * - * Returns: 1: correct signature - * 0: incorrect signature - * Args: ctx: a secp256k1 context object, initialized for verification. - * In: sig64: pointer to the 64-byte signature to verify (cannot be NULL) - * msg: the message being verified. Can only be NULL if msglen is 0. - * msglen: length of the message - * pubkey: pointer to an x-only public key to verify with (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( - const secp256k1_context* ctx, - const unsigned char *sig64, - const unsigned char *msg, - size_t msglen, - const secp256k1_xonly_pubkey *pubkey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5); - -#ifdef __cplusplus -} -#endif - -#endif /* SECP256K1_SCHNORRSIG_H */ diff --git a/libwallet/musig/selftest.h b/libwallet/musig/selftest.h deleted file mode 100644 index 52f1b844..00000000 --- a/libwallet/musig/selftest.h +++ /dev/null @@ -1,32 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2020 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_SELFTEST_H -#define SECP256K1_SELFTEST_H - -#include "hash.h" - -#include - -static int secp256k1_selftest_sha256(void) { - static const char *input63 = "For this sample, this 63-byte string will be used as input data"; - static const unsigned char output32[32] = { - 0xf0, 0x8a, 0x78, 0xcb, 0xba, 0xee, 0x08, 0x2b, 0x05, 0x2a, 0xe0, 0x70, 0x8f, 0x32, 0xfa, 0x1e, - 0x50, 0xc5, 0xc4, 0x21, 0xaa, 0x77, 0x2b, 0xa5, 0xdb, 0xb4, 0x06, 0xa2, 0xea, 0x6b, 0xe3, 0x42, - }; - unsigned char out[32]; - secp256k1_sha256 hasher; - secp256k1_sha256_initialize(&hasher); - secp256k1_sha256_write(&hasher, (const unsigned char*)input63, 63); - secp256k1_sha256_finalize(&hasher, out); - return secp256k1_memcmp_var(out, output32, 32) == 0; -} - -static int secp256k1_selftest(void) { - return secp256k1_selftest_sha256(); -} - -#endif /* SECP256K1_SELFTEST_H */ diff --git a/libwallet/musig/session.h b/libwallet/musig/session.h deleted file mode 100644 index 78d4d0cb..00000000 --- a/libwallet/musig/session.h +++ /dev/null @@ -1,20 +0,0 @@ -/********************************************************************** - * Copyright (c) 2021 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef _SECP256K1_MODULE_MUSIG_SESSION_ -#define _SECP256K1_MODULE_MUSIG_SESSION_ - -typedef struct { - int fin_nonce_parity; - const unsigned char *fin_nonce; - secp256k1_scalar noncecoef; - secp256k1_scalar challenge; - secp256k1_scalar s_part; -} secp256k1_musig_session_internal; - -static int secp256k1_musig_session_load(const secp256k1_context* ctx, secp256k1_musig_session_internal *session_i, const secp256k1_musig_session *session); - -#endif diff --git a/libwallet/musig/session_impl.h b/libwallet/musig/session_impl.h deleted file mode 100644 index fdea5aed..00000000 --- a/libwallet/musig/session_impl.h +++ /dev/null @@ -1,620 +0,0 @@ -/********************************************************************** - * Copyright (c) 2021 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef _SECP256K1_MODULE_MUSIG_SESSION_IMPL_ -#define _SECP256K1_MODULE_MUSIG_SESSION_IMPL_ - -#include "keyagg.h" -#include "session.h" - -static const unsigned char secp256k1_musig_secnonce_magic[4] = { 0x22, 0x0e, 0xdc, 0xf1 }; - -static void secp256k1_musig_secnonce_save(secp256k1_musig_secnonce *secnonce, secp256k1_scalar *k) { - memcpy(&secnonce->data[0], secp256k1_musig_secnonce_magic, 4); - secp256k1_scalar_get_b32(&secnonce->data[4], &k[0]); - secp256k1_scalar_get_b32(&secnonce->data[36], &k[1]); -} - -static int secp256k1_musig_secnonce_load(const secp256k1_context* ctx, secp256k1_scalar *k, secp256k1_musig_secnonce *secnonce) { - ARG_CHECK(secp256k1_memcmp_var(&secnonce->data[0], secp256k1_musig_secnonce_magic, 4) == 0); - secp256k1_scalar_set_b32(&k[0], &secnonce->data[4], NULL); - secp256k1_scalar_set_b32(&k[1], &secnonce->data[36], NULL); - return 1; -} - -static const unsigned char secp256k1_musig_pubnonce_magic[4] = { 0xf5, 0x7a, 0x3d, 0xa0 }; - -/* Requires that none of the provided group elements is infinity. Works for both - * musig_pubnonce and musig_aggnonce. */ -static void secp256k1_musig_pubnonce_save(secp256k1_musig_pubnonce* nonce, secp256k1_ge* ge) { - int i; - memcpy(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4); - for (i = 0; i < 2; i++) { - secp256k1_point_save(nonce->data + 4+64*i, &ge[i]); - } -} - -/* Works for both musig_pubnonce and musig_aggnonce. Returns 1 unless the nonce - * wasn't properly initialized */ -static int secp256k1_musig_pubnonce_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_musig_pubnonce* nonce) { - int i; - - ARG_CHECK(secp256k1_memcmp_var(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4) == 0); - for (i = 0; i < 2; i++) { - secp256k1_point_load(&ge[i], nonce->data + 4 + 64*i); - } - return 1; -} - -static const unsigned char secp256k1_musig_session_cache_magic[4] = { 0x9d, 0xed, 0xe9, 0x17 }; - -/* A session consists of - * - 4 byte session cache - * - 1 byte the parity of the aggregate nonce - * - 32 byte aggregated nonce - * - 32 byte nonce aggregation coefficient b - * - 32 byte signature challenge hash e - * - 32 byte scalar s that is added to the partial signatures of the signers - */ -static void secp256k1_musig_session_save(secp256k1_musig_session *session, const secp256k1_musig_session_internal *session_i) { - unsigned char *ptr = session->data; - - memcpy(ptr, secp256k1_musig_session_cache_magic, 4); - ptr += 4; - *ptr = session_i->fin_nonce_parity; - ptr += 1; - memmove(ptr, session_i->fin_nonce, 32); - ptr += 32; - secp256k1_scalar_get_b32(ptr, &session_i->noncecoef); - ptr += 32; - secp256k1_scalar_get_b32(ptr, &session_i->challenge); - ptr += 32; - secp256k1_scalar_get_b32(ptr, &session_i->s_part); -} - -static int secp256k1_musig_session_load(const secp256k1_context* ctx, secp256k1_musig_session_internal *session_i, const secp256k1_musig_session *session) { - const unsigned char *ptr = session->data; - - ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_musig_session_cache_magic, 4) == 0); - ptr += 4; - session_i->fin_nonce_parity = *ptr; - ptr += 1; - session_i->fin_nonce = ptr; - ptr += 32; - secp256k1_scalar_set_b32(&session_i->noncecoef, ptr, NULL); - ptr += 32; - secp256k1_scalar_set_b32(&session_i->challenge, ptr, NULL); - ptr += 32; - secp256k1_scalar_set_b32(&session_i->s_part, ptr, NULL); - return 1; -} - -static const unsigned char secp256k1_musig_partial_sig_magic[4] = { 0xeb, 0xfb, 0x1a, 0x32 }; - -static void secp256k1_musig_partial_sig_save(secp256k1_musig_partial_sig* sig, secp256k1_scalar *s) { - memcpy(&sig->data[0], secp256k1_musig_partial_sig_magic, 4); - secp256k1_scalar_get_b32(&sig->data[4], s); -} - -static int secp256k1_musig_partial_sig_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_musig_partial_sig* sig) { - int overflow; - - ARG_CHECK(secp256k1_memcmp_var(&sig->data[0], secp256k1_musig_partial_sig_magic, 4) == 0); - secp256k1_scalar_set_b32(s, &sig->data[4], &overflow); - /* Parsed signatures can not overflow */ - VERIFY_CHECK(!overflow); - return 1; -} - -int secp256k1_musig_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_musig_pubnonce* nonce) { - secp256k1_ge ge[2]; - int i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(out66 != NULL); - memset(out66, 0, 66); - ARG_CHECK(nonce != NULL); - - if (!secp256k1_musig_pubnonce_load(ctx, ge, nonce)) { - return 0; - } - for (i = 0; i < 2; i++) { - int ret; - size_t size = 33; - ret = secp256k1_eckey_pubkey_serialize(&ge[i], &out66[33*i], &size, 1); - /* serialize must succeed because the point was just loaded */ - VERIFY_CHECK(ret); - } - return 1; -} - -int secp256k1_musig_pubnonce_parse(const secp256k1_context* ctx, secp256k1_musig_pubnonce* nonce, const unsigned char *in66) { - secp256k1_ge ge[2]; - int i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(nonce != NULL); - ARG_CHECK(in66 != NULL); - - for (i = 0; i < 2; i++) { - if (!secp256k1_eckey_pubkey_parse(&ge[i], &in66[33*i], 33)) { - return 0; - } - if (!secp256k1_ge_is_in_correct_subgroup(&ge[i])) { - return 0; - } - } - /* The group elements can not be infinity because they were just parsed */ - secp256k1_musig_pubnonce_save(nonce, ge); - secp256k1_ge_clear(&ge[0]); - secp256k1_ge_clear(&ge[1]); - return 1; -} - -int secp256k1_musig_aggnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_musig_aggnonce* nonce) { - return secp256k1_musig_pubnonce_serialize(ctx, out66, (secp256k1_musig_pubnonce*) nonce); -} - -int secp256k1_musig_aggnonce_parse(const secp256k1_context* ctx, secp256k1_musig_aggnonce* nonce, const unsigned char *in66) { - return secp256k1_musig_pubnonce_parse(ctx, (secp256k1_musig_pubnonce*) nonce, in66); -} - -int secp256k1_musig_partial_sig_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_sig* sig) { - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(out32 != NULL); - ARG_CHECK(sig != NULL); - memcpy(out32, &sig->data[4], 32); - return 1; -} - -int secp256k1_musig_partial_sig_parse(const secp256k1_context* ctx, secp256k1_musig_partial_sig* sig, const unsigned char *in32) { - secp256k1_scalar tmp; - int overflow; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig != NULL); - ARG_CHECK(in32 != NULL); - - secp256k1_scalar_set_b32(&tmp, in32, &overflow); - if (overflow) { - secp256k1_scalar_clear(&tmp); - return 0; - } - secp256k1_musig_partial_sig_save(sig, &tmp); - secp256k1_scalar_clear(&tmp); - return 1; -} - -/* Normalizes the x-coordinate of the given group element. */ -static int secp256k1_xonly_ge_serialize(unsigned char *output32, secp256k1_ge *ge) { - if (secp256k1_ge_is_infinity(ge)) { - return 0; - } - secp256k1_fe_normalize_var(&ge->x); - secp256k1_fe_get_b32(output32, &ge->x); - return 1; -} - -static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned char *session_id, const unsigned char *key32, const unsigned char *msg32, const unsigned char *agg_pk, const unsigned char *extra_input32) { - secp256k1_sha256 sha; - unsigned char seed[32]; - unsigned char i; - enum { n_extra_in = 4 }; - const unsigned char *extra_in[n_extra_in]; - - /* TODO: this doesn't have the same sidechannel resistance as the BIP340 - * nonce function because the seckey feeds directly into SHA. */ - secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/nonce", 11); - secp256k1_sha256_write(&sha, session_id, 32); - extra_in[0] = key32; - extra_in[1] = agg_pk; - extra_in[2] = msg32; - extra_in[3] = extra_input32; - for (i = 0; i < n_extra_in; i++) { - unsigned char marker; - if (extra_in[i] != NULL) { - marker = 1; - secp256k1_sha256_write(&sha, &marker, 1); - secp256k1_sha256_write(&sha, extra_in[i], 32); - } else { - marker = 0; - secp256k1_sha256_write(&sha, &marker, 1); - } - } - secp256k1_sha256_finalize(&sha, seed); - - for (i = 0; i < 2; i++) { - unsigned char buf[32]; - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, seed, 32); - secp256k1_sha256_write(&sha, &i, 1); - secp256k1_sha256_finalize(&sha, buf); - secp256k1_scalar_set_b32(&k[i], buf, NULL); - } -} - -int secp256k1_musig_nonce_gen(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, secp256k1_musig_pubnonce *pubnonce, const unsigned char *session_id32, const unsigned char *seckey, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *extra_input32) { - secp256k1_keyagg_cache_internal cache_i; - secp256k1_scalar k[2]; - secp256k1_ge nonce_pt[2]; - int i; - unsigned char pk_ser[32]; - unsigned char *pk_ser_ptr = NULL; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secnonce != NULL); - memset(secnonce, 0, sizeof(*secnonce)); - ARG_CHECK(pubnonce != NULL); - memset(pubnonce, 0, sizeof(*pubnonce)); - ARG_CHECK(session_id32 != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - - /* Check that the seckey is valid to be able to sign for it later. */ - if (seckey != NULL) { - secp256k1_scalar sk; - int ret; - ret = secp256k1_scalar_set_b32_seckey(&sk, seckey); - /* The declassified return value indicates the validity of the seckey. - * If this function is called correctly it is always 1. (Note: - * declassify was only required for valgrind_ctime_test build with - * USE_ASM_X86_64=no. */ - secp256k1_declassify(ctx, &ret, sizeof(ret)); - ARG_CHECK(ret); - secp256k1_scalar_clear(&sk); - } - - if (keyagg_cache != NULL) { - int ret; - if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { - return 0; - } - ret = secp256k1_xonly_ge_serialize(pk_ser, &cache_i.pk); - /* Serialization can not fail because the loaded point can not be infinity. */ - VERIFY_CHECK(ret); - pk_ser_ptr = pk_ser; - } - secp256k1_nonce_function_musig(k, session_id32, seckey, msg32, pk_ser_ptr, extra_input32); - VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[0])); - VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[1])); - secp256k1_musig_secnonce_save(secnonce, k); - - for (i = 0; i < 2; i++) { - secp256k1_gej nonce_ptj; - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_ptj, &k[i]); - secp256k1_ge_set_gej(&nonce_pt[i], &nonce_ptj); - secp256k1_declassify(ctx, &nonce_pt[i], sizeof(nonce_pt)); - secp256k1_scalar_clear(&k[i]); - } - /* nonce_pt can't be infinity because k != 0 */ - secp256k1_musig_pubnonce_save(pubnonce, nonce_pt); - return 1; -} - -static int secp256k1_musig_sum_nonces(const secp256k1_context* ctx, secp256k1_gej *summed_nonces, const secp256k1_musig_pubnonce * const* pubnonces, size_t n_pubnonces) { - size_t i; - int j; - - secp256k1_gej_set_infinity(&summed_nonces[0]); - secp256k1_gej_set_infinity(&summed_nonces[1]); - - for (i = 0; i < n_pubnonces; i++) { - secp256k1_ge nonce_pt[2]; - if (!secp256k1_musig_pubnonce_load(ctx, nonce_pt, pubnonces[i])) { - return 0; - } - for (j = 0; j < 2; j++) { - secp256k1_gej_add_ge_var(&summed_nonces[j], &summed_nonces[j], &nonce_pt[j], NULL); - } - } - return 1; -} - -int secp256k1_musig_nonce_agg(const secp256k1_context* ctx, secp256k1_musig_aggnonce *aggnonce, const secp256k1_musig_pubnonce * const* pubnonces, size_t n_pubnonces) { - secp256k1_gej aggnonce_ptj[2]; - secp256k1_ge aggnonce_pt[2]; - int i; - - ARG_CHECK(aggnonce != NULL); - ARG_CHECK(pubnonces != NULL); - ARG_CHECK(n_pubnonces > 0); - - if (!secp256k1_musig_sum_nonces(ctx, aggnonce_ptj, pubnonces, n_pubnonces)) { - return 0; - } - for (i = 0; i < 2; i++) { - if (secp256k1_gej_is_infinity(&aggnonce_ptj[i])) { - return 0; - } - secp256k1_ge_set_gej(&aggnonce_pt[i], &aggnonce_ptj[i]); - } - secp256k1_musig_pubnonce_save((secp256k1_musig_pubnonce*)aggnonce, aggnonce_pt); - return 1; -} - -/* hash(aggnonce[0], aggnonce[1], agg_pk, msg) */ -static int secp256k1_musig_compute_noncehash(unsigned char *noncehash, secp256k1_ge *aggnonce, const unsigned char *agg_pk32, const unsigned char *msg) { - unsigned char buf[33]; - secp256k1_sha256 sha; - int i; - - secp256k1_sha256_initialize(&sha); - for (i = 0; i < 2; i++) { - size_t size = sizeof(buf); - if (!secp256k1_eckey_pubkey_serialize(&aggnonce[i], buf, &size, 1)) { - return 0; - } - secp256k1_sha256_write(&sha, buf, sizeof(buf)); - } - secp256k1_sha256_write(&sha, agg_pk32, 32); - secp256k1_sha256_write(&sha, msg, 32); - secp256k1_sha256_finalize(&sha, noncehash); - return 1; -} - -static int secp256k1_musig_nonce_process_internal(const secp256k1_ecmult_context* ecmult_ctx, int *fin_nonce_parity, unsigned char *fin_nonce, secp256k1_scalar *b, secp256k1_gej *aggnoncej, const unsigned char *agg_pk32, const unsigned char *msg) { - unsigned char noncehash[32]; - secp256k1_ge fin_nonce_pt; - secp256k1_gej fin_nonce_ptj; - secp256k1_ge aggnonce[2]; - - secp256k1_ge_set_gej(&aggnonce[0], &aggnoncej[0]); - secp256k1_ge_set_gej(&aggnonce[1], &aggnoncej[1]); - if (!secp256k1_musig_compute_noncehash(noncehash, aggnonce, agg_pk32, msg)) { - return 0; - } - /* aggnonce = aggnonces[0] + b*aggnonces[1] */ - secp256k1_scalar_set_b32(b, noncehash, NULL); - secp256k1_ecmult(ecmult_ctx, &fin_nonce_ptj, &aggnoncej[1], b, NULL); - secp256k1_gej_add_ge(&fin_nonce_ptj, &fin_nonce_ptj, &aggnonce[0]); - secp256k1_ge_set_gej(&fin_nonce_pt, &fin_nonce_ptj); - if (!secp256k1_xonly_ge_serialize(fin_nonce, &fin_nonce_pt)) { - /* unreachable with overwhelming probability */ - return 0; - } - secp256k1_fe_normalize_var(&fin_nonce_pt.y); - *fin_nonce_parity = secp256k1_fe_is_odd(&fin_nonce_pt.y); - return 1; -} - -int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_pubkey *adaptor) { - secp256k1_keyagg_cache_internal cache_i; - secp256k1_ge aggnonce_pt[2]; - secp256k1_gej aggnonce_ptj[2]; - unsigned char fin_nonce[32]; - secp256k1_musig_session_internal session_i; - unsigned char agg_pk32[32]; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(session != NULL); - ARG_CHECK(aggnonce != NULL); - ARG_CHECK(msg32 != NULL); - ARG_CHECK(keyagg_cache != NULL); - - if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { - return 0; - } - secp256k1_fe_get_b32(agg_pk32, &cache_i.pk.x); - - if (!secp256k1_musig_pubnonce_load(ctx, aggnonce_pt, (secp256k1_musig_pubnonce*)aggnonce)) { - return 0; - } - secp256k1_gej_set_ge(&aggnonce_ptj[0], &aggnonce_pt[0]); - secp256k1_gej_set_ge(&aggnonce_ptj[1], &aggnonce_pt[1]); - /* Add public adaptor to nonce */ - if (adaptor != NULL) { - secp256k1_ge adaptorp; - if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) { - return 0; - } - secp256k1_gej_add_ge_var(&aggnonce_ptj[0], &aggnonce_ptj[0], &adaptorp, NULL); - } - if (!secp256k1_musig_nonce_process_internal(&ctx->ecmult_ctx, &session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_ptj, agg_pk32, msg32)) { - return 0; - } - - /* Compute messagehash and store in session cache */ - secp256k1_schnorrsig_challenge(&session_i.challenge, fin_nonce, msg32, 32, agg_pk32); - - /* If there is a tweak then set `msghash` times `tweak` to the `s`-part.*/ - secp256k1_scalar_clear(&session_i.s_part); - if (cache_i.is_tweaked) { - secp256k1_scalar e_tmp = session_i.challenge; - if (!secp256k1_eckey_privkey_tweak_mul(&e_tmp, &cache_i.tweak)) { - /* This mimics the behavior of secp256k1_ec_seckey_tweak_mul regarding - * tweak being 0. */ - return 0; - } - if (secp256k1_fe_is_odd(&cache_i.pk.y)) { - secp256k1_scalar_negate(&e_tmp, &e_tmp); - } - secp256k1_scalar_add(&session_i.s_part, &session_i.s_part, &e_tmp); - } - session_i.fin_nonce = fin_nonce; - secp256k1_musig_session_save(session, &session_i); - return 1; -} - -int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_partial_sig *partial_sig, secp256k1_musig_secnonce *secnonce, const secp256k1_keypair *keypair, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_musig_session *session) { - secp256k1_scalar sk; - secp256k1_ge pk; - secp256k1_scalar k[2]; - secp256k1_scalar mu, s; - secp256k1_keyagg_cache_internal cache_i; - secp256k1_musig_session_internal session_i; - int ret; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(partial_sig != NULL); - ARG_CHECK(secnonce != NULL); - ARG_CHECK(keypair != NULL); - ARG_CHECK(keyagg_cache != NULL); - ARG_CHECK(session != NULL); - - /* Fails if the magic doesn't match */ - ret = secp256k1_musig_secnonce_load(ctx, k, secnonce); - /* Set nonce to zero to avoid nonce reuse. This will cause subsequent calls - * of this function to fail */ - memset(secnonce, 0, sizeof(*secnonce)); - if (!ret) { - return 0; - } - - /* Obtain the signer's public key point and determine if the sk is - * negated before signing. That happens if if the signer's pubkey has an odd - * Y coordinate XOR the MuSig-aggregate pubkey has an odd Y coordinate XOR - * (if tweaked) the internal key has an odd Y coordinate. - * - * This can be seen by looking at the sk key belonging to `agg_pk`. - * Let's define - * P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key - * point x_i*G, mu_i is the i-th KeyAgg coefficient and |.| is a function - * that normalizes a point to an even Y by negating if necessary similar to - * secp256k1_extrakeys_ge_even_y. Then we have - * P := |P'| + t*G where t is the tweak. - * And the aggregate xonly public key is - * |P| = x*G - * where x = sum_i(b_i*mu_i*x_i) + b'*t - * b' = -1 if P != |P|, 1 otherwise - * b_i = -1 if (P_i != |P_i| XOR P' != |P'| XOR P != |P|) and 1 - * otherwise. - */ - if (!secp256k1_keypair_load(ctx, &sk, &pk, keypair)) { - return 0; - } - if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { - return 0; - } - secp256k1_fe_normalize_var(&pk.y); - if((secp256k1_fe_is_odd(&pk.y) - + secp256k1_fe_is_odd(&cache_i.pk.y) - + (cache_i.is_tweaked - && cache_i.internal_key_parity)) - % 2 == 1) { - secp256k1_scalar_negate(&sk, &sk); - } - - /* Multiply KeyAgg coefficient */ - secp256k1_fe_normalize_var(&pk.x); - secp256k1_musig_keyaggcoef(&mu, &cache_i, &pk.x); - secp256k1_scalar_mul(&sk, &sk, &mu); - - if (!secp256k1_musig_session_load(ctx, &session_i, session)) { - return 0; - } - if (session_i.fin_nonce_parity) { - secp256k1_scalar_negate(&k[0], &k[0]); - secp256k1_scalar_negate(&k[1], &k[1]); - } - - /* Sign */ - secp256k1_scalar_mul(&s, &session_i.challenge, &sk); - secp256k1_scalar_mul(&k[1], &session_i.noncecoef, &k[1]); - secp256k1_scalar_add(&k[0], &k[0], &k[1]); - secp256k1_scalar_add(&s, &s, &k[0]); - secp256k1_musig_partial_sig_save(partial_sig, &s); - secp256k1_scalar_clear(&sk); - secp256k1_scalar_clear(&k[0]); - secp256k1_scalar_clear(&k[1]); - return 1; -} - -int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_partial_sig *partial_sig, const secp256k1_musig_pubnonce *pubnonce, const secp256k1_xonly_pubkey *pubkey, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_musig_session *session) { - secp256k1_keyagg_cache_internal cache_i; - secp256k1_musig_session_internal session_i; - secp256k1_scalar mu, e, s; - secp256k1_gej pkj; - secp256k1_ge nonce_pt[2]; - secp256k1_gej rj; - secp256k1_gej tmp; - secp256k1_ge pkp; - int i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(partial_sig != NULL); - ARG_CHECK(pubnonce != NULL); - ARG_CHECK(pubkey != NULL); - ARG_CHECK(keyagg_cache != NULL); - ARG_CHECK(session != NULL); - - if (!secp256k1_musig_session_load(ctx, &session_i, session)) { - return 0; - } - - /* Compute "effective" nonce rj = aggnonce[0] + b*aggnonce[1] */ - /* TODO: use multiexp */ - for (i = 0; i < 2; i++) { - if (!secp256k1_musig_pubnonce_load(ctx, nonce_pt, pubnonce)) { - return 0; - } - } - secp256k1_gej_set_ge(&rj, &nonce_pt[1]); - secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &rj, &session_i.noncecoef, NULL); - secp256k1_gej_add_ge_var(&rj, &rj, &nonce_pt[0], NULL); - - if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) { - return 0; - } - if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { - return 0; - } - /* Multiplying the messagehash by the KeyAgg coefficient is equivalent - * to multiplying the signer's public key by the coefficient, except - * much easier to do. */ - secp256k1_musig_keyaggcoef(&mu, &cache_i, &pkp.x); - secp256k1_scalar_mul(&e, &session_i.challenge, &mu); - - /* If the MuSig-aggregate point has an odd Y coordinate, the signers will - * sign for the negation of their individual xonly public key such that the - * aggregate signature is valid for the MuSig aggregated xonly key. If the - * MuSig-aggregate point was tweaked then `e` is negated if the aggregate key - * has an odd Y coordinate XOR the internal key has an odd Y coordinate.*/ - if (secp256k1_fe_is_odd(&cache_i.pk.y) - != (cache_i.is_tweaked - && cache_i.internal_key_parity)) { - secp256k1_scalar_negate(&e, &e); - } - - if (!secp256k1_musig_partial_sig_load(ctx, &s, partial_sig)) { - return 0; - } - /* Compute -s*G + e*pkj + rj */ - secp256k1_scalar_negate(&s, &s); - secp256k1_gej_set_ge(&pkj, &pkp); - secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &pkj, &e, &s); - if (session_i.fin_nonce_parity) { - secp256k1_gej_neg(&rj, &rj); - } - secp256k1_gej_add_var(&tmp, &tmp, &rj, NULL); - - return secp256k1_gej_is_infinity(&tmp); -} - -int secp256k1_musig_partial_sig_agg(const secp256k1_context* ctx, unsigned char *sig64, const secp256k1_musig_session *session, const secp256k1_musig_partial_sig * const* partial_sigs, size_t n_sigs) { - size_t i; - secp256k1_musig_session_internal session_i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig64 != NULL); - ARG_CHECK(session != NULL); - ARG_CHECK(partial_sigs != NULL); - - if (!secp256k1_musig_session_load(ctx, &session_i, session)) { - return 0; - } - for (i = 0; i < n_sigs; i++) { - secp256k1_scalar term; - if (!secp256k1_musig_partial_sig_load(ctx, &term, partial_sigs[i])) { - return 0; - } - secp256k1_scalar_add(&session_i.s_part, &session_i.s_part, &term); - } - secp256k1_scalar_get_b32(&sig64[32], &session_i.s_part); - memcpy(&sig64[0], session_i.fin_nonce, 32); - return 1; -} - -#endif diff --git a/libwallet/musig/tapscript_test.go b/libwallet/musig/tapscript_test.go new file mode 100644 index 00000000..a25ea011 --- /dev/null +++ b/libwallet/musig/tapscript_test.go @@ -0,0 +1,643 @@ +package musig + +import ( + "encoding/hex" + "fmt" + "os" + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + musig2v100 "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btclog" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/stretchr/testify/require" +) + +var ( + userKey = secp256k1.PrivKeyFromBytes(hexDecode( + "507d881f0b5e1b12423cb0c84a196fb24227f3fe1540a1c7b20bf78d83de4533", + )) + muunKey = secp256k1.PrivKeyFromBytes(hexDecode( + "b6f14c73ee5269f5a13a11f48ad54306293ee134e924f680fcd35f615881105b", + )) + swapperKey = secp256k1.PrivKeyFromBytes(hexDecode( + "aaa14c73ee5269f5a13a11aaaad54306293ee134e924f680fcd35f6158811aaa", + )) +) + +// testScriptSchnorrSig returns a simple bitcoin script that locks the funds to +// a Schnorr signature of the given public key. +func testScriptSchnorrSig(t *testing.T, pubKey *btcec.PublicKey) txscript.TapLeaf { + + builder := txscript.NewScriptBuilder() + builder.AddData(schnorr.SerializePubKey(pubKey)) + builder.AddOp(txscript.OP_CHECKSIG) + script2, err := builder.Script() + require.NoError(t, err) + return txscript.NewBaseTapLeaf(script2) +} + +// TestTweakedKeyGeneration ensures that our helpers converges towards the same +// result as btcd/txscript +func TestTweakedKeyGeneration(t *testing.T) { + musigVersion := Musig2v100 + pubKeys := [][]byte{ + userKey.PubKey().SerializeCompressed(), + muunKey.PubKey().SerializeCompressed(), + } + + // first combine the public keys + aggKey, err := MuSig2ComputeInternalKey(musigVersion, pubKeys, nil) + require.NoError(t, err) + signerCombinedPubKey := aggKey.PreTweakedKey + + tapScriptHash := hexDecode( + "c7d15407975a8a3ed8686b607ee955880745289fedde01c5bdfb4a933c73f911") + tweak := TapScriptTweak(tapScriptHash) + + // build the tweaked key for this tapscript using txscript helpers + tweakedPubKey := txscript.ComputeTaprootOutputKey( + signerCombinedPubKey, tapScriptHash) + + // ensure our method produces the same Q as txscript + musigCalculatedQ, err := Musig2CombinePubKeysWithTweak( + musigVersion, pubKeys, tweak) + + require.NoError(t, err) + require.Equal(t, + hex.EncodeToString(tweakedPubKey.SerializeCompressed()), + hex.EncodeToString(musigCalculatedQ.FinalKey.SerializeCompressed()), + ) +} + +// Computes the internalKey without tweaks and an optional unhardenedDerivationPath +func MuSig2ComputeInternalKey( + musigVersion MusigVersion, + pubKeys [][]byte, + unhardenedDerivationPath []uint32, +) (*musig2v100.AggregateKey, error) { + tweaks := NoopTweak().WithUnhardenedDerivationPath(unhardenedDerivationPath) + + return Musig2CombinePubKeysWithTweak(musigVersion, pubKeys, tweaks) +} + +// TestSpendTapscript ensures that we can successfully spend an output descriptor +// with the shape +// +// tr( +// internalKey, +// { +// spendPathAKey, +// spendPathBKey, +// } +// ) +// +// The above output descritptor involves +// 1) committing to a TapScript + internalKey. We will call this Key-only spend path +// 2) using any of the two extra scripts. We will call this TapScript spend path +// +// Any of the keys involved can unilaterally spend the output. In practice, each +// of the keys is indeed a MuSig aggregated key. But thanks to schnorr +// signatures, they do look exactly the same as a regular public key. +// +// 1) Key-only spend path ===================================================== +// +// In this approach we use the internalKey to sign the taproot transaction. The +// key we use is not the key as is, instead we "tweak" the key adding +// an addendum of data in a process that produces a new key pair. Thanks to +// Schnorr signatures, we can tweak both the private and the public parts of the +// key and the result of both are compatible. The process and math of how this +// works are detailed below. +// +// The addendum itself is a commitment to the merkle root of the entire TapScript. +// +// Without tweaks: +// +// sign(key, nonce, Message): (Sig, PubNonce) -> +// let PubNonce = nonce * G +// let PubKey = key * G +// let Hash = h(PubNonce || PubKey || message) ━━━━━┓ +// let Sig = nonce + Hash * priv ───────────────────╂──┐ +// return (Sig, PubNonce) ┃ │ +// ┃ │ +// verify(PubKey, Sig, PubNonce, Message) -> ┃ │ +// let Hash = h(PubNonce || PubKey || Message) <━━━━┛ │ +// │ +// return Sig == nonce + Hash * priv <─────────┘ here is the Sig, and we validate it by performing +// return Sig*G == G * (nonce + Hash * priv) all the calculations again, but we don't have private +// return Sig*G == nonce*G + Hash * (priv*G) values, so we multiply both terms by G to obtain +// return Sig*G == PubNonce + Hash * PubKey all the public information to verify Sig +// +// With tweaks: +// +// sign(key, nonce, Message, Tweak t): (Sig, PubNonce) -> +// let PubNonce = nonce * G +// let PubKey = (priv+t) * G ──────────────────────────┐ add Tweak (t) +// let Hash = h(PubNonce || PubKey || message) ━━━━━━━┓│ +// let Sig = nonce + Hash * (priv+t) ─────────────────╂┤ +// return (Sig, PubNonce) ┃│ +// ┃│ +// verify(PubKey, Sig, PubNonce, Message, Tweak t) -> ┃│ +// let Hash = h(PubNonce || PubKey || Message) <━━━━━━┛│ +// Sig == nonce + Hash * priv+t <───────┤ we've added a tweak "t" to the private key, and to balance +// Sig*G == G * (nonce + Hash * priv+t) │ out the equation, we've also added t*G to the public key +// Sig*G == nonce*G + Hash * (priv+t)*G │ this makes the verification work without modification +// return Sig*G == PubNonce + Hash * PubKey <───────┘ (priv+t)*G == PubKey \\ nonce*G == PubNonce +// +// BIP86 establishes that if we are committing to a key-only spend path ONLY, +// effectively disabling any TapScript paths then the tweak must commit to an +// empty [0]byte{} scriptPath +// +// To generate the address, we first need to tweak the internalKey using the +// merkle root of the scripts, we will call this parameter "scriptPath". +// +// let tweak = h_tapTweak(internalPubKey || scriptPath) +// tweakedPubKey = internalPubKey + (tweak*G) +// tweakedPrivKey = internalKey + tweak +// +// Our address will be constructed as Bech32m(tweakedPubKey), and the key itself +// now can be used (along with the opaque merkleRoot) to spend the output. +// +// The tweak can be calculated by passing scriptPath to the MuSig2 helpers. +// +// If the use case requires it, it is possible to disable the keyspend path, leaving +// only the script path enabled. To do it, the internalKey must be disabled using +// the hash of a fixed point in the curve. +// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs +// +// 2) TapScript path ========================================================== +// +// For TapScript, we must provide a couple things to the transaction's control +// block to make it work: +// - internalKey +// - parity bit of the tweakedPubKey + version of the leafs of the TapScript tree +// - proof of inclusion of the script (merkle proof) +// - the WitnessScript (same as on the leaf) used to redeem the UTXO +func TestSpendTapscript(t *testing.T) { + spendPathAKey := userKey + spendPathBKey := muunKey + internalKey := swapperKey + + // We're going to commit to a script and spend the output using the + // script. This is just an OP_CHECKSIG with the public key. + leafA := testScriptSchnorrSig(t, spendPathAKey.PubKey()) + leafB := testScriptSchnorrSig(t, spendPathBKey.PubKey()) + tapScriptTree := txscript.AssembleTaprootScriptTree(leafA, leafB) + scriptRootHash := tapScriptTree.RootNode.TapHash() + + testCases := []tapscriptTestCase{ + { + // tr( + // internalKey, <----------------------------- redeem + // { + // spendPathAKey, + // spendPathBKey, + // } + // ) + description: "Spend using the internal key", + internalKey: internalKey.PubKey(), + + rootScript: tapScriptTree, + witnessScript: nil, + + signer: func(t *testing.T, msg []byte) []byte { + // Before we sign the sighash, we'll need to apply the + // taptweak to the private key based on the tapScriptRootHash. + privKeyTweak := txscript.TweakTaprootPrivKey(*internalKey, scriptRootHash[:]) + sig, err := schnorr.Sign(privKeyTweak, msg) + require.NoError(t, err) + return sig.Serialize() + }, + }, + { + // tr( + // internalKey, + // { + // spendPathAKey, <-------------------- redeem + // spendPathBKey, + // } + // ) + description: "Spend tapscript with leafA", + internalKey: internalKey.PubKey(), + + rootScript: tapScriptTree, + witnessScript: leafA.Script, + + signer: func(t *testing.T, msg []byte) []byte { + sig, err := schnorr.Sign(spendPathAKey, msg) + require.NoError(t, err) + return sig.Serialize() + }, + }, + { + // tr( + // internalKey, + // { + // spendPathAKey, + // spendPathBKey, <-------------------- redeem + // } + // ) + description: "Spend tapscript with leafB", + internalKey: internalKey.PubKey(), + + rootScript: tapScriptTree, + witnessScript: leafB.Script, + + signer: func(t *testing.T, msg []byte) []byte { + sig, err := schnorr.Sign(spendPathBKey, msg) + require.NoError(t, err) + return sig.Serialize() + }, + }, + } + + for _, test := range testCases { + name := fmt.Sprintf("tapscript test case=%s", test.description) + t.Run(name, func(t *testing.T) { + testTapscriptSpend(t, test) + }) + } +} + +// This test ensures the same functionality as the previous one, but the +// internalKey and one spendPath used are MuSig2v100 +// +// Output descriptor of this test: +// +// tr( +// musig(userKey, muunKey), +// { +// musig(userKey, muunKey), +// userKey, +// } +// ) +func TestSpendTapscriptMusig(t *testing.T) { + musigVersion := Musig2v100 + + pubKeys := [][]byte{ + userKey.PubKey().SerializeCompressed(), + muunKey.PubKey().SerializeCompressed(), + } + + // first combine the public keys to get the internalKey P + aggKeyAll, err := MuSig2ComputeInternalKey(musigVersion, pubKeys, nil) + require.NoError(t, err) + signerCombinedPubKey := aggKeyAll.PreTweakedKey + + // We're going to commit to a script and spend the output using the + // script. This is just an OP_CHECKSIG with the combined MuSig2 public + // key. + leafMusig := testScriptSchnorrSig(t, signerCombinedPubKey) + leafRawKey := testScriptSchnorrSig(t, userKey.PubKey()) + tapScriptTree := txscript.AssembleTaprootScriptTree(leafMusig, leafRawKey) + scriptRootHash := tapScriptTree.RootNode.TapHash() + + randomPriv, _ := secp256k1.GeneratePrivateKey() + randomPub := randomPriv.PubKey() + + testCases := []tapscriptTestCase{ + { + // tr( + // musig(userKey, muunKey), <----------- redeem + // { + // musig(userKey, muunKey), + // userKey, + // } + // ) + description: "Keyspend using musig as signer", + internalKey: signerCombinedPubKey, + + rootScript: tapScriptTree, + witnessScript: nil, + + signer: func(t *testing.T, msg []byte) []byte { + // in the keyspend path we must pass the script root + // hash to compute the final tweaked key. + tweak := TapScriptTweak(scriptRootHash[:]) + return muunSignMusig(t, musigVersion, msg, tweak) + }, + }, + { + // tr( + // musig(userKey, muunKey), + // { + // musig(userKey, muunKey), <---- redeem + // userKey, + // } + // ) + description: "MuSig spend tapscript", + internalKey: signerCombinedPubKey, + + rootScript: tapScriptTree, + witnessScript: leafMusig.Script, + + signer: func(t *testing.T, msg []byte) []byte { + // when signing with a regular (or musig) key in + // script spend path no tweaks should be applied + // see the next test case for a non-musig example + tweak := NoopTweak() + return muunSignMusig(t, musigVersion, msg, tweak) + }, + }, + { + // tr( + // musig(userKey, muunKey), + // { + // musig(userKey, muunKey), + // userKey, <-------------------- redeem + // } + // ) + description: "key-only spend tapscript", + internalKey: signerCombinedPubKey, + + rootScript: tapScriptTree, + witnessScript: leafRawKey.Script, + + signer: func(t *testing.T, msg []byte) []byte { + sig, err := schnorr.Sign(userKey, msg) + require.NoError(t, err) + return sig.Serialize() + }, + }, + { + // this test ensures that even though a random key was + // used to generate the outpoint, a valid tapscript + // input script can redeem it + // + // tr( + // RANDOM_PUB_KEY, ================> (CHANGED!) + // { + // musig(userKey, muunKey), + // userKey, <-------------------- redeem + // } + // ) + description: "key-only(user) spend tapscript. redeeming from random keyspend+tapscript(user)", + internalKey: randomPub, + + rootScript: tapScriptTree, + witnessScript: leafRawKey.Script, + + signer: func(t *testing.T, msg []byte) []byte { + sig, err := schnorr.Sign(userKey, msg) + require.NoError(t, err) + return sig.Serialize() + }, + }, + } + + for _, test := range testCases { + name := fmt.Sprintf("tapscript test case=%s", test.description) + t.Run(name, func(t *testing.T) { + testTapscriptSpend(t, test) + }) + } +} + +type tapscriptTestCase struct { + // a description of the test + description string + + // Internal key as defined in https://bips.dev/386 this key will be used + // to craft an outpupt script for taproot using the following algorithm: + // + // merkle_root: HashTapBranch(TREE) + // 32_byte_output_key: internalKey + int(HashTapTweak(bytes(internalKey) || rootScript))G + // scriptPubKey: OP_1 <32_byte_output_key> + // + // internalKey cannot be present along p2trKey + internalKey *secp256k1.PublicKey + + // Key used for the P2TR OP_1 to be redeemed. Use this alternative + // to enable better customization of the address. This may be necessary + // when the internalKey is the result of many tweak operations + // + // internalKey cannot be present along p2trKey + p2trKey *secp256k1.PublicKey + + // rootScript commits the internalKey to a pre-defined script. it is + // used to tweak the internalKey and produce the final Bech32m address + // - when the key-only spend path is chosen, either musig or the plain + // keys will be tweaked + // - when TapScript spend path is chosen, the witnessScript+proof of + // inclusion need to be provided in the control block along with the + // internalKey. the validator will then re-compute the bech32m with + // this information to verify the transaction + rootScript *txscript.IndexedTapScriptTree + + // TapScript requires a witnessScript belonging to a valid leaf of the + // rootScript + witnessScript []byte + + // a signer function to spend the funds of receiverAddress. any key can + // sign. For key-only spend path the tweak bytes need to be computed + // from scriptRootHash. For TapScript it is not necessary. + signer func(t *testing.T, msg []byte) []byte +} + +// helper function to test tapscript spending paths. each test case must provide +// a full indexed taproot script and a leaf, along with a public key that will +// receive the funds at first and a signer function +func testTapscriptSpend(t *testing.T, tc tapscriptTestCase) { + txscript.DisableLog() + + // change if you need to see what the script engine outputs + verbose := false + if verbose { + logger := btclog.NewBackend(os.Stderr).Logger("test") + logger.SetLevel(btclog.LevelTrace) + txscript.UseLogger(logger) + } + + hasInternalKey := tc.internalKey != nil + hasP2trKey := tc.p2trKey != nil + if !hasInternalKey && !hasP2trKey { + t.Fatal("Either p2trKey or internalKey must be provided") + } + if hasInternalKey == hasP2trKey { + t.Fatal("You must provide either p2trKey or internalKey, not both") + } + + tapRootHash := tc.rootScript.RootNode.TapHash() + + if tc.p2trKey == nil { + // generate a taproot address to receive the funds using the tweaked key + tc.p2trKey = txscript.ComputeTaprootOutputKey(tc.internalKey, tapRootHash[:]) + } + + p2trScript, err := txscript.PayToTaprootScript(tc.p2trKey) + require.NoError(t, err) + + tests := []struct { + sigHashType txscript.SigHashType + }{ + {sigHashType: txscript.SigHashDefault}, + {sigHashType: txscript.SigHashAll}, + {sigHashType: txscript.SigHashNone}, + {sigHashType: txscript.SigHashSingle}, + {sigHashType: txscript.SigHashSingle | txscript.SigHashAnyOneCanPay}, + {sigHashType: txscript.SigHashNone | txscript.SigHashAnyOneCanPay}, + {sigHashType: txscript.SigHashAll | txscript.SigHashAnyOneCanPay}, + } + for _, test := range tests { + name := fmt.Sprintf("sighash=%v", test.sigHashType) + t.Run(name, func(t *testing.T) { + testTx := wire.NewMsgTx(2) + + // set a dummy txOut pointing to the same address we are spending + txOut := &wire.TxOut{Value: 1e8, PkScript: p2trScript} + testTx.AddTxOut(txOut) + + // instruct the testTx to spend a virtual outpoint with + // index 1 + testTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: wire.OutPoint{Index: 1}, + }) + + // prevFetcher is the structure used to select a virtual + // outpoint + prevFetcher := txscript.NewCannedPrevOutputFetcher( + txOut.PkScript, txOut.Value) + + // get a hash to sign for the tx with the selected leaf + sigHashes := txscript.NewTxSigHashes(testTx, prevFetcher) + + var ( + msgToSign []byte + sig []byte + ) + + // if there is no tc.leaf, we assume key-only spend path + if tc.witnessScript == nil { + msgToSign, err = txscript.CalcTaprootSignatureHash( + sigHashes, + test.sigHashType, + testTx, 0, prevFetcher, + ) + require.NoError(t, err) + + // sign the taproot keyspend hash + sig = tc.signer(t, msgToSign) + + validSignature, err := VerifySignature( + Musig2v100, + msgToSign, + tc.p2trKey.SerializeCompressed(), + sig, + ) + require.NoError(t, err) + require.True(t, validSignature, "Signature invalid for tweaked key") + } else { + leaf := txscript.NewBaseTapLeaf(tc.witnessScript) + msgToSign, err = txscript.CalcTapscriptSignaturehash( + sigHashes, test.sigHashType, testTx, 0, + prevFetcher, + leaf, + ) + require.NoError(t, err) + + sig = tc.signer(t, msgToSign) + } + + // Finally, append the sighash type to the final sig if + // it's not the default sighash value (in which case + // appending it is disallowed). + if test.sigHashType != txscript.SigHashDefault { + sig = append(sig, byte(test.sigHashType)) + } + + // If this isn't sighash default, then a sighash should + // be applied. Otherwise, it should be a normal sig. + expectedLen := schnorr.SignatureSize + if test.sigHashType != txscript.SigHashDefault { + expectedLen += 1 + } + require.Len(t, sig, expectedLen) + + if tc.witnessScript == nil { + testTx.TxIn[0].Witness = wire.TxWitness{sig} + } else { + // find the control block for the selected leaf + tree := tc.rootScript + leaf := txscript.NewBaseTapLeaf(tc.witnessScript) + leafHash := leaf.TapHash() + settleIdx := tree.LeafProofIndex[chainhash.Hash(leafHash)] + settleMerkleProof := tree.LeafMerkleProofs[settleIdx] + controlBlock := settleMerkleProof.ToControlBlock(tc.internalKey) + controlBlockBytes, err := controlBlock.ToBytes() + require.NoError(t, err) + + testTx.TxIn[0].Witness = wire.TxWitness{ + sig, + tc.witnessScript, + controlBlockBytes, + } + } + + // Finally, ensure that the signature produced is valid + // by running the VM + vm, err := txscript.NewEngine( + txOut.PkScript, testTx, 0, + txscript.StandardVerifyFlags, + nil, sigHashes, txOut.Value, prevFetcher, + ) + require.NoError(t, err) + + require.NoError(t, vm.Execute()) + }) + } +} + +// signs a message using the globally configured keys +func muunSignMusig(t *testing.T, musigVersion MusigVersion, msg []byte, tweak *MuSig2Tweaks) []byte { + // generate musig sessionId + userSession, err := secp256k1.GeneratePrivateKey() + require.NoError(t, err) + muunSession, err := secp256k1.GeneratePrivateKey() + require.NoError(t, err) + + // generate musig nonces + userNonce, err := MuSig2GenerateNonce( + musigVersion, + userSession.Serialize(), + SerializePublicKey(musigVersion, userKey.PubKey()), + ) + require.NoError(t, err) + + muunNonce, err := MuSig2GenerateNonce( + musigVersion, + muunSession.Serialize(), + SerializePublicKey(musigVersion, muunKey.PubKey()), + ) + require.NoError(t, err) + + // sign muun's part + muunPartialSignatureBytes, err := ComputeMuunPartialSignature( + musigVersion, + msg, + SerializePublicKey(musigVersion, userKey.PubKey()), + muunKey.Serialize(), + userNonce.PubNonce[:], + muunSession.Serialize(), + tweak, + ) + require.NoError(t, err) + + // sign user's part + sig, err := ComputeUserPartialSignature( + musigVersion, + msg, + userKey.Serialize(), + SerializePublicKey(musigVersion, muunKey.PubKey()), + muunPartialSignatureBytes, + muunNonce.PubNonce[:], + userSession.Serialize(), + tweak, + ) + require.NoError(t, err) + + return sig +} diff --git a/libwallet/musig/testrand.h b/libwallet/musig/testrand.h deleted file mode 100644 index 40387a9e..00000000 --- a/libwallet/musig/testrand.h +++ /dev/null @@ -1,50 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_TESTRAND_H -#define SECP256K1_TESTRAND_H - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - -/* A non-cryptographic RNG used only for test infrastructure. */ - -/** Seed the pseudorandom number generator for testing. */ -SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16); - -/** Generate a pseudorandom number in the range [0..2**32-1]. */ -static uint32_t secp256k1_testrand32(void); - -/** Generate a pseudorandom number in the range [0..2**bits-1]. Bits must be 1 or - * more. */ -static uint32_t secp256k1_testrand_bits(int bits); - -/** Generate a pseudorandom number in the range [0..range-1]. */ -static uint32_t secp256k1_testrand_int(uint32_t range); - -/** Generate a pseudorandom 32-byte array. */ -static void secp256k1_testrand256(unsigned char *b32); - -/** Generate a pseudorandom 32-byte array with long sequences of zero and one bits. */ -static void secp256k1_testrand256_test(unsigned char *b32); - -/** Generate pseudorandom bytes with long sequences of zero and one bits. */ -static void secp256k1_testrand_bytes_test(unsigned char *bytes, size_t len); - -/** Generate a pseudorandom 64-bit integer in the range min..max, inclusive. */ -static int64_t secp256k1_testrandi64(uint64_t min, uint64_t max); - -/** Flip a single random bit in a byte array */ -static void secp256k1_testrand_flip(unsigned char *b, size_t len); - -/** Initialize the test RNG using (hex encoded) array up to 16 bytes, or randomly if hexseed is NULL. */ -static void secp256k1_testrand_init(const char* hexseed); - -/** Print final test information. */ -static void secp256k1_testrand_finish(void); - -#endif /* SECP256K1_TESTRAND_H */ diff --git a/libwallet/musig/testrand_impl.h b/libwallet/musig/testrand_impl.h deleted file mode 100644 index 52c0872f..00000000 --- a/libwallet/musig/testrand_impl.h +++ /dev/null @@ -1,176 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_TESTRAND_IMPL_H -#define SECP256K1_TESTRAND_IMPL_H - -#include -#include -#include -#include - -#include "testrand.h" -#include "hash.h" - -static secp256k1_rfc6979_hmac_sha256 secp256k1_test_rng; -static uint32_t secp256k1_test_rng_precomputed[8]; -static int secp256k1_test_rng_precomputed_used = 8; -static uint64_t secp256k1_test_rng_integer; -static int secp256k1_test_rng_integer_bits_left = 0; - -SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16) { - secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, seed16, 16); -} - -SECP256K1_INLINE static uint32_t secp256k1_testrand32(void) { - if (secp256k1_test_rng_precomputed_used == 8) { - secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, (unsigned char*)(&secp256k1_test_rng_precomputed[0]), sizeof(secp256k1_test_rng_precomputed)); - secp256k1_test_rng_precomputed_used = 0; - } - return secp256k1_test_rng_precomputed[secp256k1_test_rng_precomputed_used++]; -} - -static uint32_t secp256k1_testrand_bits(int bits) { - uint32_t ret; - if (secp256k1_test_rng_integer_bits_left < bits) { - secp256k1_test_rng_integer |= (((uint64_t)secp256k1_testrand32()) << secp256k1_test_rng_integer_bits_left); - secp256k1_test_rng_integer_bits_left += 32; - } - ret = secp256k1_test_rng_integer; - secp256k1_test_rng_integer >>= bits; - secp256k1_test_rng_integer_bits_left -= bits; - ret &= ((~((uint32_t)0)) >> (32 - bits)); - return ret; -} - -static uint32_t secp256k1_testrand_int(uint32_t range) { - /* We want a uniform integer between 0 and range-1, inclusive. - * B is the smallest number such that range <= 2**B. - * two mechanisms implemented here: - * - generate B bits numbers until one below range is found, and return it - * - find the largest multiple M of range that is <= 2**(B+A), generate B+A - * bits numbers until one below M is found, and return it modulo range - * The second mechanism consumes A more bits of entropy in every iteration, - * but may need fewer iterations due to M being closer to 2**(B+A) then - * range is to 2**B. The array below (indexed by B) contains a 0 when the - * first mechanism is to be used, and the number A otherwise. - */ - static const int addbits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0}; - uint32_t trange, mult; - int bits = 0; - if (range <= 1) { - return 0; - } - trange = range - 1; - while (trange > 0) { - trange >>= 1; - bits++; - } - if (addbits[bits]) { - bits = bits + addbits[bits]; - mult = ((~((uint32_t)0)) >> (32 - bits)) / range; - trange = range * mult; - } else { - trange = range; - mult = 1; - } - while(1) { - uint32_t x = secp256k1_testrand_bits(bits); - if (x < trange) { - return (mult == 1) ? x : (x % range); - } - } -} - -static void secp256k1_testrand256(unsigned char *b32) { - secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32); -} - -static void secp256k1_testrand_bytes_test(unsigned char *bytes, size_t len) { - size_t bits = 0; - memset(bytes, 0, len); - while (bits < len * 8) { - int now; - uint32_t val; - now = 1 + (secp256k1_testrand_bits(6) * secp256k1_testrand_bits(5) + 16) / 31; - val = secp256k1_testrand_bits(1); - while (now > 0 && bits < len * 8) { - bytes[bits / 8] |= val << (bits % 8); - now--; - bits++; - } - } -} - -static void secp256k1_testrand256_test(unsigned char *b32) { - secp256k1_testrand_bytes_test(b32, 32); -} - -SECP256K1_INLINE static int64_t secp256k1_testrandi64(uint64_t min, uint64_t max) { - uint64_t range; - uint64_t r; - uint64_t clz; - VERIFY_CHECK(max >= min); - if (max == min) { - return min; - } - range = max - min; - clz = secp256k1_clz64_var(range); - do { - r = ((uint64_t)secp256k1_testrand32() << 32) | secp256k1_testrand32(); - r >>= clz; - } while (r > range); - return min + (int64_t)r; -} - -static void secp256k1_testrand_flip(unsigned char *b, size_t len) { - b[secp256k1_testrand_int(len)] ^= (1 << secp256k1_testrand_int(8)); -} - -static void secp256k1_testrand_init(const char* hexseed) { - unsigned char seed16[16] = {0}; - if (hexseed && strlen(hexseed) != 0) { - int pos = 0; - while (pos < 16 && hexseed[0] != 0 && hexseed[1] != 0) { - unsigned short sh; - if ((sscanf(hexseed, "%2hx", &sh)) == 1) { - seed16[pos] = sh; - } else { - break; - } - hexseed += 2; - pos++; - } - } else { - FILE *frand = fopen("/dev/urandom", "rb"); - if ((frand == NULL) || fread(&seed16, 1, sizeof(seed16), frand) != sizeof(seed16)) { - uint64_t t = time(NULL) * (uint64_t)1337; - fprintf(stderr, "WARNING: could not read 16 bytes from /dev/urandom; falling back to insecure PRNG\n"); - seed16[0] ^= t; - seed16[1] ^= t >> 8; - seed16[2] ^= t >> 16; - seed16[3] ^= t >> 24; - seed16[4] ^= t >> 32; - seed16[5] ^= t >> 40; - seed16[6] ^= t >> 48; - seed16[7] ^= t >> 56; - } - if (frand) { - fclose(frand); - } - } - - printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]); - secp256k1_testrand_seed(seed16); -} - -static void secp256k1_testrand_finish(void) { - unsigned char run32[32]; - secp256k1_testrand256(run32); - printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]); -} - -#endif /* SECP256K1_TESTRAND_IMPL_H */ diff --git a/libwallet/musig/tweaks.go b/libwallet/musig/tweaks.go new file mode 100644 index 00000000..d8c6da5f --- /dev/null +++ b/libwallet/musig/tweaks.go @@ -0,0 +1,140 @@ +package musig + +import ( + "github.com/btcsuite/btcd/btcec/v2" + musig2v100 "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/muun/libwallet/musig2v040" +) + +// MuSig2Tweaks is a struct that contains all tweaks that can be applied to a +// MuSig2 combined public key. +type MuSig2Tweaks struct { + // GenericTweaks is a list of normal tweaks to apply to the combined + // public key (and to the private key when signing). + GenericTweaks []musig2v100.KeyTweakDesc + + // TaprootBIP0086Tweak indicates that the final key should use the + // taproot tweak as defined in BIP 341, with the BIP 86 modification: + // outputKey = internalKey + h_tapTweak(internalKey)*G. + // In this case, the aggregated key before the tweak will be used as the + // internal key. If this is set to true then TaprootTweak will be + // ignored. + TaprootBIP0086Tweak bool + + // TaprootTweak specifies that the final key should use the taproot + // tweak as defined in BIP 341: + // outputKey = internalKey + h_tapTweak(internalKey || scriptRoot). + // In this case, the aggregated key before the tweak will be used as the + // internal key. Will be ignored if TaprootBIP0086Tweak is set to true. + TaprootTweak []byte + + // Unhardened derivation path specifies the unhardened path to follow in + // order to derivate the final key based on BIP32 + BIP328. This property + // produces a list of GenericTweaks to be processed AFTER the provided + // GenericTweaks + UnhardenedDerivationPath []uint32 +} + +// HasTaprootTweak returns true if either a taproot BIP0086 tweak or a taproot +// script root tweak is set. +func (t *MuSig2Tweaks) HasTaprootTweak() bool { + return t.TaprootBIP0086Tweak || len(t.TaprootTweak) > 0 +} + +// ToContextOptions converts the tweak descriptor to context options. +func (t *MuSig2Tweaks) ToContextOptions(allSignerPubKeys []*btcec.PublicKey) ([]musig2v100.ContextOption, error) { + var tweakOpts []musig2v100.ContextOption + + if len(t.GenericTweaks) > 0 { + tweakOpts = append(tweakOpts, musig2v100.WithTweakedContext( + t.GenericTweaks..., + )) + } + + if len(t.UnhardenedDerivationPath) > 0 { + bip328tweaks, err := getKeyDerivationTweaksForMusig( + allSignerPubKeys, + t.UnhardenedDerivationPath) + if err != nil { + return nil, err + } + tweakOpts = append(tweakOpts, musig2v100.WithTweakedContext( + bip328tweaks..., + )) + } + + // The BIP0086 tweak and the taproot script tweak are mutually + // exclusive. + if t.TaprootBIP0086Tweak { + tweakOpts = append(tweakOpts, musig2v100.WithBip86TweakCtx()) + } else if len(t.TaprootTweak) > 0 { + tweakOpts = append(tweakOpts, musig2v100.WithTaprootTweakCtx( + t.TaprootTweak, + )) + } + + return tweakOpts, nil +} + +// ToV040ContextOptions converts the tweak descriptor to v0.4.0 context options. +func (t *MuSig2Tweaks) ToV040ContextOptions() []musig2v040.ContextOption { + var tweakOpts []musig2v040.ContextOption + + if len(t.GenericTweaks) > 0 { + genericTweaksCopy := make( + []musig2v040.KeyTweakDesc, len(t.GenericTweaks), + ) + for idx := range t.GenericTweaks { + genericTweaksCopy[idx] = musig2v040.KeyTweakDesc{ + Tweak: t.GenericTweaks[idx].Tweak, + IsXOnly: t.GenericTweaks[idx].IsXOnly, + } + } + tweakOpts = append(tweakOpts, musig2v040.WithTweakedContext( + genericTweaksCopy..., + )) + } + + // The BIP0086 tweak and the taproot script tweak are mutually + // exclusive. + if t.TaprootBIP0086Tweak { + tweakOpts = append(tweakOpts, musig2v040.WithBip86TweakCtx()) + } else if len(t.TaprootTweak) > 0 { + tweakOpts = append(tweakOpts, musig2v040.WithTaprootTweakCtx( + t.TaprootTweak, + )) + } + + return tweakOpts +} + +// If the spending conditions do not require a script path, the +// output key should commit to an unspendable script path instead +// of having no script path. This can be achieved by computing the +// output key point as Q = P + int(hashTapTweak(bytes(P)))G. +func KeySpendOnlyTweak() *MuSig2Tweaks { + return &MuSig2Tweaks{ + TaprootBIP0086Tweak: true, + } +} + +// Create tweak for taproot script, the argument is the TapHash of the root node. +func TapScriptTweak(rootNodeHash []byte) *MuSig2Tweaks { + return &MuSig2Tweaks{ + TaprootTweak: rootNodeHash, + } +} + +// Creates a tweak tha performs no operations over the final key. +// +// Use this tweak to SIGN during a tapscript spend path. +// Use this tweak to obtain the internal key of musig. +func NoopTweak() *MuSig2Tweaks { + return &MuSig2Tweaks{} +} + +// Mutates the current tweaks and sets a new unhardened derivation path for it. +func (t *MuSig2Tweaks) WithUnhardenedDerivationPath(path []uint32) *MuSig2Tweaks { + t.UnhardenedDerivationPath = path + return t +} diff --git a/libwallet/musig/umbrella.c b/libwallet/musig/umbrella.c deleted file mode 100644 index 2c1a6bd3..00000000 --- a/libwallet/musig/umbrella.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "umbrella.h" - -#include "secp256k1.k" -#include "scalar_8x32.h" -#include "scalar_impl.h" -#include "field_impl.h" -#include "group_impl.h" -#include "ecmult_gen_impl.h" -#include "ecmult_impl.h" -#include "hash_impl.h" -#include "extrakeys_main_impl.h" -#include "schnorrsig_main_impl.h" -#include "musig_main_impl.h" - diff --git a/libwallet/musig/umbrella.h b/libwallet/musig/umbrella.h deleted file mode 100644 index bf169036..00000000 --- a/libwallet/musig/umbrella.h +++ /dev/null @@ -1,5 +0,0 @@ -#include "secp256k1.h" -#include "secp256k1_extrakeys.h" -#include "secp256k1_schnorrsig.h" -#include "secp256k1_musig.h" -#include "util.h" diff --git a/libwallet/musig/util.h b/libwallet/musig/util.h deleted file mode 100644 index 7a244e35..00000000 --- a/libwallet/musig/util.h +++ /dev/null @@ -1,370 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_UTIL_H -#define SECP256K1_UTIL_H - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - -#include -#include -#include -#include - -typedef struct { - void (*fn)(const char *text, void* data); - const void* data; -} secp256k1_callback; - -static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback * const cb, const char * const text) { - cb->fn(text, (void*)cb->data); -} - -#ifdef DETERMINISTIC -#define TEST_FAILURE(msg) do { \ - fprintf(stderr, "%s\n", msg); \ - abort(); \ -} while(0); -#else -#define TEST_FAILURE(msg) do { \ - fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg); \ - abort(); \ -} while(0) -#endif - -#if SECP256K1_GNUC_PREREQ(3, 0) -#define EXPECT(x,c) __builtin_expect((x),(c)) -#else -#define EXPECT(x,c) (x) -#endif - -#ifdef DETERMINISTIC -#define CHECK(cond) do { \ - if (EXPECT(!(cond), 0)) { \ - TEST_FAILURE("test condition failed"); \ - } \ -} while(0) -#else -#define CHECK(cond) do { \ - if (EXPECT(!(cond), 0)) { \ - TEST_FAILURE("test condition failed: " #cond); \ - } \ -} while(0) -#endif - -/* Like assert(), but when VERIFY is defined, and side-effect safe. */ -#if defined(COVERAGE) -#define VERIFY_CHECK(check) -#define VERIFY_SETUP(stmt) -#elif defined(VERIFY) -#define VERIFY_CHECK CHECK -#define VERIFY_SETUP(stmt) do { stmt; } while(0) -#else -#define VERIFY_CHECK(cond) do { (void)(cond); } while(0) -#define VERIFY_SETUP(stmt) -#endif - -/* Define `VG_UNDEF` and `VG_CHECK` when VALGRIND is defined */ -#if !defined(VG_CHECK) -# if defined(VALGRIND) -# include -# define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y)) -# define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y)) -# else -# define VG_UNDEF(x,y) -# define VG_CHECK(x,y) -# endif -#endif - -/* Like `VG_CHECK` but on VERIFY only */ -#if defined(VERIFY) -#define VG_CHECK_VERIFY(x,y) VG_CHECK((x), (y)) -#else -#define VG_CHECK_VERIFY(x,y) -#endif - -static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) { - void *ret = malloc(size); - if (ret == NULL) { - secp256k1_callback_call(cb, "Out of memory"); - } - return ret; -} - -static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void *ptr, size_t size) { - void *ret = realloc(ptr, size); - if (ret == NULL) { - secp256k1_callback_call(cb, "Out of memory"); - } - return ret; -} - -#if defined(__BIGGEST_ALIGNMENT__) -#define ALIGNMENT __BIGGEST_ALIGNMENT__ -#else -/* Using 16 bytes alignment because common architectures never have alignment - * requirements above 8 for any of the types we care about. In addition we - * leave some room because currently we don't care about a few bytes. */ -#define ALIGNMENT 16 -#endif - -#define ROUND_TO_ALIGN(size) ((((size) + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT) - -/* Assume there is a contiguous memory object with bounds [base, base + max_size) - * of which the memory range [base, *prealloc_ptr) is already allocated for usage, - * where *prealloc_ptr is an aligned pointer. In that setting, this functions - * reserves the subobject [*prealloc_ptr, *prealloc_ptr + alloc_size) of - * alloc_size bytes by increasing *prealloc_ptr accordingly, taking into account - * alignment requirements. - * - * The function returns an aligned pointer to the newly allocated subobject. - * - * This is useful for manual memory management: if we're simply given a block - * [base, base + max_size), the caller can use this function to allocate memory - * in this block and keep track of the current allocation state with *prealloc_ptr. - * - * It is VERIFY_CHECKed that there is enough space left in the memory object and - * *prealloc_ptr is aligned relative to base. - */ -static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_size, void* base, size_t max_size) { - size_t aligned_alloc_size = ROUND_TO_ALIGN(alloc_size); - void* ret; - VERIFY_CHECK(prealloc_ptr != NULL); - VERIFY_CHECK(*prealloc_ptr != NULL); - VERIFY_CHECK(base != NULL); - VERIFY_CHECK((unsigned char*)*prealloc_ptr >= (unsigned char*)base); - VERIFY_CHECK(((unsigned char*)*prealloc_ptr - (unsigned char*)base) % ALIGNMENT == 0); - VERIFY_CHECK((unsigned char*)*prealloc_ptr - (unsigned char*)base + aligned_alloc_size <= max_size); - ret = *prealloc_ptr; - *prealloc_ptr = (unsigned char*)*prealloc_ptr + aligned_alloc_size; - return ret; -} - -/* Extract the sign of an int64, take the abs and return a uint64, constant time. */ -SECP256K1_INLINE static int secp256k1_sign_and_abs64(uint64_t *out, int64_t in) { - uint64_t mask0, mask1; - int ret; - ret = in < 0; - mask0 = ret + ~((uint64_t)0); - mask1 = ~mask0; - *out = (uint64_t)in; - *out = (*out & mask0) | ((~*out + 1) & mask1); - return ret; -} - -SECP256K1_INLINE static int secp256k1_clz64_var(uint64_t x) { - int ret; - if (!x) { - return 64; - } -# if defined(HAVE_BUILTIN_CLZLL) - ret = __builtin_clzll(x); -# else - /*FIXME: debruijn fallback. */ - for (ret = 0; ((x & (1ULL << 63)) == 0); x <<= 1, ret++); -# endif - return ret; -} - -/* Macro for restrict, when available and not in a VERIFY build. */ -#if defined(SECP256K1_BUILD) && defined(VERIFY) -# define SECP256K1_RESTRICT -#else -# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) -# if SECP256K1_GNUC_PREREQ(3,0) -# define SECP256K1_RESTRICT __restrict__ -# elif (defined(_MSC_VER) && _MSC_VER >= 1400) -# define SECP256K1_RESTRICT __restrict -# else -# define SECP256K1_RESTRICT -# endif -# else -# define SECP256K1_RESTRICT restrict -# endif -#endif - -#if defined(_WIN32) -# define I64FORMAT "I64d" -# define I64uFORMAT "I64u" -#else -# define I64FORMAT "lld" -# define I64uFORMAT "llu" -#endif - -#if defined(__GNUC__) -# define SECP256K1_GNUC_EXT __extension__ -#else -# define SECP256K1_GNUC_EXT -#endif - -/* If SECP256K1_{LITTLE,BIG}_ENDIAN is not explicitly provided, infer from various other system macros. */ -#if !defined(SECP256K1_LITTLE_ENDIAN) && !defined(SECP256K1_BIG_ENDIAN) -/* Inspired by https://github.com/rofl0r/endianness.h/blob/9853923246b065a3b52d2c43835f3819a62c7199/endianness.h#L52L73 */ -# if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ - defined(_X86_) || defined(__x86_64__) || defined(__i386__) || \ - defined(__i486__) || defined(__i586__) || defined(__i686__) || \ - defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) || \ - defined(__ARMEL__) || defined(__AARCH64EL__) || \ - (defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__ == 1) || \ - (defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN == 1) || \ - defined(_M_IX86) || defined(_M_AMD64) || defined(_M_ARM) /* MSVC */ -# define SECP256K1_LITTLE_ENDIAN -# endif -# if (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ - defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) || \ - defined(__MICROBLAZEEB__) || defined(__ARMEB__) || defined(__AARCH64EB__) || \ - (defined(__BIG_ENDIAN__) && __BIG_ENDIAN__ == 1) || \ - (defined(_BIG_ENDIAN) && _BIG_ENDIAN == 1) -# define SECP256K1_BIG_ENDIAN -# endif -#endif -#if defined(SECP256K1_LITTLE_ENDIAN) == defined(SECP256K1_BIG_ENDIAN) -# error Please make sure that either SECP256K1_LITTLE_ENDIAN or SECP256K1_BIG_ENDIAN is set, see src/util.h. -#endif - -/* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */ -static SECP256K1_INLINE void secp256k1_memczero(void *s, size_t len, int flag) { - unsigned char *p = (unsigned char *)s; - /* Access flag with a volatile-qualified lvalue. - This prevents clang from figuring out (after inlining) that flag can - take only be 0 or 1, which leads to variable time code. */ - volatile int vflag = flag; - unsigned char mask = -(unsigned char) vflag; - while (len) { - *p &= ~mask; - p++; - len--; - } -} - -/** Semantics like memcmp. Variable-time. - * - * We use this to avoid possible compiler bugs with memcmp, e.g. - * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189 - */ -static SECP256K1_INLINE int secp256k1_memcmp_var(const void *s1, const void *s2, size_t n) { - const unsigned char *p1 = s1, *p2 = s2; - size_t i; - - for (i = 0; i < n; i++) { - int diff = p1[i] - p2[i]; - if (diff != 0) { - return diff; - } - } - return 0; -} - -/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized and non-negative.*/ -static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag) { - unsigned int mask0, mask1, r_masked, a_masked; - /* Access flag with a volatile-qualified lvalue. - This prevents clang from figuring out (after inlining) that flag can - take only be 0 or 1, which leads to variable time code. */ - volatile int vflag = flag; - - /* Casting a negative int to unsigned and back to int is implementation defined behavior */ - VERIFY_CHECK(*r >= 0 && *a >= 0); - - mask0 = (unsigned int)vflag + ~0u; - mask1 = ~mask0; - r_masked = ((unsigned int)*r & mask0); - a_masked = ((unsigned int)*a & mask1); - - *r = (int)(r_masked | a_masked); -} - -/* If USE_FORCE_WIDEMUL_{INT128,INT64} is set, use that wide multiplication implementation. - * Otherwise use the presence of __SIZEOF_INT128__ to decide. - */ -#if defined(USE_FORCE_WIDEMUL_INT128) -# define SECP256K1_WIDEMUL_INT128 1 -#elif defined(USE_FORCE_WIDEMUL_INT64) -# define SECP256K1_WIDEMUL_INT64 1 -#elif defined(UINT128_MAX) || defined(__SIZEOF_INT128__) -# define SECP256K1_WIDEMUL_INT128 1 -#else -# define SECP256K1_WIDEMUL_INT64 1 -#endif -#if defined(SECP256K1_WIDEMUL_INT128) -# if !defined(UINT128_MAX) && defined(__SIZEOF_INT128__) -SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; -SECP256K1_GNUC_EXT typedef __int128 int128_t; -#define UINT128_MAX ((uint128_t)(-1)) -#define INT128_MAX ((int128_t)(UINT128_MAX >> 1)) -#define INT128_MIN (-INT128_MAX - 1) -/* No (U)INT128_C macros because compilers providing __int128 do not support 128-bit literals. */ -# endif -#endif - -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - -/* Determine the number of trailing zero bits in a (non-zero) 32-bit x. - * This function is only intended to be used as fallback for - * secp256k1_ctz32_var, but permits it to be tested separately. */ -static SECP256K1_INLINE int secp256k1_ctz32_var_debruijn(uint32_t x) { - static const uint8_t debruijn[32] = { - 0x00, 0x01, 0x02, 0x18, 0x03, 0x13, 0x06, 0x19, 0x16, 0x04, 0x14, 0x0A, - 0x10, 0x07, 0x0C, 0x1A, 0x1F, 0x17, 0x12, 0x05, 0x15, 0x09, 0x0F, 0x0B, - 0x1E, 0x11, 0x08, 0x0E, 0x1D, 0x0D, 0x1C, 0x1B - }; - return debruijn[((x & -x) * 0x04D7651F) >> 27]; -} - -/* Determine the number of trailing zero bits in a (non-zero) 64-bit x. - * This function is only intended to be used as fallback for - * secp256k1_ctz64_var, but permits it to be tested separately. */ -static SECP256K1_INLINE int secp256k1_ctz64_var_debruijn(uint64_t x) { - static const uint8_t debruijn[64] = { - 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, - 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, - 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, - 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12 - }; - return debruijn[((x & -x) * 0x022FDD63CC95386D) >> 58]; -} - -/* Determine the number of trailing zero bits in a (non-zero) 32-bit x. */ -static SECP256K1_INLINE int secp256k1_ctz32_var(uint32_t x) { - VERIFY_CHECK(x != 0); -#if (__has_builtin(__builtin_ctz) || SECP256K1_GNUC_PREREQ(3,4)) - /* If the unsigned type is sufficient to represent the largest uint32_t, consider __builtin_ctz. */ - if (((unsigned)UINT32_MAX) == UINT32_MAX) { - return __builtin_ctz(x); - } -#endif -#if (__has_builtin(__builtin_ctzl) || SECP256K1_GNUC_PREREQ(3,4)) - /* Otherwise consider __builtin_ctzl (the unsigned long type is always at least 32 bits). */ - return __builtin_ctzl(x); -#else - /* If no suitable CTZ builtin is available, use a (variable time) software emulation. */ - return secp256k1_ctz32_var_debruijn(x); -#endif -} - -/* Determine the number of trailing zero bits in a (non-zero) 64-bit x. */ -static SECP256K1_INLINE int secp256k1_ctz64_var(uint64_t x) { - VERIFY_CHECK(x != 0); -#if (__has_builtin(__builtin_ctzl) || SECP256K1_GNUC_PREREQ(3,4)) - /* If the unsigned long type is sufficient to represent the largest uint64_t, consider __builtin_ctzl. */ - if (((unsigned long)UINT64_MAX) == UINT64_MAX) { - return __builtin_ctzl(x); - } -#endif -#if (__has_builtin(__builtin_ctzll) || SECP256K1_GNUC_PREREQ(3,4)) - /* Otherwise consider __builtin_ctzll (the unsigned long long type is always at least 64 bits). */ - return __builtin_ctzll(x); -#else - /* If no suitable CTZ builtin is available, use a (variable time) software emulation. */ - return secp256k1_ctz64_var_debruijn(x); -#endif -} - -#endif /* SECP256K1_UTIL_H */ diff --git a/libwallet/musig2v040/README.md b/libwallet/musig2v040/README.md new file mode 100644 index 00000000..c127bb74 --- /dev/null +++ b/libwallet/musig2v040/README.md @@ -0,0 +1,17 @@ +# MuSig2 v0.4.0 + +This package contains a modified copy of the MuSig2 code as found in +`github.com/btcsuite/btcec/v2/schnorr/musig2` at the tag `btcec/v2.2.2` commit +`a2cbce3dbcfaf696ceb057147352ca61f1f968ec`. If you are planning to reproduce +those modifications, the patch in this directory may become handy. + +The nature of the modifications is to adapt the behavior to what +`secp256k1-zkp` does. The nonces are generated only by providing a sessionId +as rand parameter. Leaving the rest null, the serialization of the preimage of +the nonce also varies. + +This corresponds to the [MuSig2 BIP specification version of +`v0.4.0`](https://github.com/jonasnick/bips/blob/musig2/bip-musig2.mediawiki). + +We only keep this code here to allow implementing a backward compatible, +versioned MuSig2 RPC. diff --git a/libwallet/musig2v040/context.go b/libwallet/musig2v040/context.go new file mode 100644 index 00000000..95fe1290 --- /dev/null +++ b/libwallet/musig2v040/context.go @@ -0,0 +1,638 @@ +// Copyright (c) 2013-2022 The btcsuite developers + +package musig2v040 + +import ( + "errors" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" +) + +var ( + // ErrSignersNotSpecified is returned when a caller attempts to create + // a context without specifying either the total number of signers, or + // the complete set of singers. + ErrSignersNotSpecified = fmt.Errorf("total number of signers or all " + + "signers must be known") + + // ErrSignerNotInKeySet is returned when a the private key for a signer + // isn't included in the set of signing public keys. + ErrSignerNotInKeySet = fmt.Errorf("signing key is not found in key" + + " set") + + // ErrAlredyHaveAllNonces is called when RegisterPubNonce is called too + // many times for a given signing session. + ErrAlredyHaveAllNonces = fmt.Errorf("already have all nonces") + + // ErrNotEnoughSigners is returned when a caller attempts to create a + // session from a context, but before all the required signers are + // known. + ErrNotEnoughSigners = fmt.Errorf("not enough signers") + + // ErrAlredyHaveAllNonces is returned when a caller attempts to + // register a signer, once we already have the total set of known + // signers. + ErrAlreadyHaveAllSigners = fmt.Errorf("all signers registered") + + // ErrAlredyHaveAllSigs is called when CombineSig is called too many + // times for a given signing session. + ErrAlredyHaveAllSigs = fmt.Errorf("already have all sigs") + + // ErrSigningContextReuse is returned if a user attempts to sign using + // the same signing context more than once. + ErrSigningContextReuse = fmt.Errorf("nonce already used") + + // ErrFinalSigInvalid is returned when the combined signature turns out + // to be invalid. + ErrFinalSigInvalid = fmt.Errorf("final signature is invalid") + + // ErrCombinedNonceUnavailable is returned when a caller attempts to + // sign a partial signature, without first having collected all the + // required combined nonces. + ErrCombinedNonceUnavailable = fmt.Errorf("missing combined nonce") + + // ErrTaprootInternalKeyUnavailable is returned when a user attempts to + // obtain the + ErrTaprootInternalKeyUnavailable = fmt.Errorf("taproot tweak not used") + + // ErrNotEnoughSigners is returned if a caller attempts to obtain an + // early nonce when it wasn't specified + ErrNoEarlyNonce = fmt.Errorf("no early nonce available") +) + +// Context is a managed signing context for musig2. It takes care of things +// like securely generating secret nonces, aggregating keys and nonces, etc. +type Context struct { + // signingKey is the key we'll use for signing. + signingKey *btcec.PrivateKey + + // pubKey is our even-y coordinate public key. + pubKey *btcec.PublicKey + + // combinedKey is the aggregated public key. + combinedKey *AggregateKey + + // uniqueKeyIndex is the index of the second unique key in the keySet. + // This is used to speed up signing and verification computations. + uniqueKeyIndex int + + // keysHash is the hash of all the keys as defined in musig2. + keysHash []byte + + // opts is the set of options for the context. + opts *contextOptions + + // shouldSort keeps track of if the public keys should be sorted before + // any operations. + shouldSort bool +} + +// ContextOption is a functional option argument that allows callers to modify +// the musig2 signing is done within a context. +type ContextOption func(*contextOptions) + +// contextOptions houses the set of functional options that can be used to +// musig2 signing protocol. +type contextOptions struct { + // tweaks is the set of optinoal tweaks to apply to the combined public + // key. + tweaks []KeyTweakDesc + + // taprootTweak specifies the taproot tweak. If specified, then we'll + // use this as the script root for the BIP 341 taproot (x-only) tweak. + // Normally we'd just apply the raw 32 byte tweak, but for taproot, we + // first need to compute the aggregated key before tweaking, and then + // use it as the internal key. This is required as the taproot tweak + // also commits to the public key, which in this case is the aggregated + // key before the tweak. + taprootTweak []byte + + // bip86Tweak if true, then the weak will just be + // h_tapTweak(internalKey) as there is no true script root. + bip86Tweak bool + + // keySet is the complete set of signers for this context. + keySet []*btcec.PublicKey + + // numSigners is the total number of signers that will eventually be a + // part of the context. + numSigners int + + // earlyNonce determines if a nonce should be generated during context + // creation, to be automatically passed to the created session. + earlyNonce bool +} + +// defaultContextOptions returns the default context options. +func defaultContextOptions() *contextOptions { + return &contextOptions{} +} + +// WithTweakedContext specifies that within the context, the aggregated public +// key should be tweaked with the specified tweaks. +func WithTweakedContext(tweaks ...KeyTweakDesc) ContextOption { + return func(o *contextOptions) { + o.tweaks = tweaks + } +} + +// WithTaprootTweakCtx specifies that within this context, the final key should +// use the taproot tweak as defined in BIP 341: outputKey = internalKey + +// h_tapTweak(internalKey || scriptRoot). In this case, the aggreaged key +// before the tweak will be used as the internal key. +func WithTaprootTweakCtx(scriptRoot []byte) ContextOption { + return func(o *contextOptions) { + o.taprootTweak = scriptRoot + } +} + +// WithBip86TweakCtx specifies that within this context, the final key should +// use the taproot tweak as defined in BIP 341, with the BIP 86 modification: +// outputKey = internalKey + h_tapTweak(internalKey)*G. In this case, the +// aggreaged key before the tweak will be used as the internal key. +func WithBip86TweakCtx() ContextOption { + return func(o *contextOptions) { + o.bip86Tweak = true + } +} + +// WithKnownSigners is an optional parameter that should be used if a session +// can be created as soon as all the singers are known. +func WithKnownSigners(signers []*btcec.PublicKey) ContextOption { + return func(o *contextOptions) { + o.keySet = signers + o.numSigners = len(signers) + } +} + +// WithNumSigners is a functional option used to specify that a context should +// be created without knowing all the signers. Instead the total number of +// signers is specified to ensure that a session can only be created once all +// the signers are known. +// +// NOTE: Either WithKnownSigners or WithNumSigners MUST be specified. +func WithNumSigners(n int) ContextOption { + return func(o *contextOptions) { + o.numSigners = n + } +} + +// WithEarlyNonceGen allow a caller to specify that a nonce should be generated +// early, before the session is created. This should be used in protocols that +// require some partial nonce exchange before all the signers are known. +// +// NOTE: This option must only be specified with the WithNumSigners option. +func WithEarlyNonceGen() ContextOption { + return func(o *contextOptions) { + o.earlyNonce = true + } +} + +// NewContext creates a new signing context with the passed singing key and set +// of public keys for each of the other signers. +// +// NOTE: This struct should be used over the raw Sign API whenever possible. +func NewContext(signingKey *btcec.PrivateKey, shouldSort bool, + ctxOpts ...ContextOption) (*Context, error) { + + // First, parse the set of optional context options. + opts := defaultContextOptions() + for _, option := range ctxOpts { + option(opts) + } + + pubKey, err := schnorr.ParsePubKey( + schnorr.SerializePubKey(signingKey.PubKey()), + ) + if err != nil { + return nil, err + } + + ctx := &Context{ + signingKey: signingKey, + pubKey: pubKey, + opts: opts, + shouldSort: shouldSort, + } + + switch { + + // We know all the signers, so we can compute the aggregated key, along + // with all the other intermediate state we need to do signing and + // verification. + case opts.keySet != nil: + if err := ctx.combineSignerKeys(); err != nil { + return nil, err + } + + // The total signers are known, so we add ourselves, and skip key + // aggregation. + case opts.numSigners != 0: + // Otherwise, we'll add ourselves as the only known signer, and + // await further calls to RegisterSigner before a session can + // be created. + opts.keySet = make([]*btcec.PublicKey, 0, opts.numSigners) + opts.keySet = append(opts.keySet, pubKey) + + default: + return nil, ErrSignersNotSpecified + } + + return ctx, nil +} + +// combineSignerKeys is used to compute the aggregated signer key once all the +// signers are known. +func (c *Context) combineSignerKeys() error { + // As a sanity check, make sure the signing key is actually + // amongst the sit of signers. + var keyFound bool + for _, key := range c.opts.keySet { + if key.IsEqual(c.pubKey) { + keyFound = true + break + } + } + if !keyFound { + return ErrSignerNotInKeySet + } + + // Now that we know that we're actually a signer, we'll + // generate the key hash finger print and second unique key + // index so we can speed up signing later. + c.keysHash = keyHashFingerprint(c.opts.keySet, c.shouldSort) + c.uniqueKeyIndex = secondUniqueKeyIndex( + c.opts.keySet, c.shouldSort, + ) + + keyAggOpts := []KeyAggOption{ + WithKeysHash(c.keysHash), + WithUniqueKeyIndex(c.uniqueKeyIndex), + } + switch { + case c.opts.bip86Tweak: + keyAggOpts = append( + keyAggOpts, WithBIP86KeyTweak(), + ) + case c.opts.taprootTweak != nil: + keyAggOpts = append( + keyAggOpts, WithTaprootKeyTweak(c.opts.taprootTweak), + ) + case len(c.opts.tweaks) != 0: + keyAggOpts = append(keyAggOpts, WithKeyTweaks(c.opts.tweaks...)) + } + + // Next, we'll use this information to compute the aggregated + // public key that'll be used for signing in practice. + var err error + c.combinedKey, _, _, err = AggregateKeys( + c.opts.keySet, c.shouldSort, keyAggOpts..., + ) + if err != nil { + return err + } + + return nil +} + +// EarlySessionNonce returns the early session nonce, if available. +func (c *Context) EarlySessionNonce() (*musig2.Nonces, error) { + return nil, ErrNoEarlyNonce +} + +// RegisterSigner allows a caller to register a signer after the context has +// been created. This will be used in scenarios where the total number of +// signers is known, but nonce exchange needs to happen before all the signers +// are known. +// +// A bool is returned which indicates if all the signers have been registered. +// +// NOTE: If the set of keys are not to be sorted during signing, then the +// ordering each key is registered with MUST match the desired ordering. +func (c *Context) RegisterSigner(pub *btcec.PublicKey) (bool, error) { + haveAllSigners := len(c.opts.keySet) == c.opts.numSigners + if haveAllSigners { + return false, ErrAlreadyHaveAllSigners + } + + c.opts.keySet = append(c.opts.keySet, pub) + + // If we have the expected number of signers at this point, then we can + // generate the aggregated key and other necessary information. + haveAllSigners = len(c.opts.keySet) == c.opts.numSigners + if haveAllSigners { + if err := c.combineSignerKeys(); err != nil { + return false, err + } + } + + return haveAllSigners, nil +} + +// NumRegisteredSigners returns the total number of registered signers. +func (c *Context) NumRegisteredSigners() int { + return len(c.opts.keySet) +} + +// CombinedKey returns the combined public key that will be used to generate +// multi-signatures against. +func (c *Context) CombinedKey() (*btcec.PublicKey, error) { + // If the caller hasn't registered all the signers at this point, then + // the combined key won't be available. + if c.combinedKey == nil { + return nil, ErrNotEnoughSigners + } + + return c.combinedKey.FinalKey, nil +} + +// PubKey returns the public key of the signer of this session. +func (c *Context) PubKey() btcec.PublicKey { + return *c.pubKey +} + +// SigningKeys returns the set of keys used for signing. +func (c *Context) SigningKeys() []*btcec.PublicKey { + keys := make([]*btcec.PublicKey, len(c.opts.keySet)) + copy(keys, c.opts.keySet) + + return keys +} + +// TaprootInternalKey returns the internal taproot key, which is the aggregated +// key _before_ the tweak is applied. If a taproot tweak was specified, then +// CombinedKey() will return the fully tweaked output key, with this method +// returning the internal key. If a taproot tweak wasn't specified, then this +// method will return an error. +func (c *Context) TaprootInternalKey() (*btcec.PublicKey, error) { + // If the caller hasn't registered all the signers at this point, then + // the combined key won't be available. + if c.combinedKey == nil { + return nil, ErrNotEnoughSigners + } + + if c.opts.taprootTweak == nil && !c.opts.bip86Tweak { + return nil, ErrTaprootInternalKeyUnavailable + } + + return c.combinedKey.PreTweakedKey, nil +} + +// SessionOption is a functional option argument that allows callers to modify +// the musig2 signing is done within a session. +type SessionOption func(*sessionOptions) + +// sessionOptions houses the set of functional options that can be used to +// modify the musig2 signing protocol. +type sessionOptions struct { + externalNonce *musig2.Nonces +} + +// defaultSessionOptions returns the default session options. +func defaultSessionOptions() *sessionOptions { + return &sessionOptions{} +} + +// WithPreGeneratedNonce allows a caller to start a session using a nonce +// they've generated themselves. This may be useful in protocols where all the +// signer keys may not be known before nonce exchange needs to occur. +func WithPreGeneratedNonce(nonce *musig2.Nonces) SessionOption { + return func(o *sessionOptions) { + o.externalNonce = nonce + } +} + +// Session represents a musig2 signing session. A new instance should be +// created each time a multi-signature is needed. The session struct handles +// nonces management, incremental partial sig vitrifaction, as well as final +// signature combination. Errors are returned when unsafe behavior such as +// nonce re-use is attempted. +// +// NOTE: This struct should be used over the raw Sign API whenever possible. +type Session struct { + opts *sessionOptions + + ctx *Context + + localNonces *musig2.Nonces + + pubNonces [][musig2.PubNonceSize]byte + + combinedNonce *[musig2.PubNonceSize]byte + + msg [32]byte + + ourSig *PartialSignature + sigs []*PartialSignature + + finalSig *schnorr.Signature +} + +// NewSession creates a new musig2 signing session. +func (c *Context) NewSession(options ...SessionOption) (*Session, error) { + opts := defaultSessionOptions() + for _, opt := range options { + opt(opts) + } + + // At this point we verify that we know of all the signers, as + // otherwise we can't proceed with the session. This check is intended + // to catch misuse of the API wherein a caller forgets to register the + // remaining signers if they're doing nonce generation ahead of time. + if len(c.opts.keySet) != c.opts.numSigners { + return nil, ErrNotEnoughSigners + } + + // If an early nonce was specified, then we'll automatically add the + // corresponding session option for the caller. + var localNonces *musig2.Nonces + if opts.externalNonce != nil { + // Otherwise if there's a custom nonce passed in via the + // session options, then use that instead. + localNonces = opts.externalNonce + } + + // Now that we know we have enough signers, we'll either use the caller + // specified nonce, or generate a fresh set. + if localNonces == nil { + return nil, errors.New("a pre-created nonce must be passed onto the session") + } + + s := &Session{ + opts: opts, + ctx: c, + localNonces: localNonces, + pubNonces: make([][musig2.PubNonceSize]byte, 0, c.opts.numSigners), + sigs: make([]*PartialSignature, 0, c.opts.numSigners), + } + + s.pubNonces = append(s.pubNonces, localNonces.PubNonce) + + return s, nil +} + +// PublicNonce returns the public nonce for a signer. This should be sent to +// other parties before signing begins, so they can compute the aggregated +// public nonce. +func (s *Session) PublicNonce() [musig2.PubNonceSize]byte { + return s.localNonces.PubNonce +} + +// NumRegisteredNonces returns the total number of nonces that have been +// regsitered so far. +func (s *Session) NumRegisteredNonces() int { + return len(s.pubNonces) +} + +// RegisterPubNonce should be called for each public nonce from the set of +// signers. This method returns true once all the public nonces have been +// accounted for. +func (s *Session) RegisterPubNonce(nonce [musig2.PubNonceSize]byte) (bool, error) { + // If we already have all the nonces, then this method was called too + // many times. + haveAllNonces := len(s.pubNonces) == s.ctx.opts.numSigners + if haveAllNonces { + return false, ErrAlredyHaveAllNonces + } + + // Add this nonce and check again if we already have tall the nonces we + // need. + s.pubNonces = append(s.pubNonces, nonce) + haveAllNonces = len(s.pubNonces) == s.ctx.opts.numSigners + + // If we have all the nonces, then we can go ahead and combine them + // now. + if haveAllNonces { + combinedNonce, err := AggregateNonces(s.pubNonces) + if err != nil { + return false, err + } + + s.combinedNonce = &combinedNonce + } + + return haveAllNonces, nil +} + +// Sign generates a partial signature for the target message, using the target +// context. If this method is called more than once per context, then an error +// is returned, as that means a nonce was re-used. +func (s *Session) Sign(msg [32]byte, + signOpts ...SignOption) (*PartialSignature, error) { + + switch { + // If no local nonce is present, then this means we already signed, so + // we'll return an error to prevent nonce re-use. + case s.localNonces == nil: + return nil, ErrSigningContextReuse + + // We also need to make sure we have the combined nonce, otherwise this + // function was called too early. + case s.combinedNonce == nil: + return nil, ErrCombinedNonceUnavailable + } + + switch { + case s.ctx.opts.bip86Tweak: + signOpts = append( + signOpts, WithBip86SignTweak(), + ) + case s.ctx.opts.taprootTweak != nil: + signOpts = append( + signOpts, WithTaprootSignTweak(s.ctx.opts.taprootTweak), + ) + case len(s.ctx.opts.tweaks) != 0: + signOpts = append(signOpts, WithTweaks(s.ctx.opts.tweaks...)) + } + + partialSig, err := Sign( + s.localNonces.SecNonce, s.ctx.signingKey, *s.combinedNonce, + s.ctx.opts.keySet, msg, signOpts..., + ) + + // Now that we've generated our signature, we'll make sure to blank out + // our signing nonce. + s.localNonces = nil + + if err != nil { + return nil, err + } + + s.msg = msg + + s.ourSig = partialSig + s.sigs = append(s.sigs, partialSig) + + return partialSig, nil +} + +// CombineSig buffers a partial signature received from a signing party. The +// method returns true once all the signatures are available, and can be +// combined into the final signature. +func (s *Session) CombineSig(sig *PartialSignature) (bool, error) { + // First check if we already have all the signatures we need. We + // already accumulated our own signature when we generated the sig. + haveAllSigs := len(s.sigs) == len(s.ctx.opts.keySet) + if haveAllSigs { + return false, ErrAlredyHaveAllSigs + } + + // TODO(roasbeef): incremental check for invalid sig, or just detect at + // the very end? + + // Accumulate this sig, and check again if we have all the sigs we + // need. + s.sigs = append(s.sigs, sig) + haveAllSigs = len(s.sigs) == len(s.ctx.opts.keySet) + + // If we have all the signatures, then we can combine them all into the + // final signature. + if haveAllSigs { + var combineOpts []CombineOption + switch { + case s.ctx.opts.bip86Tweak: + combineOpts = append( + combineOpts, WithBip86TweakedCombine( + s.msg, s.ctx.opts.keySet, + s.ctx.shouldSort, + ), + ) + case s.ctx.opts.taprootTweak != nil: + combineOpts = append( + combineOpts, WithTaprootTweakedCombine( + s.msg, s.ctx.opts.keySet, + s.ctx.opts.taprootTweak, s.ctx.shouldSort, + ), + ) + case len(s.ctx.opts.tweaks) != 0: + combineOpts = append( + combineOpts, WithTweakedCombine( + s.msg, s.ctx.opts.keySet, + s.ctx.opts.tweaks, s.ctx.shouldSort, + ), + ) + } + + finalSig := CombineSigs(s.ourSig.R, s.sigs, combineOpts...) + + // We'll also verify the signature at this point to ensure it's + // valid. + // + // TODO(roasbef): allow skipping? + if !finalSig.Verify(s.msg[:], s.ctx.combinedKey.FinalKey) { + return false, ErrFinalSigInvalid + } + + s.finalSig = finalSig + } + + return haveAllSigs, nil +} + +// FinalSig returns the final combined multi-signature, if present. +func (s *Session) FinalSig() *schnorr.Signature { + return s.finalSig +} diff --git a/libwallet/musig2v040/keys.go b/libwallet/musig2v040/keys.go new file mode 100644 index 00000000..9016f0b5 --- /dev/null +++ b/libwallet/musig2v040/keys.go @@ -0,0 +1,459 @@ +// Copyright 2013-2022 The btcsuite developers + +package musig2v040 + +import ( + "bytes" + "fmt" + "sort" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg/chainhash" + secp "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +var ( + // KeyAggTagList is the tagged hash tag used to compute the hash of the + // list of sorted public keys. + KeyAggTagList = []byte("KeyAgg list") + + // KeyAggTagCoeff is the tagged hash tag used to compute the key + // aggregation coefficient for each key. + KeyAggTagCoeff = []byte("KeyAgg coefficient") + + // ErrTweakedKeyIsInfinity is returned if while tweaking a key, we end + // up with the point at infinity. + ErrTweakedKeyIsInfinity = fmt.Errorf("tweaked key is infinity point") + + // ErrTweakedKeyOverflows is returned if a tweaking key is larger than + // 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141. + ErrTweakedKeyOverflows = fmt.Errorf("tweaked key is to large") +) + +// sortableKeys defines a type of slice of public keys that implements the sort +// interface for BIP 340 keys. +type sortableKeys []*btcec.PublicKey + +// Less reports whether the element with index i must sort before the element +// with index j. +func (s sortableKeys) Less(i, j int) bool { + // TODO(roasbeef): more efficient way to compare... + keyIBytes := schnorr.SerializePubKey(s[i]) + keyJBytes := schnorr.SerializePubKey(s[j]) + + return bytes.Compare(keyIBytes, keyJBytes) == -1 +} + +// Swap swaps the elements with indexes i and j. +func (s sortableKeys) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Len is the number of elements in the collection. +func (s sortableKeys) Len() int { + return len(s) +} + +// sortKeys takes a set of schnorr public keys and returns a new slice that is +// a copy of the keys sorted in lexicographical order bytes on the x-only +// pubkey serialization. +func sortKeys(keys []*btcec.PublicKey) []*btcec.PublicKey { + keySet := sortableKeys(keys) + if sort.IsSorted(keySet) { + return keys + } + + sort.Sort(keySet) + return keySet +} + +// keyHashFingerprint computes the tagged hash of the series of (sorted) public +// keys passed as input. This is used to compute the aggregation coefficient +// for each key. The final computation is: +// - H(tag=KeyAgg list, pk1 || pk2..) +func keyHashFingerprint(keys []*btcec.PublicKey, sort bool) []byte { + if sort { + keys = sortKeys(keys) + } + + // We'll create a single buffer and slice into that so the bytes buffer + // doesn't continually need to grow the underlying buffer. + keyAggBuf := make([]byte, 32*len(keys)) + keyBytes := bytes.NewBuffer(keyAggBuf[0:0]) + for _, key := range keys { + keyBytes.Write(schnorr.SerializePubKey(key)) + } + + h := chainhash.TaggedHash(KeyAggTagList, keyBytes.Bytes()) + return h[:] +} + +// keyBytesEqual returns true if two keys are the same from the PoV of BIP +// 340's 32-byte x-only public keys. +func keyBytesEqual(a, b *btcec.PublicKey) bool { + return bytes.Equal( + schnorr.SerializePubKey(a), + schnorr.SerializePubKey(b), + ) +} + +// aggregationCoefficient computes the key aggregation coefficient for the +// specified target key. The coefficient is computed as: +// - H(tag=KeyAgg coefficient, keyHashFingerprint(pks) || pk) +func aggregationCoefficient(keySet []*btcec.PublicKey, + targetKey *btcec.PublicKey, keysHash []byte, + secondKeyIdx int) *btcec.ModNScalar { + + var mu btcec.ModNScalar + + // If this is the second key, then this coefficient is just one. + if secondKeyIdx != -1 && keyBytesEqual(keySet[secondKeyIdx], targetKey) { + return mu.SetInt(1) + } + + // Otherwise, we'll compute the full finger print hash for this given + // key and then use that to compute the coefficient tagged hash: + // * H(tag=KeyAgg coefficient, keyHashFingerprint(pks, pk) || pk) + var coefficientBytes [64]byte + copy(coefficientBytes[:], keysHash[:]) + copy(coefficientBytes[32:], schnorr.SerializePubKey(targetKey)) + + muHash := chainhash.TaggedHash(KeyAggTagCoeff, coefficientBytes[:]) + + mu.SetByteSlice(muHash[:]) + + return &mu +} + +// secondUniqueKeyIndex returns the index of the second unique key. If all keys +// are the same, then a value of -1 is returned. +func secondUniqueKeyIndex(keySet []*btcec.PublicKey, sort bool) int { + if sort { + keySet = sortKeys(keySet) + } + + // Find the first key that isn't the same as the very first key (second + // unique key). + for i := range keySet { + if !keyBytesEqual(keySet[i], keySet[0]) { + return i + } + } + + // A value of negative one is used to indicate that all the keys in the + // sign set are actually equal, which in practice actually makes musig2 + // useless, but we need a value to distinguish this case. + return -1 +} + +// KeyTweakDesc describes a tweak to be applied to the aggregated public key +// generation and signing process. The IsXOnly specifies if the target key +// should be converted to an x-only public key before tweaking. +type KeyTweakDesc struct { + // Tweak is the 32-byte value that will modify the public key. + Tweak [32]byte + + // IsXOnly if true, then the public key will be mapped to an x-only key + // before the tweaking operation is applied. + IsXOnly bool +} + +// KeyAggOption is a functional option argument that allows callers to specify +// more or less information that has been pre-computed to the main routine. +type KeyAggOption func(*keyAggOption) + +// keyAggOption houses the set of functional options that modify key +// aggregation. +type keyAggOption struct { + // keyHash is the output of keyHashFingerprint for a given set of keys. + keyHash []byte + + // uniqueKeyIndex is the pre-computed index of the second unique key. + uniqueKeyIndex *int + + // tweaks specifies a series of tweaks to be applied to the aggregated + // public key. + tweaks []KeyTweakDesc + + // taprootTweak controls if the tweaks above should be applied in a BIP + // 340 style. + taprootTweak bool + + // bip86Tweak specifies that the taproot tweak should be done in a BIP + // 86 style, where we don't expect an actual tweak and instead just + // commit to the public key itself. + bip86Tweak bool +} + +// WithKeysHash allows key aggregation to be optimize, by allowing the caller +// to specify the hash of all the keys. +func WithKeysHash(keyHash []byte) KeyAggOption { + return func(o *keyAggOption) { + o.keyHash = keyHash + } +} + +// WithUniqueKeyIndex allows the caller to specify the index of the second +// unique key. +func WithUniqueKeyIndex(idx int) KeyAggOption { + return func(o *keyAggOption) { + i := idx + o.uniqueKeyIndex = &i + } +} + +// WithKeyTweaks allows a caller to specify a series of 32-byte tweaks that +// should be applied to the final aggregated public key. +func WithKeyTweaks(tweaks ...KeyTweakDesc) KeyAggOption { + return func(o *keyAggOption) { + o.tweaks = tweaks + } +} + +// WithTaprootKeyTweak specifies that within this context, the final key should +// use the taproot tweak as defined in BIP 341: outputKey = internalKey + +// h_tapTweak(internalKey || scriptRoot). In this case, the aggregated key +// before the tweak will be used as the internal key. +// +// This option should be used instead of WithKeyTweaks when the aggregated key +// is intended to be used as a taproot output key that commits to a script +// root. +func WithTaprootKeyTweak(scriptRoot []byte) KeyAggOption { + return func(o *keyAggOption) { + var tweak [32]byte + copy(tweak[:], scriptRoot[:]) + + o.tweaks = []KeyTweakDesc{ + { + Tweak: tweak, + IsXOnly: true, + }, + } + o.taprootTweak = true + } +} + +// WithBIP86KeyTweak specifies that then during key aggregation, the BIP 86 +// tweak which just commits to the hash of the serialized public key should be +// used. This option should be used when signing with a key that was derived +// using BIP 86. +func WithBIP86KeyTweak() KeyAggOption { + return func(o *keyAggOption) { + o.tweaks = []KeyTweakDesc{ + { + IsXOnly: true, + }, + } + o.taprootTweak = true + o.bip86Tweak = true + } +} + +// defaultKeyAggOptions returns the set of default arguments for key +// aggregation. +func defaultKeyAggOptions() *keyAggOption { + return &keyAggOption{} +} + +// hasEvenY returns true if the affine representation of the passed jacobian +// point has an even y coordinate. +// +// TODO(roasbeef): double check, can just check the y coord even not jacobian? +func hasEvenY(pJ btcec.JacobianPoint) bool { + pJ.ToAffine() + p := btcec.NewPublicKey(&pJ.X, &pJ.Y) + keyBytes := p.SerializeCompressed() + return keyBytes[0] == secp.PubKeyFormatCompressedEven +} + +// tweakKey applies a tweaks to the passed public key using the specified +// tweak. The parityAcc and tweakAcc are returned (in that order) which +// includes the accumulate ration of the parity factor and the tweak multiplied +// by the parity factor. The xOnly bool specifies if this is to be an x-only +// tweak or not. +func tweakKey(keyJ btcec.JacobianPoint, parityAcc btcec.ModNScalar, tweak [32]byte, + tweakAcc btcec.ModNScalar, + xOnly bool) (btcec.JacobianPoint, btcec.ModNScalar, btcec.ModNScalar, error) { + + // First we'll compute the new parity factor for this key. If the key has + // an odd y coordinate (not even), then we'll need to negate it (multiply + // by -1 mod n, in this case). + var parityFactor btcec.ModNScalar + if xOnly && !hasEvenY(keyJ) { + parityFactor.SetInt(1).Negate() + } else { + parityFactor.SetInt(1) + } + + // Next, map the tweak into a mod n integer so we can use it for + // manipulations below. + tweakInt := new(btcec.ModNScalar) + overflows := tweakInt.SetBytes(&tweak) + if overflows == 1 { + return keyJ, parityAcc, tweakAcc, ErrTweakedKeyOverflows + } + + // Next, we'll compute: Q_i = g*Q + t*G, where g is our parityFactor and t + // is the tweakInt above. We'll space things out a bit to make it easier to + // follow. + // + // First compute t*G: + var tweakedGenerator btcec.JacobianPoint + btcec.ScalarBaseMultNonConst(tweakInt, &tweakedGenerator) + + // Next compute g*Q: + btcec.ScalarMultNonConst(&parityFactor, &keyJ, &keyJ) + + // Finally add both of them together to get our final + // tweaked point. + btcec.AddNonConst(&tweakedGenerator, &keyJ, &keyJ) + + // As a sanity check, make sure that we didn't just end up with the + // point at infinity. + if keyJ == infinityPoint { + return keyJ, parityAcc, tweakAcc, ErrTweakedKeyIsInfinity + } + + // As a final wrap up step, we'll accumulate the parity + // factor and also this tweak into the final set of accumulators. + parityAcc.Mul(&parityFactor) + tweakAcc.Mul(&parityFactor).Add(tweakInt) + + return keyJ, parityAcc, tweakAcc, nil +} + +// AggregateKey is a final aggregated key along with a possible version of the +// key without any tweaks applied. +type AggregateKey struct { + // FinalKey is the final aggregated key which may include one or more + // tweaks applied to it. + FinalKey *btcec.PublicKey + + // PreTweakedKey is the aggregated *before* any tweaks have been + // applied. This should be used as the internal key in taproot + // contexts. + PreTweakedKey *btcec.PublicKey +} + +// AggregateKeys takes a list of possibly unsorted keys and returns a single +// aggregated key as specified by the musig2 key aggregation algorithm. A nil +// value can be passed for keyHash, which causes this function to re-derive it. +// In addition to the combined public key, the parity accumulator and the tweak +// accumulator are returned as well. +func AggregateKeys(keys []*btcec.PublicKey, sort bool, + keyOpts ...KeyAggOption) ( + *AggregateKey, *btcec.ModNScalar, *btcec.ModNScalar, error) { + + // First, parse the set of optional signing options. + opts := defaultKeyAggOptions() + for _, option := range keyOpts { + option(opts) + } + + // Sort the set of public key so we know we're working with them in + // sorted order for all the routines below. + if sort { + keys = sortKeys(keys) + } + + // The caller may provide the hash of all the keys as an optimization + // during signing, as it already needs to be computed. + if opts.keyHash == nil { + opts.keyHash = keyHashFingerprint(keys, sort) + } + + // A caller may also specify the unique key index themselves so we + // don't need to re-compute it. + if opts.uniqueKeyIndex == nil { + idx := secondUniqueKeyIndex(keys, sort) + opts.uniqueKeyIndex = &idx + } + + // For each key, we'll compute the intermediate blinded key: a_i*P_i, + // where a_i is the aggregation coefficient for that key, and P_i is + // the key itself, then accumulate that (addition) into the main final + // key: P = P_1 + P_2 ... P_N. + var finalKeyJ btcec.JacobianPoint + for _, key := range keys { + // Port the key over to Jacobian coordinates as we need it in + // this format for the routines below. + var keyJ btcec.JacobianPoint + key.AsJacobian(&keyJ) + + // Compute the aggregation coefficient for the key, then + // multiply it by the key itself: P_i' = a_i*P_i. + var tweakedKeyJ btcec.JacobianPoint + a := aggregationCoefficient( + keys, key, opts.keyHash, *opts.uniqueKeyIndex, + ) + btcec.ScalarMultNonConst(a, &keyJ, &tweakedKeyJ) + + // Finally accumulate this into the final key in an incremental + // fashion. + btcec.AddNonConst(&finalKeyJ, &tweakedKeyJ, &finalKeyJ) + } + + // We'll copy over the key at this point, since this represents the + // aggregated key before any tweaks have been applied. This'll be used + // as the internal key for script path proofs. + finalKeyJ.ToAffine() + combinedKey := btcec.NewPublicKey(&finalKeyJ.X, &finalKeyJ.Y) + + // At this point, if this is a taproot tweak, then we'll modify the + // base tweak value to use the BIP 341 tweak value. + if opts.taprootTweak { + // Emulate the same behavior as txscript.ComputeTaprootOutputKey + // which only operates on the x-only public key. + key, _ := schnorr.ParsePubKey(schnorr.SerializePubKey( + combinedKey, + )) + + // We only use the actual tweak bytes if we're not committing + // to a BIP-0086 key only spend output. Otherwise, we just + // commit to the internal key and an empty byte slice as the + // root hash. + tweakBytes := []byte{} + if !opts.bip86Tweak { + tweakBytes = opts.tweaks[0].Tweak[:] + } + + // Compute the taproot key tagged hash of: + // h_tapTweak(internalKey || scriptRoot). We only do this for + // the first one, as you can only specify a single tweak when + // using the taproot mode with this API. + tapTweakHash := chainhash.TaggedHash( + chainhash.TagTapTweak, schnorr.SerializePubKey(key), + tweakBytes, + ) + opts.tweaks[0].Tweak = *tapTweakHash + } + + var ( + err error + tweakAcc btcec.ModNScalar + parityAcc btcec.ModNScalar + ) + parityAcc.SetInt(1) + + // In this case we have a set of tweaks, so we'll incrementally apply + // each one, until we have our final tweaked key, and the related + // accumulators. + for i := 1; i <= len(opts.tweaks); i++ { + finalKeyJ, parityAcc, tweakAcc, err = tweakKey( + finalKeyJ, parityAcc, opts.tweaks[i-1].Tweak, tweakAcc, + opts.tweaks[i-1].IsXOnly, + ) + if err != nil { + return nil, nil, nil, err + } + } + + finalKeyJ.ToAffine() + finalKey := btcec.NewPublicKey(&finalKeyJ.X, &finalKeyJ.Y) + + return &AggregateKey{ + PreTweakedKey: combinedKey, + FinalKey: finalKey, + }, &parityAcc, &tweakAcc, nil +} diff --git a/libwallet/musig2v040/nonces.go b/libwallet/musig2v040/nonces.go new file mode 100644 index 00000000..96b8a147 --- /dev/null +++ b/libwallet/musig2v040/nonces.go @@ -0,0 +1,324 @@ +// Copyright 2013-2022 The btcsuite developers + +package musig2v040 + +import ( + "bytes" + "crypto/rand" + "crypto/sha256" + "io" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/btcsuite/btcd/chaincfg/chainhash" +) + +var ( + // NonceAuxTag is the tag used to optionally mix in the secret key with + // the set of aux randomness. + NonceAuxTag = []byte("MuSig/aux") + + // NonceGenTag is used to generate the value (from a set of required an + // optional field) that will be used as the part of the secret nonce. + NonceGenTag = []byte("MuSig/nonce") +) + +// secNonceToPubNonce takes our two secrete nonces, and produces their two +// corresponding EC points, serialized in compressed format. +func secNonceToPubNonce(secNonce [musig2.SecNonceSize]byte) [musig2.PubNonceSize]byte { + var k1Mod, k2Mod btcec.ModNScalar + k1Mod.SetByteSlice(secNonce[:btcec.PrivKeyBytesLen]) + k2Mod.SetByteSlice(secNonce[btcec.PrivKeyBytesLen:]) + + var r1, r2 btcec.JacobianPoint + btcec.ScalarBaseMultNonConst(&k1Mod, &r1) + btcec.ScalarBaseMultNonConst(&k2Mod, &r2) + + // Next, we'll convert the key in jacobian format to a normal public + // key expressed in affine coordinates. + r1.ToAffine() + r2.ToAffine() + r1Pub := btcec.NewPublicKey(&r1.X, &r1.Y) + r2Pub := btcec.NewPublicKey(&r2.X, &r2.Y) + + var pubNonce [musig2.PubNonceSize]byte + + // The public nonces are serialized as: R1 || R2, where both keys are + // serialized in compressed format. + copy(pubNonce[:], r1Pub.SerializeCompressed()) + copy( + pubNonce[btcec.PubKeyBytesLenCompressed:], + r2Pub.SerializeCompressed(), + ) + + return pubNonce +} + +// NonceGenOption is a function option that allows callers to modify how nonce +// generation happens. +type NonceGenOption func(*nonceGenOpts) + +// nonceGenOpts is the set of options that control how nonce generation +// happens. +type nonceGenOpts struct { + // randReader is what we'll use to generate a set of random bytes. If + // unspecified, then the normal crypto/rand rand.Read method will be + // used in place. + randReader io.Reader + + // secretKey is an optional argument that's used to further augment the + // generated nonce by xor'ing it with this secret key. + secretKey []byte + + // combinedKey is an optional argument that if specified, will be + // combined along with the nonce generation. + combinedKey []byte + + // msg is an optional argument that will be mixed into the nonce + // derivation algorithm. + msg []byte + + // auxInput is an optional argument that will be mixed into the nonce + // derivation algorithm. + auxInput []byte +} + +// cryptoRandAdapter is an adapter struct that allows us to pass in the package +// level Read function from crypto/rand into a context that accepts an +// io.Reader. +type cryptoRandAdapter struct { +} + +// Read implements the io.Reader interface for the crypto/rand package. By +// default, we always use the crypto/rand reader, but the caller is able to +// specify their own generation, which can be useful for deterministic tests. +func (c *cryptoRandAdapter) Read(p []byte) (n int, err error) { + return rand.Read(p) +} + +// defaultNonceGenOpts returns the default set of nonce generation options. +func defaultNonceGenOpts() *nonceGenOpts { + return &nonceGenOpts{ + randReader: &cryptoRandAdapter{}, + } +} + +// WithCustomRand allows a caller to use a custom random number generator in +// place for crypto/rand. This should only really be used to generate +// determinstic tests. +func WithCustomRand(r io.Reader) NonceGenOption { + return func(o *nonceGenOpts) { + o.randReader = r + } +} + +// WithNonceSecretKeyAux allows a caller to optionally specify a secret key +// that should be used to augment the randomness used to generate the nonces. +func WithNonceSecretKeyAux(secKey *btcec.PrivateKey) NonceGenOption { + return func(o *nonceGenOpts) { + o.secretKey = secKey.Serialize() + } +} + +// WithNonceCombinedKeyAux allows a caller to optionally specify the combined +// key used in this signing session to further augment the randomness used to +// generate nonces. +func WithNonceCombinedKeyAux(combinedKey *btcec.PublicKey) NonceGenOption { + return func(o *nonceGenOpts) { + o.combinedKey = schnorr.SerializePubKey(combinedKey) + } +} + +// WithNonceMessageAux allows a caller to optionally specify a message to be +// mixed into the randomness generated to create the nonce. +func WithNonceMessageAux(msg [32]byte) NonceGenOption { + return func(o *nonceGenOpts) { + o.msg = msg[:] + } +} + +// WithNonceAuxInput is a set of auxiliary randomness, similar to BIP 340 that +// can be used to further augment the nonce generation process. +func WithNonceAuxInput(aux []byte) NonceGenOption { + return func(o *nonceGenOpts) { + o.auxInput = aux + } +} + +// genNonceAuxBytes writes out the full byte string used to derive a secret +// nonce based on some initial randomness as well as the series of optional +// fields. The byte string used for derivation is: +// - let seed = tagged_hash( +// "MuSig/nonce", +// rand || len(aggpk) || aggpk || len(m) || m || len(in) || in +// ) +// - return sha256(seed || i) +// +// where i is the ith secret nonce being generated. +// +// Muun only provides the rand parameter as sessionId. All other parameters are encoded as if they were len=0 +func genNonceAuxBytes(rand []byte, i int) ([]byte, error) { + var w bytes.Buffer + + // First, write out the randomness generated in the prior step. + if _, err := w.Write(rand); err != nil { + return nil, err + } + + // write byte 0 for `sec_key` + if err := w.WriteByte(0); err != nil { + return nil, err + } + + // write byte 0 for `aggpk` + if err := w.WriteByte(0); err != nil { + return nil, err + } + + // write byte 0 for `message` + if err := w.WriteByte(0); err != nil { + return nil, err + } + + // write byte 0 for `extra_input32` + if err := w.WriteByte(0); err != nil { + return nil, err + } + + seed := chainhash.TaggedHash([]byte("MuSig/nonce"), w.Bytes()) + + h := sha256.New() + // write the seed to the buffer + if _, err := h.Write(seed.CloneBytes()); err != nil { + return nil, err + } + + // Next we'll write out the interaction/index number which will + // uniquely generate two nonces given the rest of the possibly static + // parameters. + ith := make([]byte, 1) + ith[0] = uint8(i) + + if _, err := h.Write(ith); err != nil { + return nil, err + } + + return h.Sum(nil), nil +} + +// Custom implementation of GenNonces to produce a bit-to-bit copy of the secp256k1_zkp +// All other parameters are ignored. +// +// Pseudo algorithm (|| means byte concat) +// +// let seed = TaggedHash("MuSig/nonce", sessionId || 0 || 0 || 0 || 0) +// let k = [sha256(seed || 0), sha256(seed || 1)] +// let r = k*G +// return toPublicKeyFormat(r) +func GenNonces(options ...NonceGenOption) (*musig2.Nonces, error) { + opts := defaultNonceGenOpts() + for _, opt := range options { + opt(opts) + } + + // First, we'll start out by generating 32 random bytes drawn from our + // CSPRNG. + var randBytes [32]byte + if _, err := opts.randReader.Read(randBytes[:]); err != nil { + return nil, err + } + + // Using our randomness and the set of optional params, generate our + // two secret nonces: k1 and k2. + k1, err := genNonceAuxBytes(randBytes[:], 0) + if err != nil { + return nil, err + } + k2, err := genNonceAuxBytes(randBytes[:], 1) + if err != nil { + return nil, err + } + + var k1Mod, k2Mod btcec.ModNScalar + k1Mod.SetBytes((*[32]byte)(k1)) + k2Mod.SetBytes((*[32]byte)(k2)) + + // The secret nonces are serialized as the concatenation of the two 32 + // byte secret nonce values. + var nonces musig2.Nonces + k1Mod.PutBytesUnchecked(nonces.SecNonce[:]) + k2Mod.PutBytesUnchecked(nonces.SecNonce[btcec.PrivKeyBytesLen:]) + + // Next, we'll generate R_1 = k_1*G and R_2 = k_2*G. Along the way we + // need to map our nonce values into mod n scalars so we can work with + // the btcec API. + nonces.PubNonce = secNonceToPubNonce(nonces.SecNonce) + + return &nonces, nil +} + +// AggregateNonces aggregates the set of a pair of public nonces for each party +// into a single aggregated nonces to be used for multi-signing. +func AggregateNonces(pubNonces [][musig2.PubNonceSize]byte) ([musig2.PubNonceSize]byte, error) { + // combineNonces is a helper function that aggregates (adds) up a + // series of nonces encoded in compressed format. It uses a slicing + // function to extra 33 bytes at a time from the packed 2x public + // nonces. + type nonceSlicer func([musig2.PubNonceSize]byte) []byte + combineNonces := func(slicer nonceSlicer) (btcec.JacobianPoint, error) { + // Convert the set of nonces into jacobian coordinates we can + // use to accumulate them all into each other. + pubNonceJs := make([]*btcec.JacobianPoint, len(pubNonces)) + for i, pubNonceBytes := range pubNonces { + // Using the slicer, extract just the bytes we need to + // decode. + var nonceJ btcec.JacobianPoint + + nonceJ, err := btcec.ParseJacobian(slicer(pubNonceBytes)) + if err != nil { + return btcec.JacobianPoint{}, err + } + + pubNonceJs[i] = &nonceJ + } + + // Now that we have the set of complete nonces, we'll aggregate + // them: R = R_i + R_i+1 + ... + R_i+n. + var aggregateNonce btcec.JacobianPoint + for _, pubNonceJ := range pubNonceJs { + btcec.AddNonConst( + &aggregateNonce, pubNonceJ, &aggregateNonce, + ) + } + + aggregateNonce.ToAffine() + return aggregateNonce, nil + } + + // The final nonce public nonce is actually two nonces, one that + // aggregate the first nonce of all the parties, and the other that + // aggregates the second nonce of all the parties. + var finalNonce [musig2.PubNonceSize]byte + combinedNonce1, err := combineNonces(func(n [musig2.PubNonceSize]byte) []byte { + return n[:btcec.PubKeyBytesLenCompressed] + }) + if err != nil { + return finalNonce, err + } + + combinedNonce2, err := combineNonces(func(n [musig2.PubNonceSize]byte) []byte { + return n[btcec.PubKeyBytesLenCompressed:] + }) + if err != nil { + return finalNonce, err + } + + copy(finalNonce[:], btcec.JacobianToByteSlice(combinedNonce1)) + copy( + finalNonce[btcec.PubKeyBytesLenCompressed:], + btcec.JacobianToByteSlice(combinedNonce2), + ) + + return finalNonce, nil +} diff --git a/libwallet/musig2v040/sign.go b/libwallet/musig2v040/sign.go new file mode 100644 index 00000000..2bb8a618 --- /dev/null +++ b/libwallet/musig2v040/sign.go @@ -0,0 +1,700 @@ +// Copyright 2013-2022 The btcsuite developers + +package musig2v040 + +import ( + "bytes" + "crypto/sha256" + "fmt" + "io" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/btcsuite/btcd/chaincfg/chainhash" + secp "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +var ( + // ChallengeHashTag is the tag used to construct the challenge hash + ChallengeHashTag = []byte("BIP0340/challenge") + + // ErrNoncePointAtInfinity is returned if during signing, the fully + // combined public nonce is the point at infinity. + ErrNoncePointAtInfinity = fmt.Errorf("signing nonce is the infinity " + + "point") + + // ErrPrivKeyZero is returned when the private key for signing is + // actually zero. + ErrPrivKeyZero = fmt.Errorf("priv key is zero") + + // ErrPartialSigInvalid is returned when a partial is found to be + // invalid. + ErrPartialSigInvalid = fmt.Errorf("partial signature is invalid") + + // ErrSecretNonceZero is returned when a secret nonce is passed in a + // zero. + ErrSecretNonceZero = fmt.Errorf("secret nonce is blank") +) + +// infinityPoint is the jacobian representation of the point at infinity. +var infinityPoint btcec.JacobianPoint + +// PartialSignature reprints a partial (s-only) musig2 multi-signature. This +// isn't a valid schnorr signature by itself, as it needs to be aggregated +// along with the other partial signatures to be completed. +type PartialSignature struct { + S *btcec.ModNScalar + + R *btcec.PublicKey +} + +// NewPartialSignature returns a new instances of the partial sig struct. +func NewPartialSignature(s *btcec.ModNScalar, + r *btcec.PublicKey) PartialSignature { + + return PartialSignature{ + S: s, + R: r, + } +} + +// Encode writes a serialized version of the partial signature to the passed +// io.Writer +func (p *PartialSignature) Encode(w io.Writer) error { + var sBytes [32]byte + p.S.PutBytes(&sBytes) + + if _, err := w.Write(sBytes[:]); err != nil { + return err + } + + return nil +} + +// Decode attempts to parse a serialized PartialSignature stored in the passed +// io reader. +func (p *PartialSignature) Decode(r io.Reader) error { + p.S = new(btcec.ModNScalar) + + var sBytes [32]byte + if _, err := io.ReadFull(r, sBytes[:]); err != nil { + return nil + } + + overflows := p.S.SetBytes(&sBytes) + if overflows == 1 { + return ErrPartialSigInvalid + } + + return nil +} + +// SignOption is a functional option argument that allows callers to modify the +// way we generate musig2 schnorr signatures. +type SignOption func(*signOptions) + +// signOptions houses the set of functional options that can be used to modify +// the method used to generate the musig2 partial signature. +type signOptions struct { + // fastSign determines if we'll skip the check at the end of the + // routine where we attempt to verify the produced signature. + fastSign bool + + // sortKeys determines if the set of keys should be sorted before doing + // key aggregation. + sortKeys bool + + // tweaks specifies a series of tweaks to be applied to the aggregated + // public key, which also partially carries over into the signing + // process. + tweaks []KeyTweakDesc + + // taprootTweak specifies a taproot specific tweak. of the tweaks + // specified above. Normally we'd just apply the raw 32 byte tweak, but + // for taproot, we first need to compute the aggregated key before + // tweaking, and then use it as the internal key. This is required as + // the taproot tweak also commits to the public key, which in this case + // is the aggregated key before the tweak. + taprootTweak []byte + + // bip86Tweak specifies that the taproot tweak should be done in a BIP + // 86 style, where we don't expect an actual tweak and instead just + // commit to the public key itself. + bip86Tweak bool +} + +// defaultSignOptions returns the default set of signing operations. +func defaultSignOptions() *signOptions { + return &signOptions{} +} + +// WithFastSign forces signing to skip the extra verification step at the end. +// Performance sensitive applications may opt to use this option to speed up +// the signing operation. +func WithFastSign() SignOption { + return func(o *signOptions) { + o.fastSign = true + } +} + +// WithSortedKeys determines if the set of signing public keys are to be sorted +// or not before doing key aggregation. +func WithSortedKeys() SignOption { + return func(o *signOptions) { + o.sortKeys = true + } +} + +// WithTweaks determines if the aggregated public key used should apply a +// series of tweaks before key aggregation. +func WithTweaks(tweaks ...KeyTweakDesc) SignOption { + return func(o *signOptions) { + o.tweaks = tweaks + } +} + +// WithTaprootSignTweak allows a caller to specify a tweak that should be used +// in a bip 340 manner when signing. This differs from WithTweaks as the tweak +// will be assumed to always be x-only and the intermediate aggregate key +// before tweaking will be used to generate part of the tweak (as the taproot +// tweak also commits to the internal key). +// +// This option should be used in the taproot context to create a valid +// signature for the keypath spend for taproot, when the output key is actually +// committing to a script path, or some other data. +func WithTaprootSignTweak(scriptRoot []byte) SignOption { + return func(o *signOptions) { + o.taprootTweak = scriptRoot + } +} + +// WithBip86SignTweak allows a caller to specify a tweak that should be used in +// a bip 340 manner when signing, factoring in BIP 86 as well. This differs +// from WithTaprootSignTweak as no true script root will be committed to, +// instead we just commit to the internal key. +// +// This option should be used in the taproot context to create a valid +// signature for the keypath spend for taproot, when the output key was +// generated using BIP 86. +func WithBip86SignTweak() SignOption { + return func(o *signOptions) { + o.bip86Tweak = true + } +} + +// Sign generates a musig2 partial signature given the passed key set, secret +// nonce, public nonce, and private keys. This method returns an error if the +// generated nonces are either too large, or end up mapping to the point at +// infinity. +func Sign(secNonce [musig2.SecNonceSize]byte, privKey *btcec.PrivateKey, + combinedNonce [musig2.PubNonceSize]byte, pubKeys []*btcec.PublicKey, + msg [32]byte, signOpts ...SignOption) (*PartialSignature, error) { + + // First, parse the set of optional signing options. + opts := defaultSignOptions() + for _, option := range signOpts { + option(opts) + } + + // Compute the hash of all the keys here as we'll need it do aggregate + // the keys and also at the final step of signing. + keysHash := keyHashFingerprint(pubKeys, opts.sortKeys) + uniqueKeyIndex := secondUniqueKeyIndex(pubKeys, opts.sortKeys) + + keyAggOpts := []KeyAggOption{ + WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex), + } + switch { + case opts.bip86Tweak: + keyAggOpts = append( + keyAggOpts, WithBIP86KeyTweak(), + ) + case opts.taprootTweak != nil: + keyAggOpts = append( + keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak), + ) + case len(opts.tweaks) != 0: + keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...)) + } + + // Next we'll construct the aggregated public key based on the set of + // signers. + combinedKey, parityAcc, _, err := AggregateKeys( + pubKeys, opts.sortKeys, keyAggOpts..., + ) + if err != nil { + return nil, err + } + + // Next we'll compute the value b, that blinds our second public + // nonce: + // * b = h(combinedNonce || combinedKey || m). h==sha256 + var ( + nonceBlinder btcec.ModNScalar + ) + + nonceMsgBuf := sha256.New() + nonceMsgBuf.Write(combinedNonce[:]) + nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey)) + nonceMsgBuf.Write(msg[:]) + nonceBlindHash := nonceMsgBuf.Sum(nil) + nonceBlinder.SetByteSlice(nonceBlindHash[:]) + + // Next, we'll parse the public nonces into R1 and R2. + r1J, err := btcec.ParseJacobian( + combinedNonce[:btcec.PubKeyBytesLenCompressed], + ) + if err != nil { + return nil, err + } + r2J, err := btcec.ParseJacobian( + combinedNonce[btcec.PubKeyBytesLenCompressed:], + ) + if err != nil { + return nil, err + } + + // With our nonce blinding value, we'll now combine both the public + // nonces, using the blinding factor to tweak the second nonce: + // * R = R_1 + b*R_2 + var nonce btcec.JacobianPoint + btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J) + btcec.AddNonConst(&r1J, &r2J, &nonce) + + // If the combined nonce it eh point at infinity, then we'll bail out. + if nonce == infinityPoint { + G := btcec.Generator() + G.AsJacobian(&nonce) + } + + // Next we'll parse out our two secret nonces, which we'll be using in + // the core signing process below. + var k1, k2 btcec.ModNScalar + k1.SetByteSlice(secNonce[:btcec.PrivKeyBytesLen]) + k2.SetByteSlice(secNonce[btcec.PrivKeyBytesLen:]) + + if k1.IsZero() || k2.IsZero() { + return nil, ErrSecretNonceZero + } + + nonce.ToAffine() + + nonceKey := btcec.NewPublicKey(&nonce.X, &nonce.Y) + + // If the nonce R has an odd y coordinate, then we'll negate both our + // secret nonces. + if nonce.Y.IsOdd() { + k1.Negate() + k2.Negate() + } + + privKeyScalar := privKey.Key + if privKeyScalar.IsZero() { + return nil, ErrPrivKeyZero + } + + pubKey := privKey.PubKey() + pubKeyYIsOdd := func() bool { + pubKeyBytes := pubKey.SerializeCompressed() + return pubKeyBytes[0] == secp.PubKeyFormatCompressedOdd + }() + combinedKeyYIsOdd := func() bool { + combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed() + return combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd + }() + + // Next we'll compute our two parity factors for Q the combined public + // key, and P, the public key we're signing with. If the keys are odd, + // then we'll negate them. + parityCombinedKey := new(btcec.ModNScalar).SetInt(1) + paritySignKey := new(btcec.ModNScalar).SetInt(1) + if combinedKeyYIsOdd { + parityCombinedKey.Negate() + } + if pubKeyYIsOdd { + paritySignKey.Negate() + } + + // Before we sign below, we'll multiply by our various parity factors + // to ensure that the signing key is properly negated (if necessary): + // * d = gv⋅gaccv⋅gp⋅d' + privKeyScalar.Mul(parityCombinedKey).Mul(paritySignKey).Mul(parityAcc) + + // Next we'll create the challenge hash that commits to the combined + // nonce, combined public key and also the message: + // * e = H(tag=ChallengeHashTag, R || Q || m) mod n + var challengeMsg bytes.Buffer + challengeMsg.Write(schnorr.SerializePubKey(nonceKey)) + challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey)) + challengeMsg.Write(msg[:]) + challengeBytes := chainhash.TaggedHash( + ChallengeHashTag, challengeMsg.Bytes(), + ) + var e btcec.ModNScalar + e.SetByteSlice(challengeBytes[:]) + + // Next, we'll compute a, our aggregation coefficient for the key that + // we're signing with. + a := aggregationCoefficient(pubKeys, pubKey, keysHash, uniqueKeyIndex) + + // With mu constructed, we can finally generate our partial signature + // as: s = (k1_1 + b*k_2 + e*a*d) mod n. + s := new(btcec.ModNScalar) + s.Add(&k1).Add(k2.Mul(&nonceBlinder)).Add(e.Mul(a).Mul(&privKeyScalar)) + + sig := NewPartialSignature(s, nonceKey) + + // If we're not in fast sign mode, then we'll also validate our partial + // signature. + if !opts.fastSign { + pubNonce := secNonceToPubNonce(secNonce) + sigValid := sig.Verify( + pubNonce, combinedNonce, pubKeys, pubKey, msg, + signOpts..., + ) + if !sigValid { + return nil, fmt.Errorf("sig is invalid") + } + } + + return &sig, nil +} + +// Verify implements partial signature verification given the public nonce for +// the signer, aggregate nonce, signer set and finally the message being +// signed. +func (p *PartialSignature) Verify(pubNonce [musig2.PubNonceSize]byte, + combinedNonce [musig2.PubNonceSize]byte, keySet []*btcec.PublicKey, + signingKey *btcec.PublicKey, msg [32]byte, signOpts ...SignOption) bool { + + pubKey := schnorr.SerializePubKey(signingKey) + + return verifyPartialSig( + p, pubNonce, combinedNonce, keySet, pubKey, msg, signOpts..., + ) == nil +} + +// verifyPartialSig attempts to verify a partial schnorr signature given the +// necessary parameters. This is the internal version of Verify that returns +// detailed errors. signed. +func verifyPartialSig(partialSig *PartialSignature, pubNonce [musig2.PubNonceSize]byte, + combinedNonce [musig2.PubNonceSize]byte, keySet []*btcec.PublicKey, + pubKey []byte, msg [32]byte, signOpts ...SignOption) error { + + opts := defaultSignOptions() + for _, option := range signOpts { + option(opts) + } + + // First we'll map the internal partial signature back into something + // we can manipulate. + s := partialSig.S + + // Next we'll parse out the two public nonces into something we can + // use. + // + + // Compute the hash of all the keys here as we'll need it do aggregate + // the keys and also at the final step of verification. + keysHash := keyHashFingerprint(keySet, opts.sortKeys) + uniqueKeyIndex := secondUniqueKeyIndex(keySet, opts.sortKeys) + + keyAggOpts := []KeyAggOption{ + WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex), + } + switch { + case opts.bip86Tweak: + keyAggOpts = append( + keyAggOpts, WithBIP86KeyTweak(), + ) + case opts.taprootTweak != nil: + keyAggOpts = append( + keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak), + ) + case len(opts.tweaks) != 0: + keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...)) + } + + // Next we'll construct the aggregated public key based on the set of + // signers. + combinedKey, parityAcc, _, err := AggregateKeys( + keySet, opts.sortKeys, keyAggOpts..., + ) + if err != nil { + return err + } + + // Next we'll compute the value b, that blinds our second public + // nonce: + // * b = h(combinedNonce || combinedKey || m). h==sha256 + var ( + nonceBlinder btcec.ModNScalar + ) + nonceMsgBuf := sha256.New() + nonceMsgBuf.Write(combinedNonce[:]) + nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey)) + nonceMsgBuf.Write(msg[:]) + nonceBlindHash := nonceMsgBuf.Sum(nil) + nonceBlinder.SetByteSlice(nonceBlindHash[:]) + + r1J, err := btcec.ParseJacobian( + combinedNonce[:btcec.PubKeyBytesLenCompressed], + ) + if err != nil { + return err + } + r2J, err := btcec.ParseJacobian( + combinedNonce[btcec.PubKeyBytesLenCompressed:], + ) + if err != nil { + return err + } + + // With our nonce blinding value, we'll now combine both the public + // nonces, using the blinding factor to tweak the second nonce: + // * R = R_1 + b*R_2 + + var nonce btcec.JacobianPoint + btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J) + btcec.AddNonConst(&r1J, &r2J, &nonce) + + // Next, we'll parse out the set of public nonces this signer used to + // generate the signature. + pubNonce1J, err := btcec.ParseJacobian( + pubNonce[:btcec.PubKeyBytesLenCompressed], + ) + if err != nil { + return err + } + pubNonce2J, err := btcec.ParseJacobian( + pubNonce[btcec.PubKeyBytesLenCompressed:], + ) + if err != nil { + return err + } + + // If the nonce is the infinity point we set it to the Generator. + if nonce == infinityPoint { + btcec.GeneratorJacobian(&nonce) + } else { + nonce.ToAffine() + } + + // We'll perform a similar aggregation and blinding operator as we did + // above for the combined nonces: R' = R_1' + b*R_2'. + var pubNonceJ btcec.JacobianPoint + + btcec.ScalarMultNonConst(&nonceBlinder, &pubNonce2J, &pubNonce2J) + btcec.AddNonConst(&pubNonce1J, &pubNonce2J, &pubNonceJ) + + pubNonceJ.ToAffine() + + // If the combined nonce used in the challenge hash has an odd y + // coordinate, then we'll negate our final public nonce. + if nonce.Y.IsOdd() { + pubNonceJ.Y.Negate(1) + pubNonceJ.Y.Normalize() + } + + // Next we'll create the challenge hash that commits to the combined + // nonce, combined public key and also the message: + // * e = H(tag=ChallengeHashTag, R || Q || m) mod n + var challengeMsg bytes.Buffer + challengeMsg.Write(schnorr.SerializePubKey(btcec.NewPublicKey( + &nonce.X, &nonce.Y, + ))) + challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey)) + challengeMsg.Write(msg[:]) + challengeBytes := chainhash.TaggedHash( + ChallengeHashTag, challengeMsg.Bytes(), + ) + var e btcec.ModNScalar + e.SetByteSlice(challengeBytes[:]) + + signingKey, err := schnorr.ParsePubKey(pubKey) + if err != nil { + return err + } + + // Next, we'll compute a, our aggregation coefficient for the key that + // we're signing with. + a := aggregationCoefficient(keySet, signingKey, keysHash, uniqueKeyIndex) + + // If the combined key has an odd y coordinate, then we'll negate + // parity factor for the signing key. + paritySignKey := new(btcec.ModNScalar).SetInt(1) + combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed() + if combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd { + paritySignKey.Negate() + } + + // Next, we'll construct the final parity factor by multiplying the + // sign key parity factor with the accumulated parity factor for all + // the keys. + finalParityFactor := paritySignKey.Mul(parityAcc) + + // Now we'll multiply the parity factor by our signing key, which'll + // take care of the amount of negation needed. + var signKeyJ btcec.JacobianPoint + signingKey.AsJacobian(&signKeyJ) + btcec.ScalarMultNonConst(finalParityFactor, &signKeyJ, &signKeyJ) + + // In the final set, we'll check that: s*G == R' + e*a*P. + var sG, rP btcec.JacobianPoint + btcec.ScalarBaseMultNonConst(s, &sG) + btcec.ScalarMultNonConst(e.Mul(a), &signKeyJ, &rP) + btcec.AddNonConst(&rP, &pubNonceJ, &rP) + + sG.ToAffine() + rP.ToAffine() + + if sG != rP { + return ErrPartialSigInvalid + } + + return nil +} + +// CombineOption is a functional option argument that allows callers to modify the +// way we combine musig2 schnorr signatures. +type CombineOption func(*combineOptions) + +// combineOptions houses the set of functional options that can be used to +// modify the method used to combine the musig2 partial signatures. +type combineOptions struct { + msg [32]byte + + combinedKey *btcec.PublicKey + + tweakAcc *btcec.ModNScalar +} + +// defaultCombineOptions returns the default set of signing operations. +func defaultCombineOptions() *combineOptions { + return &combineOptions{} +} + +// WithTweakedCombine is a functional option that allows callers to specify +// that the signature was produced using a tweaked aggregated public key. In +// order to properly aggregate the partial signatures, the caller must specify +// enough information to reconstruct the challenge, and also the final +// accumulated tweak value. +func WithTweakedCombine(msg [32]byte, keys []*btcec.PublicKey, + tweaks []KeyTweakDesc, sort bool) CombineOption { + + return func(o *combineOptions) { + combinedKey, _, tweakAcc, _ := AggregateKeys( + keys, sort, WithKeyTweaks(tweaks...), + ) + + o.msg = msg + o.combinedKey = combinedKey.FinalKey + o.tweakAcc = tweakAcc + } +} + +// WithTaprootTweakedCombine is similar to the WithTweakedCombine option, but +// assumes a BIP 341 context where the final tweaked key is to be used as the +// output key, where the internal key is the aggregated key pre-tweak. +// +// This option should be used over WithTweakedCombine when attempting to +// aggregate signatures for a top-level taproot keyspend, where the output key +// commits to a script root. +func WithTaprootTweakedCombine(msg [32]byte, keys []*btcec.PublicKey, + scriptRoot []byte, sort bool) CombineOption { + + return func(o *combineOptions) { + combinedKey, _, tweakAcc, _ := AggregateKeys( + keys, sort, WithTaprootKeyTweak(scriptRoot), + ) + + o.msg = msg + o.combinedKey = combinedKey.FinalKey + o.tweakAcc = tweakAcc + } +} + +// WithBip86TweakedCombine is similar to the WithTaprootTweakedCombine option, +// but assumes a BIP 341 + BIP 86 context where the final tweaked key is to be +// used as the output key, where the internal key is the aggregated key +// pre-tweak. +// +// This option should be used over WithTaprootTweakedCombine when attempting to +// aggregate signatures for a top-level taproot keyspend, where the output key +// was generated using BIP 86. +func WithBip86TweakedCombine(msg [32]byte, keys []*btcec.PublicKey, + sort bool) CombineOption { + + return func(o *combineOptions) { + combinedKey, _, tweakAcc, _ := AggregateKeys( + keys, sort, WithBIP86KeyTweak(), + ) + + o.msg = msg + o.combinedKey = combinedKey.FinalKey + o.tweakAcc = tweakAcc + } +} + +// CombineSigs combines the set of public keys given the final aggregated +// nonce, and the series of partial signatures for each nonce. +func CombineSigs(combinedNonce *btcec.PublicKey, + partialSigs []*PartialSignature, + combineOpts ...CombineOption) *schnorr.Signature { + + // First, parse the set of optional combine options. + opts := defaultCombineOptions() + for _, option := range combineOpts { + option(opts) + } + + // If signer keys and tweaks are specified, then we need to carry out + // some intermediate steps before we can combine the signature. + var tweakProduct *btcec.ModNScalar + if opts.combinedKey != nil && opts.tweakAcc != nil { + // Next, we'll construct the parity factor of the combined key, + // negating it if the combined key has an even y coordinate. + parityFactor := new(btcec.ModNScalar).SetInt(1) + combinedKeyBytes := opts.combinedKey.SerializeCompressed() + if combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd { + parityFactor.Negate() + } + + // Next we'll reconstruct e the challenge has based on the + // nonce and combined public key. + // * e = H(tag=ChallengeHashTag, R || Q || m) mod n + var challengeMsg bytes.Buffer + challengeMsg.Write(schnorr.SerializePubKey(combinedNonce)) + challengeMsg.Write(schnorr.SerializePubKey(opts.combinedKey)) + challengeMsg.Write(opts.msg[:]) + challengeBytes := chainhash.TaggedHash( + ChallengeHashTag, challengeMsg.Bytes(), + ) + var e btcec.ModNScalar + e.SetByteSlice(challengeBytes[:]) + + tweakProduct = new(btcec.ModNScalar).Set(&e) + tweakProduct.Mul(opts.tweakAcc).Mul(parityFactor) + } + + // Finally, the tweak factor also needs to be re-computed as well. + var combinedSig btcec.ModNScalar + for _, partialSig := range partialSigs { + combinedSig.Add(partialSig.S) + } + + // If the tweak product was set above, then we'll need to add the value + // at the very end in order to produce a valid signature under the + // final tweaked key. + if tweakProduct != nil { + combinedSig.Add(tweakProduct) + } + + // TODO(roasbeef): less verbose way to get the x coord... + var nonceJ btcec.JacobianPoint + combinedNonce.AsJacobian(&nonceJ) + nonceJ.ToAffine() + + return schnorr.NewSignature(&nonceJ.X, &combinedSig) +} diff --git a/libwallet/musig2v040/upstream-patch.patch b/libwallet/musig2v040/upstream-patch.patch new file mode 100644 index 00000000..516cc894 --- /dev/null +++ b/libwallet/musig2v040/upstream-patch.patch @@ -0,0 +1,677 @@ +libwallet/musig2v040/context.go --- 1/11 --- Go + 1 // Copyright (c) 2013-2022 The btcsuite develope 1 // Copyright (c) 2013-2022 The btcsuite develope + . rs . rs + 2 2 + 3 package musig2 3 package musig2v040 + 4 4 + 5 import ( 5 import ( + . 6 "errors" + 6 "fmt" 7 "fmt" + 7 8 + 8 "github.com/btcsuite/btcd/btcec/v2" 9 "github.com/btcsuite/btcd/btcec/v2" + 9 "github.com/btcsuite/btcd/btcec/v2/schnorr" 10 "github.com/btcsuite/btcd/btcec/v2/schnorr" + . 11 "github.com/btcsuite/btcd/btcec/v2/schnorr/m + . .. usig2" +10 ) 12 ) +11 13 +12 var ( 14 var ( + +libwallet/musig2v040/context.go --- 2/11 --- Go +87 89 // any operations. +88 90 shouldSort bool +89 .. +90 .. // sessionNonce will be populated if the earlyNonce option is true. +91 .. // After the first session is created, this nonce will be blanked out. +92 .. sessionNonce *Nonces +93 91 } +94 92 +95 93 // ContextOption is a functional option argument that allows callers to modify + +libwallet/musig2v040/context.go --- 3/11 --- Go +239 237 opts.keySet = make([]*btcec.PublicKey, 0, opts.numSigners) +240 238 opts.keySet = append(opts.keySet, pubKey) +241 ... +242 ... // If early nonce generation is specified, then we'll generate +243 ... // the nonce now to pass in to the session once all the callers +244 ... // are known. +245 ... if opts.earlyNonce { +246 ... ctx.sessionNonce, err = GenNonces() +247 ... if err != nil { +248 ... return nil, err +249 ... } +250 ... } +251 239 +252 240 default: +253 241 return nil, ErrSignersNotSpecified + +libwallet/musig2v040/context.go --- 4/11 --- Go +311 } 299 } +312 300 +313 // EarlySessionNonce returns the early session 301 // EarlySessionNonce returns the early session +... nonce, if available. ... nonce, if available. +314 func (c *Context) EarlySessionNonce() (*Nonces, 302 func (c *Context) EarlySessionNonce() (*musig2. +... error) { ... Nonces, error) { +315 if c.sessionNonce == nil { ... +316 return nil, ErrNoEarlyNonce 303 return nil, ErrNoEarlyNonce +317 } ... +318 ... +319 return c.sessionNonce, nil ... +320 } 304 } +321 305 +322 // RegisterSigner allows a caller to register a 306 // RegisterSigner allows a caller to register a + signer after the context has signer after the context has + +libwallet/musig2v040/context.go --- 5/11 --- Go +404 388 // sessionOptions houses the set of functional options that can be used to +405 389 // modify the musig2 signing protocol. +406 390 type sessionOptions struct { +407 391 externalNonce *musig2.Nonces +408 392 } +409 393 +410 394 // defaultSessionOptions returns the default session options. + +libwallet/musig2v040/context.go --- 6/11 --- Go +415 399 // WithPreGeneratedNonce allows a caller to start a session using a nonce +416 400 // they've generated themselves. This may be useful in protocols where all the +417 401 // signer keys may not be known before nonce exchange needs to occur. +418 402 func WithPreGeneratedNonce(nonce *musig2.Nonces) SessionOption { +419 403 return func(o *sessionOptions) { +420 404 o.externalNonce = nonce +421 405 } + +libwallet/musig2v040/context.go --- 7/11 --- Go +433 417 +434 418 ctx *Context +435 419 +436 420 localNonces *musig2.Nonces +437 421 +438 422 pubNonces [][musig2.PubNonceSize]byte +439 423 +440 424 combinedNonce *[musig2.PubNonceSize]byte +441 425 +442 426 msg [32]byte +443 427 + +libwallet/musig2v040/context.go --- 8/11 --- Go +464 448 +465 // If an early nonce was specified, then we 449 // If an early nonce was specified, then we +... 'll automatically add the ... 'll automatically add the +466 // corresponding session option for the cal 450 // corresponding session option for the cal +... ler. ... ler. +467 var localNonces *Nonces 451 var localNonces *musig2.Nonces +468 if c.sessionNonce != nil { ... +469 // Apply the early nonce to the session ... +... , and also blank out the ... +470 // session nonce on the context to ensu ... +... re it isn't ever re-used ... +471 // for another session. ... +472 localNonces = c.sessionNonce ... +473 c.sessionNonce = nil ... +474 } else if opts.externalNonce != nil { 452 if opts.externalNonce != nil { +475 // Otherwise if there's a custom nonce 453 // Otherwise if there's a custom nonce +... passed in via the ... passed in via the +476 // session options, then use that inste 454 // session options, then use that inste +... ad. ... ad. +477 localNonces = opts.externalNonce 455 localNonces = opts.externalNonce +478 } 456 } +479 457 +480 // Now that we know we have enough signers, 458 // Now that we know we have enough signers, +... we'll either use the caller ... we'll either use the caller +481 // specified nonce, or generate a fresh set 459 // specified nonce, or generate a fresh set +... . ... . +482 var err error ... +483 if localNonces == nil { 460 if localNonces == nil { +484 // At this point we need to generate a ... +... fresh nonce. We'll pass ... +485 // in some auxiliary information to str ... +... engthen the nonce ... +486 // generated. ... +487 localNonces, err = GenNonces( 461 return nil, errors.New("a pre-created n +... ... once must be passed onto the session") +488 WithNonceSecretKeyAux(c.signingKey) ... +... , ... +489 WithNonceCombinedKeyAux(c.combinedK ... +... ey.FinalKey), ... +490 ) ... +491 if err != nil { ... +492 return nil, err ... +493 } ... +494 } 462 } +495 463 +496 s := &Session{ 464 s := &Session{ +497 opts: opts, 465 opts: opts, +498 ctx: c, 466 ctx: c, +499 localNonces: localNonces, 467 localNonces: localNonces, +500 pubNonces: make([][PubNonceSize]byte, 468 pubNonces: make([][musig2.PubNonceSiz +... 0, c.opts.numSigners), ... e]byte, 0, c.opts.numSigners), +501 sigs: make([]*PartialSignature, 469 sigs: make([]*PartialSignature, +... 0, c.opts.numSigners), ... 0, c.opts.numSigners), +502 } 470 } +503 471 + +libwallet/musig2v040/context.go --- 9/11 --- Go +509 477 // PublicNonce returns the public nonce for a signer. This should be sent to +510 478 // other parties before signing begins, so they can compute the aggregated +511 479 // public nonce. +512 480 func (s *Session) PublicNonce() [musig2.PubNonceSize]byte { +513 481 return s.localNonces.PubNonce +514 482 } +515 483 + +libwallet/musig2v040/context.go --- 10/11 --- Go +522 490 // RegisterPubNonce should be called for each public nonce from the set of +523 491 // signers. This method returns true once all the public nonces have been +524 492 // accounted for. +525 493 func (s *Session) RegisterPubNonce(nonce [musig2.PubNonceSize]byte) (bool, error) { +526 494 // If we already have all the nonces, then this method was called too +527 495 // many times. +528 496 haveAllNonces := len(s.pubNonces) == s.ctx.opts.numSigners + +libwallet/musig2v040/context.go --- 11/11 --- Go +562 return nil, ErrSigningContextReuse 530 return nil, ErrSigningContextReuse +563 531 +564 // We also need to make sure we have the co 532 // We also need to make sure we have the co +... mbined nonce, otherwise this ... mbined nonce, otherwise this +565 // funciton was called too early. 533 // function was called too early. +566 case s.combinedNonce == nil: 534 case s.combinedNonce == nil: +567 return nil, ErrCombinedNonceUnavailable 535 return nil, ErrCombinedNonceUnavailable +568 } 536 } + +libwallet/musig2v040/keys.go --- 1/3 --- Go + 1 // Copyright 2013-2022 The btcsuite developers 1 // Copyright 2013-2022 The btcsuite developers + 2 2 + 3 package musig2 3 package musig2v040 + 4 4 + 5 import ( 5 import ( + 6 "bytes" 6 "bytes" + 7 "fmt" 7 "fmt" + 8 "sort" 8 "sort" + 9 9 + . 10 "github.com/btcsuite/btcd/btcec/v2" + . 11 "github.com/btcsuite/btcd/btcec/v2/schnorr" + . 12 "github.com/btcsuite/btcd/chaincfg/chainhash + . .. " +10 secp "github.com/decred/dcrd/dcrec/secp256k1 13 secp "github.com/decred/dcrd/dcrec/secp256k1 +.. /v4" .. /v4" +11 .. +12 "github.com/btcsuite/btcd/btcec/v2" .. +13 "github.com/btcsuite/btcd/btcec/v2/schnorr" .. +14 "github.com/btcsuite/btcd/chaincfg/chainhash .. +.. " .. +15 ) 14 ) +16 15 +17 var ( 16 var ( + +libwallet/musig2v040/keys.go --- 2/3 --- Go +72 // keyHashFingerprint computes the tagged hash o 71 // keyHashFingerprint computes the tagged hash o +.. f the series of (sorted) public .. f the series of (sorted) public +73 // keys passed as input. This is used to compute 72 // keys passed as input. This is used to compute +.. the aggregation coefficient .. the aggregation coefficient +74 // for each key. The final computation is: 73 // for each key. The final computation is: +75 // * H(tag=KeyAgg list, pk1 || pk2..) 74 // - H(tag=KeyAgg list, pk1 || pk2..) +76 func keyHashFingerprint(keys []*btcec.PublicKey, 75 func keyHashFingerprint(keys []*btcec.PublicKey, +.. sort bool) []byte { .. sort bool) []byte { +77 if sort { 76 if sort { +78 keys = sortKeys(keys) 77 keys = sortKeys(keys) + +libwallet/musig2v040/keys.go --- 3/3 --- Go +101 100 +102 // aggregationCoefficient computes the key aggr 101 // aggregationCoefficient computes the key aggr +... egation coefficient for the ... egation coefficient for the +103 // specified target key. The coefficient is com 102 // specified target key. The coefficient is com +... puted as: ... puted as: +104 // * H(tag=KeyAgg coefficient, keyHashFingerpr 103 // - H(tag=KeyAgg coefficient, keyHashFingerp +... int(pks) || pk) ... rint(pks) || pk) +105 func aggregationCoefficient(keySet []*btcec.Pub 104 func aggregationCoefficient(keySet []*btcec.Pub +... licKey, ... licKey, +106 targetKey *btcec.PublicKey, keysHash []byte 105 targetKey *btcec.PublicKey, keysHash []byte +... , ... , +107 secondKeyIdx int) *btcec.ModNScalar { 106 secondKeyIdx int) *btcec.ModNScalar { + +libwallet/musig2v040/nonces.go --- 1/8 --- Go + 1 // Copyright 2013-2022 The btcsuite developers 1 // Copyright 2013-2022 The btcsuite developers + 2 2 + 3 package musig2 3 package musig2v040 + 4 4 + 5 import ( 5 import ( + 6 "bytes" 6 "bytes" + 7 "crypto/rand" 7 "crypto/rand" + 8 "encoding/binary" 8 "crypto/sha256" + 9 "io" 9 "io" +10 10 +11 "github.com/btcsuite/btcd/btcec/v2" 11 "github.com/btcsuite/btcd/btcec/v2" +12 "github.com/btcsuite/btcd/btcec/v2/schnorr" 12 "github.com/btcsuite/btcd/btcec/v2/schnorr" +.. 13 "github.com/btcsuite/btcd/btcec/v2/schnorr/m +.. .. usig2" +13 "github.com/btcsuite/btcd/chaincfg/chainhash 14 "github.com/btcsuite/btcd/chaincfg/chainhash +.. " .. " +14 ) 15 ) +15 .. +16 const ( .. +17 // PubNonceSize is the size of the public no .. +.. nces. Each public nonce is .. +18 // serialized the full compressed encoding, .. +.. which uses 32 bytes for each .. +19 // nonce. .. +20 PubNonceSize = 66 .. +21 .. +22 // SecNonceSize is the size of the secret no .. +.. nces for musig2. The secret .. +23 // nonces are the corresponding private keys .. +.. to the public nonce points. .. +24 SecNonceSize = 64 .. +25 ) .. +26 16 +27 var ( 17 var ( +28 // NonceAuxTag is the tag used to optionally 18 // NonceAuxTag is the tag used to optionally + mix in the secret key with mix in the secret key with + +libwallet/musig2v040/nonces.go --- 2/8 --- Go +33 // optional field) that will be used as the 23 // optional field) that will be used as the +.. part of the secret nonce. .. part of the secret nonce. +34 NonceGenTag = []byte("MuSig/nonce") 24 NonceGenTag = []byte("MuSig/nonce") +35 .. +36 byteOrder = binary.BigEndian .. +37 ) 25 ) +38 .. +39 // zeroSecNonce is a secret nonce that's all zer .. +.. oes. This is used to check that .. +40 // we're not attempting to re-use a nonce, and a .. +.. lso protect callers from it. .. +41 var zeroSecNonce [SecNonceSize]byte .. +42 .. +43 // Nonces holds the public and secret nonces req .. +.. uired for musig2. .. +44 // .. +45 // TODO(roasbeef): methods on this to help w/ pa .. +.. rsing, etc? .. +46 type Nonces struct { .. +47 // PubNonce holds the two 33-byte compressed .. +.. encoded points that serve .. +48 // as the public set of nonces. .. +49 PubNonce [PubNonceSize]byte .. +50 .. +51 // SecNonce holds the two 32-byte scalar val .. +.. ues that are the private .. +52 // keys to the two public nonces. .. +53 SecNonce [SecNonceSize]byte .. +54 } .. +55 26 +56 // secNonceToPubNonce takes our two secrete nonc 27 // secNonceToPubNonce takes our two secrete nonc +.. es, and produces their two .. es, and produces their two +57 // corresponding EC points, serialized in compre 28 // corresponding EC points, serialized in compre +.. ssed format. .. ssed format. +58 func secNonceToPubNonce(secNonce [SecNonceSize]b 29 func secNonceToPubNonce(secNonce [musig2.SecNonc +.. yte) [PubNonceSize]byte { .. eSize]byte) [musig2.PubNonceSize]byte { +59 var k1Mod, k2Mod btcec.ModNScalar 30 var k1Mod, k2Mod btcec.ModNScalar +60 k1Mod.SetByteSlice(secNonce[:btcec.PrivKeyBy 31 k1Mod.SetByteSlice(secNonce[:btcec.PrivKeyBy +.. tesLen]) .. tesLen]) +61 k2Mod.SetByteSlice(secNonce[btcec.PrivKeyByt 32 k2Mod.SetByteSlice(secNonce[btcec.PrivKeyByt + esLen:]) esLen:]) + +libwallet/musig2v040/nonces.go --- 3/8 --- Go +71 42 r1Pub := btcec.NewPublicKey(&r1.X, &r1.Y) +72 43 r2Pub := btcec.NewPublicKey(&r2.X, &r2.Y) +73 44 +74 45 var pubNonce [musig2.PubNonceSize]byte +75 46 +76 47 // The public nonces are serialized as: R1 || R2, where both keys are +77 48 // serialized in compressed format. + +libwallet/musig2v040/nonces.go --- 4/8 --- Go +175 } 146 } +176 } 147 } +177 148 +178 // withCustomOptions allows a caller to pass a 149 // genNonceAuxBytes writes out the full byte st +... complete set of custom ... ring used to derive a secret +179 // nonceGenOpts, without needing to create cust 150 // nonce based on some initial randomness as we +... om and checked structs such as ... ll as the series of optional +180 // *btcec.PrivateKey. This is mainly used to ma 151 // fields. The byte string used for derivation +... tch the testcases provided by ... is: +181 // the MuSig2 BIP. 152 // - let seed = tagged_hash( +182 func withCustomOptions(customOpts nonceGenOpts) 153 // "MuSig/nonce", +... NonceGenOption { ... +183 return func(o *nonceGenOpts) { 154 // rand || len(aggpk) || aggpk || len(m) || +... ... m || len(in) || in +184 o.randReader = customOpts.randReader 155 // ) +185 o.secretKey = customOpts.secretKey 156 // - return sha256(seed || i) +186 o.combinedKey = customOpts.combinedKey 157 // +187 o.msg = customOpts.msg 158 // where i is the ith secret nonce being genera +... ... ted. +188 o.auxInput = customOpts.auxInput 159 // +189 } 160 // Muun only provides the rand parameter as ses +... ... sionId. All other parameters are encoded as if +... ... they were len=0 +190 } ... +191 ... +192 // lengthWriter is a function closure that allo ... +... ws a caller to control how the ... +193 // length prefix of a byte slice is written. ... +194 type lengthWriter func(w io.Writer, b []byte) e ... +... rror ... +195 ... +196 // uint8Writer is an implementation of lengthWr ... +... iter that writes the length of ... +197 // the byte slice using 1 byte. ... +198 func uint8Writer(w io.Writer, b []byte) error { 161 func genNonceAuxBytes(rand []byte, i int) ([]by +... ... te, error) { +199 return binary.Write(w, byteOrder, uint8(len ... +... (b))) ... +200 } ... +201 ... +202 // uint32Writer is an implementation of lengthW ... +... riter that writes the length of ... +203 // the byte slice using 4 bytes. ... +204 func uint32Writer(w io.Writer, b []byte) error ... +... { ... +205 return binary.Write(w, byteOrder, uint32(le ... +... n(b))) ... +206 } ... +207 ... +208 // writeBytesPrefix is used to write out: len(b ... +... ) || b, to the passed io.Writer. ... +209 // The lengthWriter function closure is used to ... +... allow the caller to specify the ... +210 // precise byte packing of the length. ... +211 func writeBytesPrefix(w io.Writer, b []byte, le 162 var w bytes.Buffer +... nWriter lengthWriter) error { ... +212 // Write out the length of the byte first, 163 +... followed by the set of bytes ... +213 // itself. 164 // First, write out the randomness generate +... ... d in the prior step. +214 if err := lenWriter(w, b); err != nil { 165 if _, err := w.Write(rand); err != nil { +215 return err 166 return nil, err +216 } 167 } +217 168 +... 169 // write byte 0 for `sec_key` +218 if _, err := w.Write(b); err != nil { 170 if err := w.WriteByte(0); err != nil { +219 return err 171 return nil, err +220 } 172 } +221 173 +222 return nil 174 // write byte 0 for `aggpk` +223 } ... +224 ... +225 // genNonceAuxBytes writes out the full byte st ... +... ring used to derive a secret ... +226 // nonce based on some initial randomness as we ... +... ll as the series of optional ... +227 // fields. The byte string used for derivation ... +... is: ... +228 // * tagged_hash("MuSig/nonce", rand || len(ag ... +... gpk) || aggpk || len(m) ... +229 // || m || len(in) ... +... || in || i). ... +230 // ... +231 // where i is the ith secret nonce being genera ... +... ted. ... +232 func genNonceAuxBytes(rand []byte, i int, ... +233 opts *nonceGenOpts) (*chainhash.Hash, error ... +... ) { ... +234 ... +235 var w bytes.Buffer ... +236 ... +237 // First, write out the randomness generate ... +... d in the prior step. ... +238 if _, err := w.Write(rand); err != nil { 175 if err := w.WriteByte(0); err != nil { +239 return nil, err 176 return nil, err +240 } 177 } +241 178 +242 // Next, we'll write out: len(aggpk) || agg 179 // write byte 0 for `message` +... pk. ... +243 err := writeBytesPrefix(&w, opts.combinedKe 180 if err := w.WriteByte(0); err != nil { +... y, uint8Writer) ... +244 if err != nil { ... +245 return nil, err 181 return nil, err +246 } 182 } +247 183 +248 // Next, we'll write out the length prefixe 184 // write byte 0 for `extra_input32` +... d message. ... +249 err = writeBytesPrefix(&w, opts.msg, uint8W 185 if err := w.WriteByte(0); err != nil { +... riter) ... +250 if err != nil { ... +251 return nil, err 186 return nil, err +252 } 187 } +253 188 +254 // Finally we'll write out the auxiliary in ... +... put. ... +255 err = writeBytesPrefix(&w, opts.auxInput, u 189 seed := chainhash.TaggedHash([]byte("MuSig/ +... int32Writer) ... nonce"), w.Bytes()) +... 190 +... 191 h := sha256.New() +... 192 // write the seed to the buffer +256 if err != nil { 193 if _, err := h.Write(seed.CloneBytes()); er +... ... r != nil { +257 return nil, err 194 return nil, err +258 } 195 } +259 196 +260 // Next we'll write out the interaction/ind 197 // Next we'll write out the interaction/ind +... ex number which will ... ex number which will +261 // uniquely generate two nonces given the r 198 // uniquely generate two nonces given the r +... est of the possibly static ... est of the possibly static +262 // parameters. 199 // parameters. +... 200 ith := make([]byte, 1) +... 201 ith[0] = uint8(i) +... 202 +263 if err := binary.Write(&w, byteOrder, uint8 203 if _, err := h.Write(ith); err != nil { +... (i)); err != nil { ... +264 return nil, err 204 return nil, err +265 } 205 } +266 206 +267 // With the message buffer complete, we'll ... +... now derive the tagged hash ... +268 // using our set of params. ... +269 return chainhash.TaggedHash(NonceGenTag, w. 207 return h.Sum(nil), nil +... Bytes()), nil ... +270 } 208 } +271 209 +272 // GenNonces generates the secret nonces, as we 210 // Custom implementation of GenNonces to produc +... ll as the public nonces which ... e a bit-to-bit copy of the secp256k1_zkp +273 // correspond to an EC point generated using th 211 // All other parameters are ignored. +... e secret nonce as a private key. ... +... 212 // +... 213 // Pseudo algorithm (|| means byte concat) +... 214 // +... 215 // let seed = TaggedHash("MuSig/nonce", sess +... ... ionId || 0 || 0 || 0 || 0) +... 216 // let k = [sha256(seed || 0), sha256(seed | +... ... | 1)] +... 217 // let r = k*G +... 218 // return toPublicKeyFormat(r) +274 func GenNonces(options ...NonceGenOption) (*Non 219 func GenNonces(options ...NonceGenOption) (*mus +... ces, error) { ... ig2.Nonces, error) { +275 opts := defaultNonceGenOpts() 220 opts := defaultNonceGenOpts() +276 for _, opt := range options { 221 for _, opt := range options { +277 opt(opts) 222 opt(opts) + +libwallet/musig2v040/nonces.go --- 5/8 --- Go +284 229 return nil, err +285 230 } +286 ... +287 ... // If the options contain a secret key, we XOR it with with the tagged +288 ... // random bytes. +289 ... if len(opts.secretKey) == 32 { +290 ... taggedHash := chainhash.TaggedHash(NonceAuxTag, randBytes[:]) +291 ... +292 ... for i := 0; i < chainhash.HashSize; i++ { +293 ... randBytes[i] = opts.secretKey[i] ^ taggedHash[i] +294 ... } +295 ... } +296 231 +297 232 // Using our randomness and the set of optional params, generate our +298 233 // two secret nonces: k1 and k2. +299 234 k1, err := genNonceAuxBytes(randBytes[:], 0, opts) +300 235 if err != nil { +301 236 return nil, err +302 237 } +303 238 k2, err := genNonceAuxBytes(randBytes[:], 1, opts) +304 239 if err != nil { +305 240 return nil, err +306 241 } + +libwallet/musig2v040/nonces.go --- 6/8 --- Go +311 246 +312 247 // The secret nonces are serialized as the concatenation of the two 32 +313 248 // byte secret nonce values. +314 249 var nonces musig2.Nonces +315 250 k1Mod.PutBytesUnchecked(nonces.SecNonce[:]) +316 251 k2Mod.PutBytesUnchecked(nonces.SecNonce[btcec.PrivKeyBytesLen:]) +317 252 + +libwallet/musig2v040/nonces.go --- 7/8 --- Go +325 260 +326 261 // AggregateNonces aggregates the set of a pair of public nonces for each party +327 262 // into a single aggregated nonces to be used for multi-signing. +328 263 func AggregateNonces(pubNonces [][musig2.PubNonceSize]byte) ([musig2.PubNonceSize]byte, error) { +329 264 // combineNonces is a helper function that aggregates (adds) up a +330 265 // series of nonces encoded in compressed format. It uses a slicing +331 266 // function to extra 33 bytes at a time from the packed 2x public +332 267 // nonces. +333 268 type nonceSlicer func([musig2.PubNonceSize]byte) []byte +334 269 combineNonces := func(slicer nonceSlicer) (btcec.JacobianPoint, error) { +335 270 // Convert the set of nonces into jacobian coordinates we can +336 271 // use to accumulate them all into each other. + +libwallet/musig2v040/nonces.go --- 8/8 --- Go +364 299 // The final nonce public nonce is actually two nonces, one that +365 300 // aggregate the first nonce of all the parties, and the other that +366 301 // aggregates the second nonce of all the parties. +367 302 var finalNonce [musig2.PubNonceSize]byte +368 303 combinedNonce1, err := combineNonces(func(n [musig2.PubNonceSize]byte) []byte { +369 304 return n[:btcec.PubKeyBytesLenCompressed] +370 305 }) +371 306 if err != nil { +372 307 return finalNonce, err +373 308 } +374 309 +375 310 combinedNonce2, err := combineNonces(func(n [musig2.PubNonceSize]byte) []byte { +376 311 return n[btcec.PubKeyBytesLenCompressed:] +377 312 }) +378 313 if err != nil { + +libwallet/musig2v040/sign.go --- 1/7 --- Go + 1 // Copyright 2013-2022 The btcsuite developers 1 // Copyright 2013-2022 The btcsuite developers + 2 2 + 3 package musig2 3 package musig2v040 + 4 4 + 5 import ( 5 import ( + 6 "bytes" 6 "bytes" + . 7 "crypto/sha256" + 7 "fmt" 8 "fmt" + 8 "io" 9 "io" + 9 10 + . 11 "github.com/btcsuite/btcd/btcec/v2" + . 12 "github.com/btcsuite/btcd/btcec/v2/schnorr" + . 13 "github.com/btcsuite/btcd/btcec/v2/schnorr/m + . .. usig2" + . 14 "github.com/btcsuite/btcd/chaincfg/chainhash + . .. " +10 secp "github.com/decred/dcrd/dcrec/secp256k1 15 secp "github.com/decred/dcrd/dcrec/secp256k1 +.. /v4" .. /v4" +11 .. +12 "github.com/btcsuite/btcd/btcec/v2" .. +13 "github.com/btcsuite/btcd/btcec/v2/schnorr" .. +14 "github.com/btcsuite/btcd/chaincfg/chainhash .. +.. " .. +15 ) 16 ) +16 17 +17 var ( 18 var ( +18 // NonceBlindTag is that tag used to constru .. +.. ct the value b, which .. +19 // blinds the second public nonce of each pa .. +.. rty. .. +20 NonceBlindTag = []byte("MuSig/noncecoef") .. +21 .. +22 // ChallengeHashTag is the tag used to const 19 // ChallengeHashTag is the tag used to const +.. ruct the challenge hash .. ruct the challenge hash +23 ChallengeHashTag = []byte("BIP0340/challenge 20 ChallengeHashTag = []byte("BIP0340/challenge + ") ") + +libwallet/musig2v040/sign.go --- 2/7 --- Go +190 187 // nonce, public nonce, and private keys. This method returns an error if the +191 188 // generated nonces are either too large, or end up mapping to the point at +192 189 // infinity. +193 190 func Sign(secNonce [musig2.SecNonceSize]byte, privKey *btcec.PrivateKey, +194 191 combinedNonce [musig2.PubNonceSize]byte, pubKeys []*btcec.PublicKey, +195 192 msg [32]byte, signOpts ...SignOption) (*PartialSignature, error) { +196 193 +197 194 // First, parse the set of optional signing options. + +libwallet/musig2v040/sign.go --- 3/7 --- Go +232 229 +233 // Next we'll compute the value b, that bli 230 // Next we'll compute the value b, that bli +... nds our second public ... nds our second public +234 // nonce: 231 // nonce: +235 // * b = h(tag=NonceBlindTag, combinedNonc 232 // * b = h(combinedNonce || combinedKey || +... e || combinedKey || m). ... m). h==sha256 +236 var ( 233 var ( +237 nonceMsgBuf bytes.Buffer ... +238 nonceBlinder btcec.ModNScalar 234 nonceBlinder btcec.ModNScalar +239 ) 235 ) +... 236 +... 237 nonceMsgBuf := sha256.New() +240 nonceMsgBuf.Write(combinedNonce[:]) 238 nonceMsgBuf.Write(combinedNonce[:]) +241 nonceMsgBuf.Write(schnorr.SerializePubKey(c 239 nonceMsgBuf.Write(schnorr.SerializePubKey(c +... ombinedKey.FinalKey)) ... ombinedKey.FinalKey)) +242 nonceMsgBuf.Write(msg[:]) 240 nonceMsgBuf.Write(msg[:]) +243 nonceBlindHash := chainhash.TaggedHash( 241 nonceBlindHash := nonceMsgBuf.Sum(nil) +244 NonceBlindTag, nonceMsgBuf.Bytes(), ... +245 ) ... +246 nonceBlinder.SetByteSlice(nonceBlindHash[:] 242 nonceBlinder.SetByteSlice(nonceBlindHash[:] +... ) ... ) +247 243 +248 // Next, we'll parse the public nonces into 244 // Next, we'll parse the public nonces into + R1 and R2. R1 and R2. + +libwallet/musig2v040/sign.go --- 4/7 --- Go +358 signOpts..., 354 signOpts..., +359 ) 355 ) +360 if !sigValid { 356 if !sigValid { +361 return nil, fmt.Errorf("sig is inva 357 return nil, fmt.Errorf("sig is inva +... lid!") ... lid") +362 } 358 } +363 } 359 } +364 360 + +libwallet/musig2v040/sign.go --- 5/7 --- Go +368 364 // Verify implements partial signature verification given the public nonce for +369 365 // the signer, aggregate nonce, signer set and finally the message being +370 366 // signed. +371 367 func (p *PartialSignature) Verify(pubNonce [musig2.PubNonceSize]byte, +372 368 combinedNonce [musig2.PubNonceSize]byte, keySet []*btcec.PublicKey, +373 369 signingKey *btcec.PublicKey, msg [32]byte, signOpts ...SignOption) bool { +374 370 +375 371 pubKey := schnorr.SerializePubKey(signingKey) + +libwallet/musig2v040/sign.go --- 6/7 --- Go +382 378 // verifyPartialSig attempts to verify a partial schnorr signature given the +383 379 // necessary parameters. This is the internal version of Verify that returns +384 380 // detailed errors. signed. +385 381 func verifyPartialSig(partialSig *PartialSignature, pubNonce [musig2.PubNonceSize]byte, +386 382 combinedNonce [musig2.PubNonceSize]byte, keySet []*btcec.PublicKey, +387 383 pubKey []byte, msg [32]byte, signOpts ...SignOption) error { +388 384 +389 385 opts := defaultSignOptions() + +libwallet/musig2v040/sign.go --- 7/7 --- Go +431 427 +432 // Next we'll compute the value b, that bli 428 // Next we'll compute the value b, that bli +... nds our second public ... nds our second public +433 // nonce: 429 // nonce: +434 // * b = h(tag=NonceBlindTag, combinedNonc 430 // * b = h(combinedNonce || combinedKey || +... e || combinedKey || m). ... m). h==sha256 +435 var ( 431 var ( +436 nonceMsgBuf bytes.Buffer ... +437 nonceBlinder btcec.ModNScalar 432 nonceBlinder btcec.ModNScalar +438 ) 433 ) +... 434 nonceMsgBuf := sha256.New() +439 nonceMsgBuf.Write(combinedNonce[:]) 435 nonceMsgBuf.Write(combinedNonce[:]) +440 nonceMsgBuf.Write(schnorr.SerializePubKey(c 436 nonceMsgBuf.Write(schnorr.SerializePubKey(c +... ombinedKey.FinalKey)) ... ombinedKey.FinalKey)) +441 nonceMsgBuf.Write(msg[:]) 437 nonceMsgBuf.Write(msg[:]) +442 nonceBlindHash := chainhash.TaggedHash(Nonc 438 nonceBlindHash := nonceMsgBuf.Sum(nil) +... eBlindTag, nonceMsgBuf.Bytes()) ... +443 nonceBlinder.SetByteSlice(nonceBlindHash[:] 439 nonceBlinder.SetByteSlice(nonceBlindHash[:] +... ) ... ) +444 440 +445 r1J, err := btcec.ParseJacobian( 441 r1J, err := btcec.ParseJacobian( + diff --git a/libwallet/newop/bridge_persistence_fee_bump_functions.go b/libwallet/newop/bridge_persistence_fee_bump_functions.go new file mode 100644 index 00000000..9d2b2dfe --- /dev/null +++ b/libwallet/newop/bridge_persistence_fee_bump_functions.go @@ -0,0 +1,145 @@ +package newop + +import ( + "encoding/base64" + "encoding/binary" + "errors" + "fmt" + "math" + "path" + "time" + + "github.com/muun/libwallet" + "github.com/muun/libwallet/operation" + "github.com/muun/libwallet/walletdb" +) + +const invalidationTimeInSeconds = 60.0 + +// PersistFeeBumpFunctions This is a bridge that stores fee bump functions +// from native apps in the device's local database. +func PersistFeeBumpFunctions(encodedBase64Functions *libwallet.StringList) error { + + if encodedBase64Functions == nil { + return errors.New("encoded base 64 function list is null") + } + + decodedFunctions, err := decodeFunctions(encodedBase64Functions.ConvertToArray()) + if err != nil { + return err + } + + feeBumpFunctions := convertToLibwalletFeeBumpFunctions(decodedFunctions) + + db, err := walletdb.Open(path.Join(libwallet.Cfg.DataDir, "wallet.db")) + if err != nil { + return err + } + defer db.Close() + + repository := db.NewFeeBumpRepository() + + return repository.Store(feeBumpFunctions) +} + +func AreFeeBumpFunctionsInvalidated() bool { + db, err := walletdb.Open(path.Join(libwallet.Cfg.DataDir, "wallet.db")) + if err != nil { + return true + } + defer db.Close() + + repository := db.NewFeeBumpRepository() + creationDate, err := repository.GetCreationDate() + + if err != nil || creationDate == nil { + return true + } + + durationInSeconds := time.Since(*creationDate).Seconds() + + return durationInSeconds >= invalidationTimeInSeconds +} + +func decodeFunctions(encodedFunctions []string) ([][][]float64, error) { + var decodedFunctions [][][]float64 + for i := range encodedFunctions { + decodedFunctionValues, err := decodeFromBase64(encodedFunctions[i]) + if err != nil { + return nil, err + } + decodedFunctions = append(decodedFunctions, decodedFunctionValues) + } + return decodedFunctions, nil +} + +func decodeFromBase64(base64Function string) ([][]float64, error) { + decodedBytes, err := base64.StdEncoding.DecodeString(base64Function) + if err != nil { + return nil, err + } + + const bytesPerFloat = 4 + if len(decodedBytes)%bytesPerFloat != 0 { + return nil, fmt.Errorf( + "decoded bytes length: %d is invalid. It should by multiple of %d", + len(decodedBytes), + bytesPerFloat, + ) + } + + var listOfFloats []float64 + for i := 0; i < len(decodedBytes); i += bytesPerFloat { + bits := binary.BigEndian.Uint32(decodedBytes[i : i+bytesPerFloat]) + decodedFloat := math.Float32frombits(bits) + listOfFloats = append(listOfFloats, float64(decodedFloat)) + } + + // Each function consists 3 float values: Right Endpoint, Slope and Intercept. + // Left Endpoint starts at 0 for the first interval, and then each subsequent + // interval uses the previous Right Endpoint. + const floatsPerTuple = 3 + var result [][]float64 + for i := 0; i < len(listOfFloats); i += floatsPerTuple { + end := i + floatsPerTuple + if end > len(listOfFloats) { + return nil, fmt.Errorf( + "fee bump function was incorrectly encoded; it should be a multiply of %d float numbers, got: %d", + floatsPerTuple, + len(listOfFloats), + ) + } + result = append(result, listOfFloats[i:end]) + } + return result, nil +} + +func convertToLibwalletFeeBumpFunctions(decodedFunctions [][][]float64) []*operation.FeeBumpFunction { + // Convert to libwallet data types + var feeBumpFunctions []*operation.FeeBumpFunction + const rightOpenEndpointPosition = 0 + const slopePosition = 1 + const interceptPosition = 2 + for i := range decodedFunctions { + lastLeftClosedEndpoint := 0.0 + var partialLinearFunctions []*operation.PartialLinearFunction + for j := range decodedFunctions[i] { + partialLinearFunction := &operation.PartialLinearFunction{ + LeftClosedEndpoint: lastLeftClosedEndpoint, + RightOpenEndpoint: decodedFunctions[i][j][rightOpenEndpointPosition], + Slope: decodedFunctions[i][j][slopePosition], + Intercept: decodedFunctions[i][j][interceptPosition], + } + partialLinearFunctions = append( + partialLinearFunctions, + partialLinearFunction, + ) + lastLeftClosedEndpoint = decodedFunctions[i][j][rightOpenEndpointPosition] + } + feeBumpFunctions = append( + feeBumpFunctions, + &operation.FeeBumpFunction{PartialLinearFunctions: partialLinearFunctions}, + ) + } + return feeBumpFunctions +} diff --git a/libwallet/newop/bridge_persistence_fee_bump_functions_test.go b/libwallet/newop/bridge_persistence_fee_bump_functions_test.go new file mode 100644 index 00000000..008cfe1d --- /dev/null +++ b/libwallet/newop/bridge_persistence_fee_bump_functions_test.go @@ -0,0 +1,168 @@ +package newop + +import ( + "github.com/muun/libwallet" + "github.com/muun/libwallet/operation" + "github.com/muun/libwallet/walletdb" + "math" + "path" + "reflect" + "testing" + "time" +) + +func TestDecodeFeeBumpFunctions(t *testing.T) { + testCases := []struct { + desc string + encodedFunction string + expectedValues [][]float64 + err bool + }{ + { + desc: "decode fee bump function with single interval", + encodedFunction: "f4AAAD+AAAAAAAAA", // [+Inf, 1, 0] + expectedValues: [][]float64{{math.Inf(1), 1, 0}}, + err: false, + }, + { + desc: "decode fee bump function with two intervals", + encodedFunction: "QsgAAAAAAAAAAAAAf4AAAD+AAABAAAAA", // [[100, 0, 0], [+Inf, 1, 2]] + expectedValues: [][]float64{ + {100, 0, 0}, + {math.Inf(1), 1, 2}, + }, + err: false, + }, + { + desc: "decode fee bump function incorrectly encoded (4 floats)", + encodedFunction: "QEAAAAAAAAAAAAAAAAAAAA==", // [3 0 0 0] + expectedValues: nil, + err: true, + }, + } + + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + feeBumpFunctionList, err := decodeFromBase64(tC.encodedFunction) + + if err != nil && !tC.err { + t.Fatal(err) + } + + if !reflect.DeepEqual(feeBumpFunctionList, tC.expectedValues) { + t.Fatalf( + "decoded values does not match expected, got: %+v, expected: %+v", + feeBumpFunctionList, + tC.expectedValues, + ) + } + }) + } +} + +func TestPersistFeeBumpFunctions(t *testing.T) { + testCases := []struct { + desc string + encodedFunctionList []string + expectedFunctions []*operation.FeeBumpFunction + err bool + }{ + { + desc: "Persist single fee bump function", + encodedFunctionList: []string{"QsgAAAAAAAAAAAAAf4AAAD+AAABAAAAA"}, // [[100, 0, 0], [+Inf, 1, 2]] + expectedFunctions: []*operation.FeeBumpFunction{ + { + CreatedAt: time.Now(), + PartialLinearFunctions: []*operation.PartialLinearFunction{ + { + LeftClosedEndpoint: 0, + RightOpenEndpoint: 100, + Slope: 0, + Intercept: 0, + }, + { + LeftClosedEndpoint: 100, + RightOpenEndpoint: math.Inf(1), + Slope: 1, + Intercept: 2, + }, + }, + }, + }, + err: false, + }, + { + desc: "Persist with two fee bump functions", + encodedFunctionList: []string{ + "QsgAAAAAAAAAAAAAf4AAAD+AAABAAAAA", // [[100, 0, 0], [+Inf, 1, 2]] + "f4AAAD+AAAAAAAAA", // [[+Inf, 1, 0]]] + }, + expectedFunctions: []*operation.FeeBumpFunction{ + { + CreatedAt: time.Now(), + PartialLinearFunctions: []*operation.PartialLinearFunction{ + { + LeftClosedEndpoint: 0, + RightOpenEndpoint: 100, + Slope: 0, + Intercept: 0, + }, + { + LeftClosedEndpoint: 100, + RightOpenEndpoint: math.Inf(1), + Slope: 1, + Intercept: 2, + }, + }, + }, + { + PartialLinearFunctions: []*operation.PartialLinearFunction{ + { + LeftClosedEndpoint: 0, + RightOpenEndpoint: math.Inf(1), + Slope: 1, + Intercept: 0, + }, + }, + }, + }, + err: false, + }, + } + + // Set temporary file for testing + libwallet.Init(&libwallet.Config{DataDir: t.TempDir()}) + + db, err := walletdb.Open(path.Join(libwallet.Cfg.DataDir, "wallet.db")) + if err != nil { + t.Fatalf("error opening DB") + } + defer db.Close() + + for _, tC := range testCases { + functionList := libwallet.NewStringListWithElements(tC.encodedFunctionList) + err := PersistFeeBumpFunctions(functionList) + + if err != nil && tC.err { + t.Fatal(err) + } + + repository := db.NewFeeBumpRepository() + + feeBumpFunctions, err := repository.GetAll() + + if err != nil { + t.Fatalf("error getting bump functions") + } + + if len(feeBumpFunctions) != len(tC.expectedFunctions) { + t.Fatalf("fee bump functions were not saved properly") + } + + for i, expectedFunction := range tC.expectedFunctions { + if !reflect.DeepEqual(expectedFunction.PartialLinearFunctions, feeBumpFunctions[i].PartialLinearFunctions) { + t.Fatalf("fee bump functions were not saved properly") + } + } + } +} diff --git a/libwallet/newop/context.go b/libwallet/newop/context.go index 7f03666a..76358c51 100644 --- a/libwallet/newop/context.go +++ b/libwallet/newop/context.go @@ -2,14 +2,32 @@ package newop import "github.com/muun/libwallet/operation" +// InitialPaymentContext receives operation data provided by the native platform. +type InitialPaymentContext struct { + FeeWindow *FeeWindow + NextTransactionSize *NextTransactionSize + ExchangeRateWindow *ExchangeRateWindow + PrimaryCurrency string + MinFeeRateInSatsPerVByte float64 + SubmarineSwap *SubmarineSwap +} + // PaymentContext stores data required to analyze and validate an operation +// It comprises InitialPaymentContext with data from native apps, +// adding properties loaded inside Libwallet. type PaymentContext struct { + //****** InitialPaymentContext ****** + // Copied from InitialPaymentContext to avoid awful nested hierarchy + // on native apps. FeeWindow *FeeWindow NextTransactionSize *NextTransactionSize ExchangeRateWindow *ExchangeRateWindow PrimaryCurrency string MinFeeRateInSatsPerVByte float64 SubmarineSwap *SubmarineSwap + //*********************************** + + feeBumpFunctions []*operation.FeeBumpFunction } func (c *PaymentContext) totalBalance() int64 { @@ -32,5 +50,18 @@ func newPaymentAnalyzer(context *PaymentContext) *operation.PaymentAnalyzer { return operation.NewPaymentAnalyzer( context.FeeWindow.toInternalType(), context.NextTransactionSize.toInternalType(), + context.feeBumpFunctions, ) } + +func (ipc *InitialPaymentContext) newPaymentContext(feeBumpFunctions []*operation.FeeBumpFunction) *PaymentContext { + return &PaymentContext{ + FeeWindow: ipc.FeeWindow, + NextTransactionSize: ipc.NextTransactionSize, + ExchangeRateWindow: ipc.ExchangeRateWindow, + PrimaryCurrency: ipc.PrimaryCurrency, + MinFeeRateInSatsPerVByte: ipc.MinFeeRateInSatsPerVByte, + SubmarineSwap: ipc.SubmarineSwap, + feeBumpFunctions: feeBumpFunctions, + } +} diff --git a/libwallet/newop/context_test.go b/libwallet/newop/context_test.go index 4897cec3..c8e5e4e1 100644 --- a/libwallet/newop/context_test.go +++ b/libwallet/newop/context_test.go @@ -8,7 +8,7 @@ import ( var testPaymentContext = createTestPaymentContext() func createTestPaymentContext() *PaymentContext { - var context = &PaymentContext{ + var initialContext = &InitialPaymentContext{ NextTransactionSize: &NextTransactionSize{ ExpectedDebtInSat: 10_000, }, @@ -19,14 +19,16 @@ func createTestPaymentContext() *PaymentContext { PrimaryCurrency: "BTC", MinFeeRateInSatsPerVByte: 1.0, } - context.NextTransactionSize.AddSizeForAmount(&SizeForAmount{ + initialContext.NextTransactionSize.AddSizeForAmount(&SizeForAmount{ AmountInSat: 100_000_000, SizeInVByte: 240, UtxoStatus: "CONFIRMED", }) - context.ExchangeRateWindow.AddRate("BTC", 1) - context.ExchangeRateWindow.AddRate("USD", 32_000) + initialContext.ExchangeRateWindow.AddRate("BTC", 1) + initialContext.ExchangeRateWindow.AddRate("USD", 32_000) + + context := initialContext.newPaymentContext(nil) return context } diff --git a/libwallet/newop/money.go b/libwallet/newop/money.go index 4895abc6..fda534b4 100644 --- a/libwallet/newop/money.go +++ b/libwallet/newop/money.go @@ -4,7 +4,7 @@ import ( "fmt" "log" - "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/btcutil" "github.com/shopspring/decimal" ) diff --git a/libwallet/newop/state.go b/libwallet/newop/state.go index 6241c803..0d33d9d4 100644 --- a/libwallet/newop/state.go +++ b/libwallet/newop/state.go @@ -2,12 +2,14 @@ package newop import ( "fmt" + "path" "strings" "time" - "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/btcutil" "github.com/muun/libwallet" "github.com/muun/libwallet/operation" + "github.com/muun/libwallet/walletdb" ) // Transitions that involve asynchronous work block, so apps should always fire in background and @@ -261,12 +263,40 @@ type ResolveState struct { PaymentIntent *PaymentIntent } -func (s *ResolveState) SetContext(context *PaymentContext) error { - return s.setContextWithTime(context, time.Now()) +func (s *ResolveState) SetContext(initialContext *InitialPaymentContext) error { + return s.setContextWithTime(initialContext, time.Now()) +} + +func loadFeeBumpFunctions() ([]*operation.FeeBumpFunction, error) { + db, err := walletdb.Open(path.Join(libwallet.Cfg.DataDir, "wallet.db")) + if err != nil { + return nil, err + } + defer db.Close() + + repository := db.NewFeeBumpRepository() + feeBumpFunctions, err := repository.GetAll() + // TODO: Check if the data is invalidated, on that case we should refresh it + + if err != nil { + return nil, err + } + + return feeBumpFunctions, nil } // setContextWithTime is meant only for testing, allows caller to use a fixed time to check invoice expiration -func (s *ResolveState) setContextWithTime(context *PaymentContext, now time.Time) error { +func (s *ResolveState) setContextWithTime(initialContext *InitialPaymentContext, now time.Time) error { + + var feeBumpFunctions []*operation.FeeBumpFunction + if libwallet.DetermineBackendActivatedFeatureStatus(libwallet.BackendFeatureEffectiveFeesCalculation) { + // Load fee bump functions from local DB + feeBumpFunctions, _ = loadFeeBumpFunctions() + + // TODO: Handle error - tracking error and refresh fee bump functions + } + + context := initialContext.newPaymentContext(feeBumpFunctions) // TODO(newop): add type to PaymentIntent to clarify lightning/onchain distinction invoice := s.PaymentIntent.URI.Invoice diff --git a/libwallet/newop/state_test.go b/libwallet/newop/state_test.go index 41e71c86..b354455e 100644 --- a/libwallet/newop/state_test.go +++ b/libwallet/newop/state_test.go @@ -1,10 +1,14 @@ package newop import ( + "math" + "path" "testing" "time" "github.com/muun/libwallet" + "github.com/muun/libwallet/operation" + "github.com/muun/libwallet/walletdb" "github.com/shopspring/decimal" ) @@ -35,10 +39,16 @@ func (t *testListener) next() State { var _ TransitionListener = &testListener{} -var testContext = createContext() +var testContext = createInitialContext() -func createContext() *PaymentContext { - var context = &PaymentContext{ +type TestBackendActivatedFeatureStatusProvider struct{} + +func (t TestBackendActivatedFeatureStatusProvider) IsBackendFlagEnabled(flag string) bool { + return true +} + +func createInitialContext() *InitialPaymentContext { + var initialContext = &InitialPaymentContext{ NextTransactionSize: &NextTransactionSize{}, ExchangeRateWindow: &ExchangeRateWindow{ rates: make(map[string]float64), @@ -47,26 +57,35 @@ func createContext() *PaymentContext { PrimaryCurrency: "BTC", MinFeeRateInSatsPerVByte: 1.0, } - context.NextTransactionSize.AddSizeForAmount(&SizeForAmount{ + initialContext.NextTransactionSize.AddSizeForAmount(&SizeForAmount{ AmountInSat: 100_000_000, SizeInVByte: 240, UtxoStatus: "CONFIRMED", }) - context.ExchangeRateWindow.AddRate("BTC", 1) - context.ExchangeRateWindow.AddRate("USD", 32_000) - context.ExchangeRateWindow.AddRate("ARS", 9_074_813.98) + initialContext.ExchangeRateWindow.AddRate("BTC", 1) + initialContext.ExchangeRateWindow.AddRate("USD", 32_000) + initialContext.ExchangeRateWindow.AddRate("ARS", 9_074_813.98) + + initialContext.FeeWindow.PutTargetedFees(1, 400.0) + initialContext.FeeWindow.PutTargetedFees(15, 120.0) + initialContext.FeeWindow.PutTargetedFees(90, 8.0) + + return initialContext +} - context.FeeWindow.PutTargetedFees(1, 400.0) - context.FeeWindow.PutTargetedFees(15, 120.0) - context.FeeWindow.PutTargetedFees(90, 8.0) +func setupStateTests(t *testing.T) { - return context + libwallet.Init(&libwallet.Config{ + DataDir: t.TempDir(), + FeatureStatusProvider: TestBackendActivatedFeatureStatusProvider{}, + }) } //goland:noinspection GoUnhandledErrorResult func TestBarebonesOnChainFixedAmountFixedFee(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -101,6 +120,7 @@ func TestBarebonesOnChainFixedAmountFixedFee(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestBarebonesOnChainFixedAmountFixedDescriptionFixedFee(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -131,6 +151,7 @@ func TestBarebonesOnChainFixedAmountFixedDescriptionFixedFee(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestOnChainFixedAmountChangeFee(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -190,8 +211,152 @@ func TestOnChainFixedAmountChangeFee(t *testing.T) { } } +func TestOnChainFixedAmountChangeFeeWithFeeBump(t *testing.T) { + + setupStateTests(t) + listener := newTestListener() + startState := NewOperationFlow(listener) + + startState.Resolve("bitcoin:bcrt1qj35fkq34xend9w0ssthn432vl9pxxsuy0epzlu?amount=1.0&description=foo", libwallet.Regtest()) + + resolveState := listener.next().(*ResolveState) + + context := *testContext + nextTransactionSize := *context.NextTransactionSize + nextTransactionSize.AddSizeForAmount(&SizeForAmount{ + SizeInVByte: 500, + AmountInSat: 100_100_000, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c1:0", + UtxoStatus: string(UtxosStatusUnconfirmed), + }) + + nextTransactionSize.AddSizeForAmount(&SizeForAmount{ + SizeInVByte: 730, + AmountInSat: 100_900_000, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c2:0", + UtxoStatus: string(UtxosStatusUnconfirmed), + }) + context.NextTransactionSize = &nextTransactionSize + + db, err := walletdb.Open(path.Join(libwallet.Cfg.DataDir, "wallet.db")) + if err != nil { + t.Fatal(err) + } + defer db.Close() + + repository := db.NewFeeBumpRepository() + repository.Store([]*operation.FeeBumpFunction{ + &operation.FeeBumpFunction{PartialLinearFunctions: []*operation.PartialLinearFunction{ + &operation.PartialLinearFunction{ + LeftClosedEndpoint: 0, + RightOpenEndpoint: math.Inf(1), + Slope: 2, + Intercept: 300, + }, + }}, + }) + + resolveState.SetContext(&context) + + validateState := listener.next().(*ValidateState) + validateState.Continue() + + enterDescriptionState := listener.next().(*EnterDescriptionState) + enterDescriptionState.EnterDescription("bar") + + confirmState := listener.next().(*ConfirmState) + + if confirmState.FeeRateInSatsPerVByte != 400 { + t.Fatalf("expected initial fee rate to be 400, got %v", confirmState.FeeRateInSatsPerVByte) + } + + if confirmState.Fee.InSat != 293_100 { + t.Fatalf("expected fee amount to be 293100, got %v", confirmState.Fee.InSat) + } + + confirmState.OpenFeeEditor() + + editFeeState := listener.next().(*EditFeeState) + + // Calculate high priority transaction fee + feeRatePriorityHigh, err := editFeeState.MinFeeRateForTarget(1) + if err != nil { + t.Fatal(err) + } + feeState, err := editFeeState.CalculateFee(feeRatePriorityHigh) + if err != nil { + t.Fatal(err) + } + // Using all 3 utxos (1 confirmed, 2 unconfirmed) - Fee bump amount: 1100 + if feeState.Amount.InSat != 293_100 { + t.Fatalf("expected fee amount to be 293000, got %v", feeState.Amount.InSat) + } + + // Calculate medium priority transaction fee + feeRatePriorityMedium, err := editFeeState.MinFeeRateForTarget(15) + if err != nil { + t.Fatal(err) + } + feeState, err = editFeeState.CalculateFee(feeRatePriorityMedium) + if err != nil { + t.Fatal(err) + } + // Using 2 utxos (1 confirmed, 1 unconfirmed) - Fee bump amount: 540 + if feeState.Amount.InSat != 60_540 { + t.Fatalf("expected fee amount to be 60540, got %v", feeState.Amount.InSat) + } + + // Calculate low priority transaction fee + feeRatePriorityLow, err := editFeeState.MinFeeRateForTarget(90) + if err != nil { + t.Fatal(err) + } + feeState, err = editFeeState.CalculateFee(feeRatePriorityLow) + if err != nil { + t.Fatal(err) + } + + // Using 2 utxos (1 confirmed, 1 unconfirmed) - Fee bump amount: 316 + if feeState.Amount.InSat != 4316 { + t.Fatalf("expected fee amount to be 4316, got %v", feeState.Amount.InSat) + } + + newFeeRate := 15.0 + feeState, err = editFeeState.CalculateFee(newFeeRate) + if err != nil { + t.Fatal(err) + } + if feeState.State != FeeStateFinalFee { + t.Fatalf("expected fee state to be FinalFee, got %v", feeState.State) + } + if feeState.Amount.InSat != 7830 { + t.Fatalf("expected fee amount to be 7830, got %v", feeState.Amount.InSat) + } + + editFeeState.SetFeeRate(newFeeRate) + + validateState = listener.next().(*ValidateState) + validateState.Continue() + + confirmState = listener.next().(*ConfirmState) + + if confirmState.Note != "bar" { + t.Fatalf("expected note to match input, got '%v'", confirmState.Note) + } + if confirmState.Amount.InInputCurrency.String() != "1 BTC" { + t.Fatalf("expected amount to match resolved URI, got %v", confirmState.Amount.InInputCurrency) + } + if confirmState.Fee.InInputCurrency.String() != "0.0000783 BTC" { + t.Fatalf("expected fee to match, got %v", confirmState.Fee.InInputCurrency) + } + if confirmState.Total.InInputCurrency.String() != "1.0000783 BTC" { + t.Fatalf("expected total to match, got %v", confirmState.Total.InInputCurrency) + } +} + //goland:noinspection GoUnhandledErrorResult func TestOnChainFixedAmountFeeNeedsChange(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -261,6 +426,7 @@ func TestOnChainFixedAmountFeeNeedsChange(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestOnChainFixedAmountNoPossibleFee(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -286,6 +452,7 @@ func TestOnChainFixedAmountNoPossibleFee(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestOnChainFixedAmountTooSmall(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -303,6 +470,7 @@ func TestOnChainFixedAmountTooSmall(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestOnChainFixedAmountGreaterThanbalance(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -329,12 +497,13 @@ func TestOnChainFixedAmountGreaterThanbalance(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestOnChainSendZeroFundsWithZeroBalance(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) startState.Resolve("bitcoin:bcrt1qj35fkq34xend9w0ssthn432vl9pxxsuy0epzlu", libwallet.Regtest()) - context := createContext() + context := createInitialContext() context.NextTransactionSize = &NextTransactionSize{} resolveState := listener.next().(*ResolveState) @@ -356,6 +525,7 @@ func TestOnChainSendZeroFundsWithZeroBalance(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestOnChainTFFA(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -405,6 +575,7 @@ func TestOnChainTFFA(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestInvalidAmountEmitsInvalidAddress(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -424,6 +595,7 @@ func TestInvalidAmountEmitsInvalidAddress(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestOnChainBack(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -506,6 +678,7 @@ func TestOnChainBack(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestOnChainChangeCurrency(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -638,7 +811,7 @@ func TestLightningSendZeroFunds(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.NextTransactionSize = &NextTransactionSize{} context.SubmarineSwap = &SubmarineSwap{ BestRouteFees: []*BestRouteFees{ @@ -685,7 +858,7 @@ func TestLightningSendZeroFundsTFFA(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.NextTransactionSize = &NextTransactionSize{} context.SubmarineSwap = &SubmarineSwap{ BestRouteFees: []*BestRouteFees{ @@ -732,7 +905,7 @@ func TestLightningSendNegativeFunds(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.NextTransactionSize = &NextTransactionSize{} context.SubmarineSwap = &SubmarineSwap{ BestRouteFees: []*BestRouteFees{ @@ -779,7 +952,7 @@ func TestLightningSendNegativeFundsWithTFFA(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.NextTransactionSize = &NextTransactionSize{} context.SubmarineSwap = &SubmarineSwap{ BestRouteFees: []*BestRouteFees{ @@ -814,6 +987,7 @@ func TestLightningSendNegativeFundsWithTFFA(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestLightningExpiredInvoice(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) @@ -849,7 +1023,7 @@ func TestLightningInvoiceWithAmount(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.SubmarineSwap = &SubmarineSwap{ Fees: &SwapFees{ RoutingFeeInSat: 0, @@ -900,7 +1074,7 @@ func TestLightningWithAmountBack(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.SubmarineSwap = &SubmarineSwap{ Fees: &SwapFees{ RoutingFeeInSat: 0, @@ -960,7 +1134,7 @@ func TestLightningInvoiceWithAmountAndDescription(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.SubmarineSwap = &SubmarineSwap{ Fees: &SwapFees{ RoutingFeeInSat: 0, @@ -1008,7 +1182,7 @@ func TestLightningAmountlessInvoice(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.SubmarineSwap = &SubmarineSwap{ BestRouteFees: []*BestRouteFees{ { @@ -1076,7 +1250,7 @@ func TestInvoiceOneConf(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.SubmarineSwap = &SubmarineSwap{ BestRouteFees: []*BestRouteFees{ { @@ -1146,7 +1320,7 @@ func TestAmountConversion(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.SubmarineSwap = &SubmarineSwap{ BestRouteFees: []*BestRouteFees{ { @@ -1203,7 +1377,7 @@ func TestInvoiceUnpayable(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.SubmarineSwap = &SubmarineSwap{ BestRouteFees: []*BestRouteFees{ { @@ -1255,7 +1429,7 @@ func TestInvoiceLend(t *testing.T) { resolveState := listener.next().(*ResolveState) - context := createContext() + context := createInitialContext() context.SubmarineSwap = &SubmarineSwap{ Fees: &SwapFees{ RoutingFeeInSat: 0, @@ -1319,12 +1493,13 @@ func TestAmountInfo_Mutating(t *testing.T) { //goland:noinspection GoUnhandledErrorResult func TestOnChainTFFAWithDebtFeeNeedsChangeBecauseOutputAmountLowerThanDust(t *testing.T) { + setupStateTests(t) listener := newTestListener() startState := NewOperationFlow(listener) startState.Resolve("bitcoin:bcrt1qj35fkq34xend9w0ssthn432vl9pxxsuy0epzlu", libwallet.Regtest()) - context := createContext() + context := createInitialContext() nts := &NextTransactionSize{} nts.AddSizeForAmount(&SizeForAmount{ diff --git a/libwallet/newop/swaps.go b/libwallet/newop/swaps.go index 2600406c..68a1ad03 100644 --- a/libwallet/newop/swaps.go +++ b/libwallet/newop/swaps.go @@ -1,7 +1,7 @@ package newop import ( - "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/btcutil" "github.com/muun/libwallet/fees" ) diff --git a/libwallet/nonces.go b/libwallet/nonces.go new file mode 100644 index 00000000..7f3724b7 --- /dev/null +++ b/libwallet/nonces.go @@ -0,0 +1,92 @@ +package libwallet + +import ( + "encoding/hex" + + "github.com/muun/libwallet/addresses" + "github.com/muun/libwallet/musig" +) + +type MusigNonces struct { + sessionIds [][32]byte + publicNonces [][66]byte + addressVersions []int +} + +func (m *MusigNonces) GetPubnonceHex(index int) string { + return hex.EncodeToString(m.publicNonces[index][:]) +} + +// NOTE: this function only generates v040 nonces, used until GenerateNonce is fully adopted. +// after that this function should be deleted. Currently, this function is only used by gomobile +func GenerateMusigNonces(count int) *MusigNonces { + sessionIds := make([][32]byte, 0) + publicNonces := make([][66]byte, 0) + addressVersions := make([]int, 0) + + for i := 0; i < count; i += 1 { + sessionIds = append(sessionIds, musig.RandomSessionId()) + nonce, _ := musig.MuSig2GenerateNonce( + musig.Musig2v040Muun, + sessionIds[i][:], + nil, + ) + publicNonces = append(publicNonces, nonce.PubNonce) + addressVersions = append(addressVersions, addresses.V5) + } + + return &MusigNonces{ + sessionIds, + publicNonces, + addressVersions, + } +} + +func EmptyMusigNonces() *MusigNonces { + sessionIds := make([][32]byte, 0) + publicNonces := make([][66]byte, 0) + addressVersions := make([]int, 0) + + return &MusigNonces{ + sessionIds, + publicNonces, + addressVersions, + } +} + +// Generates a nonce for a specific address version. Returns the index of the +// generated nonce and reallocates the arrays of the current MusigNonces. +func (nonces *MusigNonces) GenerateNonce(addressVersion int, signerPubKeySerialized []byte) (int, error) { + sessionId := musig.RandomSessionId() + + return nonces.generateStaticNonce(addressVersion, signerPubKeySerialized, sessionId) +} + +// PREFER GenerateNonce, this function exists for tests only. +// +// Generates a nonce for a specific address version. Returns the index of the +// generated nonce and reallocates the arrays of the current MusigNonces. +// The provided sessionId MUST NOT be reused, it MUST be used only once. +func (nonces *MusigNonces) generateStaticNonce(addressVersion int, signerPubKeySerialized []byte, sessionId [32]byte) (int, error) { + musigVersion := addresses.MusigVersionForAddress(addressVersion) + + signerPubKey, err := musig.ParsePubKey(musigVersion, signerPubKeySerialized) + if err != nil { + return 0, err + } + + nonce, err := musig.MuSig2GenerateNonce( + musigVersion, + sessionId[:], + signerPubKey.SerializeCompressed(), + ) + if err != nil { + return 0, err + } + + nonces.addressVersions = append(nonces.addressVersions, addressVersion) + nonces.sessionIds = append(nonces.sessionIds, sessionId) + nonces.publicNonces = append(nonces.publicNonces, nonce.PubNonce) + + return len(nonces.sessionIds) - 1, nil +} diff --git a/libwallet/operation/fee_bump.go b/libwallet/operation/fee_bump.go new file mode 100644 index 00000000..3239f4b7 --- /dev/null +++ b/libwallet/operation/fee_bump.go @@ -0,0 +1,59 @@ +package operation + +import ( + "errors" + "time" +) + +type FeeBumpFunction struct { + CreatedAt time.Time + // it is provided in order by the backend + PartialLinearFunctions []*PartialLinearFunction +} + +type PartialLinearFunction struct { + LeftClosedEndpoint float64 + RightOpenEndpoint float64 + Slope float64 + Intercept float64 +} + +func (pf *PartialLinearFunction) evaluate(feeRateInSatsPerVByte float64) (float64, error) { + if feeRateInSatsPerVByte >= pf.LeftClosedEndpoint && feeRateInSatsPerVByte < pf.RightOpenEndpoint { + return pf.Slope*feeRateInSatsPerVByte + pf.Intercept, nil + } + return 0, errors.New("fee rate does not belong to this interval") +} + +func (fb *FeeBumpFunction) getPartialLinearFunctionForFeeRate( + feeRateInSatsPerVByte float64, +) (*PartialLinearFunction, error) { + for _, partialLinearFunction := range fb.PartialLinearFunctions { + if feeRateInSatsPerVByte >= partialLinearFunction.LeftClosedEndpoint && feeRateInSatsPerVByte < partialLinearFunction.RightOpenEndpoint { + return partialLinearFunction, nil + } + } + return nil, errors.New("there is no function with this fee rate interval") +} + +// GetBumpAmountForFeeRate assumes that there is no overlap between the intervals. +func (f *FeeBumpFunction) GetBumpAmountForFeeRate(feeRateInSatsPerVByte float64) (int64, error) { + if f.PartialLinearFunctions == nil { + return 0, errors.New("fee bump function does not exist") + } + + partialLinearFunction, err := f.getPartialLinearFunctionForFeeRate(feeRateInSatsPerVByte) + + if err != nil { + return 0, err + } + + bumpAmount, err := partialLinearFunction.evaluate(feeRateInSatsPerVByte) + + // interval range is checked again inside evaluate function + if err != nil { + return 0, err + } + + return int64(bumpAmount), nil +} diff --git a/libwallet/operation/fee_bump_test.go b/libwallet/operation/fee_bump_test.go new file mode 100644 index 00000000..ff13170c --- /dev/null +++ b/libwallet/operation/fee_bump_test.go @@ -0,0 +1,155 @@ +package operation + +import ( + "math" + "testing" +) + +func TestPartialLinearFunction(t *testing.T) { + testCases := []struct { + desc string + partialLinearFunction *PartialLinearFunction + feeRateInSatsPerVByte float64 + bumpAmountExpected float64 + err bool + }{ + { + desc: "evaluate bump amount inside interval", + partialLinearFunction: &PartialLinearFunction{ + LeftClosedEndpoint: 0, + RightOpenEndpoint: math.Inf(1), + Slope: 2, + Intercept: 100, + }, + feeRateInSatsPerVByte: 100, + bumpAmountExpected: 300, + err: false, + }, + { + desc: "evaluate bump amount outside interval", + partialLinearFunction: &PartialLinearFunction{ + LeftClosedEndpoint: 0, + RightOpenEndpoint: 100, + Slope: 2, + Intercept: 100, + }, + feeRateInSatsPerVByte: 200, + bumpAmountExpected: 0, + err: true, + }, + { + desc: "evaluate bump amount in right open interval", + partialLinearFunction: &PartialLinearFunction{ + LeftClosedEndpoint: 0, + RightOpenEndpoint: 100, + Slope: 2, + Intercept: 100, + }, + feeRateInSatsPerVByte: 100, + bumpAmountExpected: 0, + err: true, + }, + } + + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + bumpAmount, err := tC.partialLinearFunction.evaluate(tC.feeRateInSatsPerVByte) + + if err == nil && tC.err { + t.Fatal("expected error") + } + + if bumpAmount != tC.bumpAmountExpected { + t.Fatalf("expected fee bump to be %v, but got %v", bumpAmount, tC.bumpAmountExpected) + } + }) + } +} + +func TestFeeBumpFunction(t *testing.T) { + testCases := []struct { + desc string + feeBumpFunction *FeeBumpFunction + feeRateInSatsPerVByte float64 + bumpAmountExpected int64 + err bool + }{ + { + desc: "fee bump function with one partial function", + feeBumpFunction: &FeeBumpFunction{ + PartialLinearFunctions: []*PartialLinearFunction{ + { + LeftClosedEndpoint: 0, + RightOpenEndpoint: math.Inf(1), + Slope: 2, + Intercept: 100, + }, + }, + }, + feeRateInSatsPerVByte: 100, + bumpAmountExpected: 300, + err: false, + }, + { + desc: "fee bump function with two partial functions", + feeBumpFunction: &FeeBumpFunction{ + PartialLinearFunctions: []*PartialLinearFunction{ + { + LeftClosedEndpoint: 0, + RightOpenEndpoint: 100, + Slope: 2, + Intercept: 100, + }, + { + LeftClosedEndpoint: 100, + RightOpenEndpoint: math.Inf(1), + Slope: 3, + Intercept: 200, + }, + }, + }, + feeRateInSatsPerVByte: 200, + bumpAmountExpected: 800, + err: false, + }, + { + desc: "fee bump function without partial functions should return error", + feeBumpFunction: &FeeBumpFunction{ + PartialLinearFunctions: nil, + }, + feeRateInSatsPerVByte: 100, + bumpAmountExpected: 0, + err: true, + }, + { + desc: "fee bump function with fee rate outside any interval should return error", + feeBumpFunction: &FeeBumpFunction{ + PartialLinearFunctions: []*PartialLinearFunction{ + { + LeftClosedEndpoint: 0, + RightOpenEndpoint: math.Inf(1), + Slope: 2, + Intercept: 100, + }, + }, + }, + feeRateInSatsPerVByte: -1, + bumpAmountExpected: 0, + err: true, + }, + } + + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + bumpAmount, err := tC.feeBumpFunction.GetBumpAmountForFeeRate(tC.feeRateInSatsPerVByte) + + if err == nil && tC.err { + t.Fatal("expected error") + } + + if bumpAmount != tC.bumpAmountExpected { + t.Fatalf("expected fee bump to be %v, but got %v", bumpAmount, tC.bumpAmountExpected) + } + }) + } +} diff --git a/libwallet/operation/fees.go b/libwallet/operation/fees.go index 53edf805..2294b466 100644 --- a/libwallet/operation/fees.go +++ b/libwallet/operation/fees.go @@ -1,11 +1,15 @@ package operation import ( + "errors" + "fmt" "math" ) type feeCalculator struct { NextTransactionSize *NextTransactionSize + // there is a fee bump function for each unconfirmed utxo, in the same order as they appear in the NTS. + feeBumpFunctions []*FeeBumpFunction } // Fee DOES NOT return error when amount > balance. Instead we return the fee it would take to @@ -18,43 +22,84 @@ func (f *feeCalculator) Fee(amountInSat int64, feeRateInSatsPerVByte float64, ta if amountInSat == 0 { return 0 } - if takeFeeFromAmount { - return f.feeFromAmount(amountInSat, feeRateInSatsPerVByte) - } else { - return f.feeFromRemainingBalance(amountInSat, feeRateInSatsPerVByte) - } + return f.calculateFee(amountInSat, feeRateInSatsPerVByte, takeFeeFromAmount) } -func (f *feeCalculator) feeFromAmount(amountInSat int64, feeRateInSatsPerVByte float64) int64 { +func (f *feeCalculator) calculateFee(amountInSat int64, feeRateInSatsPerVByte float64, takeFeeFromAmount bool) int64 { if f.NextTransactionSize == nil { return 0 } var fee int64 + lastUnconfirmedUtxoUsedIndex := -1 for _, sizeForAmount := range f.NextTransactionSize.SizeProgression { - fee = computeFee(sizeForAmount.SizeInVByte, feeRateInSatsPerVByte) - if sizeForAmount.AmountInSat >= amountInSat { - break // no more UTXOs needed + // this code assumes that sizeProgression has the same order as used when fee bump functions was generated. + if sizeForAmount.UtxoStatus == UtxosStatusUnconfirmed { + lastUnconfirmedUtxoUsedIndex++ + } + + var feeBumpAmount int64 = 0 + if lastUnconfirmedUtxoUsedIndex >= 0 { + var err error + feeBumpAmount, err = f.calculateFeeBumpAmount(lastUnconfirmedUtxoUsedIndex, feeRateInSatsPerVByte) + if err != nil { + // TODO: Add listener to track non-fatal error. + fmt.Printf("Non-fatal error calculating fee bump amount: %v\n", err.Error()) + } + } + + fee = computeFee(sizeForAmount.SizeInVByte, feeRateInSatsPerVByte, feeBumpAmount) + if takeFeeFromAmount { + if sizeForAmount.AmountInSat >= amountInSat { + break // no more UTXOs needed + } + } else { + if sizeForAmount.AmountInSat >= amountInSat+fee { + break // no more UTXOs needed + } } } return fee } -func (f *feeCalculator) feeFromRemainingBalance(amountInSat int64, feeRateInSatsPerVByte float64) int64 { - if f.NextTransactionSize == nil { - return 0 +func computeFee(sizeInVByte int64, feeRate float64, feeBumpAmount int64) int64 { + return int64(math.Ceil(float64(sizeInVByte)*feeRate)) + feeBumpAmount +} + +// calculateFeeBumpAmount calculates the fee needed to bump the unconfirmed ancestors of the +// transaction via child-pays-for-parent. +// If the order among unconfirmed utxos in NTS and fee bump functions is broken, +// the fee bump function related code will not work as expected. +// it handles the case when lastUnconfirmedUtxoUsedIndex is greater than feeBumpFunctions length +func (f *feeCalculator) calculateFeeBumpAmount( + lastUnconfirmedUtxoUsedIndex int, + feeRateInSatsPerVByte float64, +) (int64, error) { + + // If feature flag is OFF, this can be nil + if len(f.feeBumpFunctions) == 0 { + // Non-fatal error, sending for tracking. Fee bump amount will be 0 + return 0, errors.New("fee bump functions were not loaded") } - var fee int64 - for _, sizeForAmount := range f.NextTransactionSize.SizeProgression { - fee = computeFee(sizeForAmount.SizeInVByte, feeRateInSatsPerVByte) - if sizeForAmount.AmountInSat >= amountInSat+fee { - break // no more UTXOs needed - } + functionIndex := f.getFeeBumpFunctionIndex(lastUnconfirmedUtxoUsedIndex) + + feeBumpAmount, err := f.feeBumpFunctions[functionIndex].GetBumpAmountForFeeRate(feeRateInSatsPerVByte) + + if err != nil { + return 0, err } - return fee + + return feeBumpAmount, nil } -func computeFee(sizeInVByte int64, feeRate float64) int64 { - return int64(math.Ceil(float64(sizeInVByte) * feeRate)) +func (f *feeCalculator) getFeeBumpFunctionIndex(lastUnconfirmedUtxoUsedIndex int) int { + // We might have fewer fee bump functions than unconfirmed UTXOs; in that case, + // we should use the last fee bump function. + // There are no gaps in the middle, meaning the last N UTXOs will use the last fee bump function. + // If this order is broken, the fee bump function related code will not work as expected. + if lastUnconfirmedUtxoUsedIndex >= len(f.feeBumpFunctions) { + lastUnconfirmedUtxoUsedIndex = len(f.feeBumpFunctions) - 1 + } + return lastUnconfirmedUtxoUsedIndex } diff --git a/libwallet/operation/fees_test.go b/libwallet/operation/fees_test.go index 390f6aff..2b6761cd 100644 --- a/libwallet/operation/fees_test.go +++ b/libwallet/operation/fees_test.go @@ -1,6 +1,7 @@ package operation import ( + "math" "testing" ) @@ -11,18 +12,26 @@ var defaultNts = &NextTransactionSize{ { AmountInSat: 103_456, SizeInVByte: 110, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c1:0", + UtxoStatus: UtxosStatusUnconfirmed, }, { AmountInSat: 20_345_678, SizeInVByte: 230, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c2:0", + UtxoStatus: UtxosStatusConfirmed, }, { AmountInSat: 303_456_789, SizeInVByte: 340, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c3:0", + UtxoStatus: UtxosStatusUnconfirmed, }, { AmountInSat: 703_456_789, SizeInVByte: 580, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c4:0", + UtxoStatus: UtxosStatusConfirmed, }, }, ExpectedDebtInSat: 0, @@ -64,6 +73,48 @@ var singleNegativeUtxoNts = &NextTransactionSize{ ExpectedDebtInSat: 0, } +var firstFeeBumpFunction = []*PartialLinearFunction{ + &PartialLinearFunction{ + LeftClosedEndpoint: 0, + RightOpenEndpoint: 50, + Slope: 2, + Intercept: 100, + }, + &PartialLinearFunction{ + LeftClosedEndpoint: 50, + RightOpenEndpoint: 100, + Slope: 3, + Intercept: 200, + }, + &PartialLinearFunction{ + LeftClosedEndpoint: 100, + RightOpenEndpoint: math.Inf(1), + Slope: 4, + Intercept: 300, + }, +} + +var secondFeeBumpFunction = []*PartialLinearFunction{ + &PartialLinearFunction{ + LeftClosedEndpoint: 100, + RightOpenEndpoint: math.Inf(1), + Slope: 7, + Intercept: 300, + }, + &PartialLinearFunction{ + LeftClosedEndpoint: 50, + RightOpenEndpoint: 100, + Slope: 6, + Intercept: 200, + }, + &PartialLinearFunction{ + LeftClosedEndpoint: 0, + RightOpenEndpoint: 50, + Slope: 5, + Intercept: 100, + }, +} + func TestFeeCalculatorForAmountZero(t *testing.T) { testCases := []struct { desc string @@ -97,7 +148,7 @@ func TestFeeCalculatorForAmountZero(t *testing.T) { } for _, nts := range allNts { - calculator := feeCalculator{&nts} + calculator := feeCalculator{&nts, nil} feeInSat := calculator.Fee(0, tC.feeRateInSatsPerVbyte, tC.takeFeeFromAmount) if feeInSat != tC.expectedFeeInSat { @@ -152,7 +203,9 @@ func TestFeeCalculator(t *testing.T) { }, }, ExpectedDebtInSat: 0, - }}, + }, + nil, + }, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: 2400, @@ -160,7 +213,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "fails when balance is zero", amountInSat: 1, - feeCalculator: &feeCalculator{emptyNts}, + feeCalculator: &feeCalculator{emptyNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: 0, @@ -168,7 +221,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "fails when balance is zero with TFFA", amountInSat: 1, - feeCalculator: &feeCalculator{emptyNts}, + feeCalculator: &feeCalculator{emptyNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: true, expectedFeeInSat: 0, @@ -176,7 +229,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "fails when amount greater than balance", amountInSat: defaultNts.TotalBalance() + 1, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: 5800, @@ -184,7 +237,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "fails when amount greater than balance with TFFA", amountInSat: defaultNts.TotalBalance() + 1, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: true, expectedFeeInSat: 5800, @@ -192,7 +245,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "calculates when amount plus fee is greater than balance", amountInSat: defaultNts.TotalBalance() - 1, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: 5800, @@ -200,7 +253,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "calculates reduced amount and fee with TFFA", amountInSat: 10_345_678, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: true, expectedFeeInSat: 2300, @@ -210,7 +263,7 @@ func TestFeeCalculator(t *testing.T) { // We don't handle that precondition in FeeCalculator to keep its API simple (no error handling) desc: "calculates when no amount is left after TFFA", amountInSat: 10, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: true, expectedFeeInSat: 1100, @@ -218,7 +271,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "calculates use-all-funds fee with TFFA", amountInSat: defaultNTS.TotalBalance(), - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: true, expectedFeeInSat: 2300, @@ -226,7 +279,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "calculates when paying fee does not require an additional UTXO (1)", amountInSat: defaultNts.SizeProgression[0].AmountInSat / 2, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: defaultNts.SizeProgression[0].SizeInVByte * 10, @@ -234,7 +287,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "calculates when paying fee does not require an additional UTXO (2)", amountInSat: defaultNts.SizeProgression[1].AmountInSat / 2, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: defaultNts.SizeProgression[1].SizeInVByte * 10, @@ -242,7 +295,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "calculates when paying fee does not require an additional UTXO (3)", amountInSat: defaultNts.SizeProgression[2].AmountInSat / 2, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: defaultNts.SizeProgression[2].SizeInVByte * 10, @@ -250,7 +303,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "calculates when paying fee does not require an additional UTXO (4)", amountInSat: defaultNts.SizeProgression[3].AmountInSat / 2, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: defaultNts.SizeProgression[3].SizeInVByte * 10, @@ -258,7 +311,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "calculates when paying fee requires an additional UTXO (1)", amountInSat: defaultNts.SizeProgression[0].AmountInSat - 1, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: defaultNts.SizeProgression[1].SizeInVByte * 10, @@ -266,7 +319,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "calculates when paying fee requires an additional UTXO (2)", amountInSat: defaultNts.SizeProgression[1].AmountInSat - 1, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: defaultNts.SizeProgression[2].SizeInVByte * 10, @@ -274,7 +327,7 @@ func TestFeeCalculator(t *testing.T) { { desc: "calculates when paying fee requires an additional UTXO (3)", amountInSat: defaultNts.SizeProgression[2].AmountInSat - 1, - feeCalculator: &feeCalculator{defaultNts}, + feeCalculator: &feeCalculator{defaultNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: defaultNts.SizeProgression[3].SizeInVByte * 10, @@ -282,11 +335,80 @@ func TestFeeCalculator(t *testing.T) { { desc: "calculates when negative UTXOs are larger than positive UTXOs", amountInSat: 1, - feeCalculator: &feeCalculator{singleNegativeUtxoNts}, + feeCalculator: &feeCalculator{singleNegativeUtxoNts, nil}, feeRateInSatsPerVbyte: 10, takeFeeFromAmount: false, expectedFeeInSat: 8400, // which is > 64, aka singleNegativeUtxoNts.TotalBalance() }, + { + desc: "calculates when feeBumpFunctions is loaded using 1 utxo unconfirmed", + amountInSat: defaultNts.SizeProgression[1].AmountInSat / 2, + feeCalculator: &feeCalculator{ + NextTransactionSize: defaultNts, + feeBumpFunctions: []*FeeBumpFunction{ + &FeeBumpFunction{PartialLinearFunctions: firstFeeBumpFunction}, + &FeeBumpFunction{PartialLinearFunctions: secondFeeBumpFunction}, + }, + }, + feeRateInSatsPerVbyte: 10, + takeFeeFromAmount: false, + expectedFeeInSat: 2420, + }, + { + desc: "calculates when feeBumpFunctions is loaded using 2 unconfirmed utxos", + amountInSat: defaultNts.SizeProgression[2].AmountInSat / 2, + feeCalculator: &feeCalculator{ + NextTransactionSize: defaultNts, + feeBumpFunctions: []*FeeBumpFunction{ + &FeeBumpFunction{PartialLinearFunctions: firstFeeBumpFunction}, + &FeeBumpFunction{PartialLinearFunctions: secondFeeBumpFunction}, + }, + }, + feeRateInSatsPerVbyte: 10, + takeFeeFromAmount: false, + expectedFeeInSat: 3550, + }, + { + desc: "calculates when we have less feeBumpFunctions than unconfirmed utxos (use the last function)", + amountInSat: defaultNts.SizeProgression[2].AmountInSat, + feeCalculator: &feeCalculator{ + NextTransactionSize: defaultNts, + feeBumpFunctions: []*FeeBumpFunction{ + &FeeBumpFunction{PartialLinearFunctions: firstFeeBumpFunction}, + }, + }, + feeRateInSatsPerVbyte: 10, + takeFeeFromAmount: true, + expectedFeeInSat: 3520, + }, + { + desc: "calculates when it does not have unconfirmed utxos", + amountInSat: singleNts.SizeProgression[0].AmountInSat / 2, + feeCalculator: &feeCalculator{ + NextTransactionSize: singleNts, + feeBumpFunctions: []*FeeBumpFunction{ + &FeeBumpFunction{PartialLinearFunctions: firstFeeBumpFunction}, + &FeeBumpFunction{PartialLinearFunctions: secondFeeBumpFunction}, + }, + }, + feeRateInSatsPerVbyte: 10, + takeFeeFromAmount: false, + expectedFeeInSat: 4000, + }, + { + desc: "calculates when feeRate is exactly on left/right endpoint", + amountInSat: defaultNts.SizeProgression[0].AmountInSat / 2, + feeCalculator: &feeCalculator{ + NextTransactionSize: defaultNts, + feeBumpFunctions: []*FeeBumpFunction{ + &FeeBumpFunction{PartialLinearFunctions: firstFeeBumpFunction}, + &FeeBumpFunction{PartialLinearFunctions: secondFeeBumpFunction}, + }, + }, + feeRateInSatsPerVbyte: 100, + takeFeeFromAmount: false, + expectedFeeInSat: 11700, + }, } for _, tC := range testCases { t.Run(tC.desc, func(t *testing.T) { diff --git a/libwallet/operation/payment_analyzer.go b/libwallet/operation/payment_analyzer.go index 7d18cd7b..bee5425f 100644 --- a/libwallet/operation/payment_analyzer.go +++ b/libwallet/operation/payment_analyzer.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/btcutil" "github.com/muun/libwallet/fees" ) @@ -125,11 +125,15 @@ type PaymentAnalysis struct { TotalInSat int64 // AmountInSat + fees (may include other than FeeInSat). May provide extra information in case of error status (e.g payment can't be made). } -func NewPaymentAnalyzer(feeWindow *FeeWindow, nts *NextTransactionSize) *PaymentAnalyzer { +func NewPaymentAnalyzer( + feeWindow *FeeWindow, + nts *NextTransactionSize, + feeBumpFunctions []*FeeBumpFunction, +) *PaymentAnalyzer { return &PaymentAnalyzer{ feeWindow: feeWindow, nextTransactionSize: nts, - feeCalculator: &feeCalculator{nts}, + feeCalculator: &feeCalculator{nts, feeBumpFunctions}, } } diff --git a/libwallet/operation/payment_analyzer_test.go b/libwallet/operation/payment_analyzer_test.go index 34d5d163..51822f7b 100644 --- a/libwallet/operation/payment_analyzer_test.go +++ b/libwallet/operation/payment_analyzer_test.go @@ -4,6 +4,7 @@ import ( "math" "reflect" "testing" + "time" "github.com/muun/libwallet/fees" ) @@ -38,10 +39,18 @@ var defaultNTS = &NextTransactionSize{ ExpectedDebtInSat: 0, } +var partialLinearFunction = &PartialLinearFunction{ + LeftClosedEndpoint: 0, + RightOpenEndpoint: math.Inf(1), + Slope: 2, + Intercept: 100, +} + func TestAnalyzeOnChain(t *testing.T) { testCases := []struct { desc string nts *NextTransactionSize + feeBump []*FeeBumpFunction payment *PaymentToAddress expected *PaymentAnalysis err bool @@ -199,6 +208,162 @@ func TestAnalyzeOnChain(t *testing.T) { TotalInSat: 12399, }, }, + { + desc: "success with fee bump", + nts: &NextTransactionSize{ + SizeProgression: []SizeForAmount{ + { + AmountInSat: 10_000, + SizeInVByte: 240, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c4:0", + UtxoStatus: UtxosStatusUnconfirmed, + }, + }, + ExpectedDebtInSat: 0, + }, + feeBump: []*FeeBumpFunction{ + &FeeBumpFunction{ + time.Now(), + []*PartialLinearFunction{ + partialLinearFunction, + }}, + &FeeBumpFunction{ + time.Now(), + []*PartialLinearFunction{ + partialLinearFunction, + }}, + &FeeBumpFunction{ + time.Now(), + []*PartialLinearFunction{ + partialLinearFunction, + }}, + }, + payment: &PaymentToAddress{ + TakeFeeFromAmount: false, + AmountInSat: 7480, + FeeRateInSatsPerVByte: 10, + }, + expected: &PaymentAnalysis{ + Status: AnalysisStatusOk, + AmountInSat: 7480, + FeeInSat: 2520, + TotalInSat: 10000, + }, + }, + { + desc: "fee bump with several unconfirmed utxos", + nts: &NextTransactionSize{ + SizeProgression: []SizeForAmount{ + { + AmountInSat: 10_000, + SizeInVByte: 240, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c4:0", + UtxoStatus: UtxosStatusConfirmed, + }, + { + AmountInSat: 20_000, + SizeInVByte: 440, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c3:0", + UtxoStatus: UtxosStatusUnconfirmed, + }, + { + AmountInSat: 30_000, + SizeInVByte: 780, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c2:0", + UtxoStatus: UtxosStatusUnconfirmed, + }, + }, + ExpectedDebtInSat: 0, + }, + feeBump: []*FeeBumpFunction{ + &FeeBumpFunction{time.Now(), firstFeeBumpFunction}, + &FeeBumpFunction{time.Now(), secondFeeBumpFunction}, + }, + payment: &PaymentToAddress{ + TakeFeeFromAmount: false, + AmountInSat: 12000, + FeeRateInSatsPerVByte: 10, + }, + expected: &PaymentAnalysis{ + Status: AnalysisStatusOk, + AmountInSat: 12000, + FeeInSat: 4520, + TotalInSat: 16520, + }, + }, + { + desc: "fee bump with total balance and takeFeeFromAmount is true", + nts: &NextTransactionSize{ + SizeProgression: []SizeForAmount{ + { + AmountInSat: 10_000, + SizeInVByte: 240, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c4:0", + UtxoStatus: UtxosStatusConfirmed, + }, + { + AmountInSat: 20_000, + SizeInVByte: 440, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c3:0", + UtxoStatus: UtxosStatusUnconfirmed, + }, + { + AmountInSat: 30_000, + SizeInVByte: 780, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c2:0", + UtxoStatus: UtxosStatusUnconfirmed, + }, + }, + ExpectedDebtInSat: 0, + }, + feeBump: []*FeeBumpFunction{ + &FeeBumpFunction{time.Now(), firstFeeBumpFunction}, + &FeeBumpFunction{time.Now(), secondFeeBumpFunction}, + }, + payment: &PaymentToAddress{ + TakeFeeFromAmount: true, + AmountInSat: 30000, + FeeRateInSatsPerVByte: 10, + }, + expected: &PaymentAnalysis{ + Status: AnalysisStatusOk, + AmountInSat: 22050, + FeeInSat: 7950, + TotalInSat: 30000, + }, + }, + { + desc: "valid amount but unpayable because of fee bump", + nts: &NextTransactionSize{ + SizeProgression: []SizeForAmount{ + { + AmountInSat: 10_000, + SizeInVByte: 240, + Outpoint: "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c4:0", + UtxoStatus: UtxosStatusUnconfirmed, + }, + }, + ExpectedDebtInSat: 0, + }, + feeBump: []*FeeBumpFunction{ + &FeeBumpFunction{ + time.Now(), + []*PartialLinearFunction{ + partialLinearFunction, + }}, + }, + payment: &PaymentToAddress{ + TakeFeeFromAmount: false, + AmountInSat: 7500, + FeeRateInSatsPerVByte: 10, + }, + expected: &PaymentAnalysis{ + Status: AnalysisStatusUnpayable, + AmountInSat: 7500, + FeeInSat: 2520, + TotalInSat: 10020, + }, + }, { desc: "valid amount plus fee using TFFA", nts: &NextTransactionSize{ @@ -425,9 +590,9 @@ func TestAnalyzeOnChain(t *testing.T) { var analyzer *PaymentAnalyzer if tC.nts != nil { - analyzer = NewPaymentAnalyzer(defaultFeeWindow, tC.nts) + analyzer = NewPaymentAnalyzer(defaultFeeWindow, tC.nts, tC.feeBump) } else { - analyzer = NewPaymentAnalyzer(defaultFeeWindow, defaultNTS) + analyzer = NewPaymentAnalyzer(defaultFeeWindow, defaultNTS, tC.feeBump) } analysis, err := analyzer.ToAddress(tC.payment) @@ -454,7 +619,9 @@ func TestAnalyzeOnChainValidAmountButUnpayableWithAnyFee(t *testing.T) { }, }, ExpectedDebtInSat: 0, - }) + }, + nil, + ) analysis, err := analyzer.ToAddress(&PaymentToAddress{ TakeFeeFromAmount: false, @@ -503,7 +670,9 @@ func TestAnalyzeOnChainValidAmountButUnpayableWithAnyFeeUsingTFFA(t *testing.T) }, }, ExpectedDebtInSat: 0, - }) + }, + nil, + ) analysis, err := analyzer.ToAddress(&PaymentToAddress{ TakeFeeFromAmount: true, @@ -2218,7 +2387,7 @@ func TestAnalyzeOffChain(t *testing.T) { feeWindow = tC.feeWindow } - analyzer := NewPaymentAnalyzer(feeWindow, nts) + analyzer := NewPaymentAnalyzer(feeWindow, nts, nil) analysis, err := analyzer.ToInvoice(tC.payment) if err == nil && tC.err { diff --git a/libwallet/partiallysignedtransaction.go b/libwallet/partiallysignedtransaction.go index 0452d381..80752fc6 100644 --- a/libwallet/partiallysignedtransaction.go +++ b/libwallet/partiallysignedtransaction.go @@ -12,8 +12,8 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" ) type SigningExpectations struct { @@ -85,6 +85,8 @@ type Input interface { type PartiallySignedTransaction struct { tx *wire.MsgTx inputs []Input + + // UserNonces nonces *MusigNonces } @@ -108,7 +110,7 @@ func (l *InputList) Inputs() []Input { } func NewPartiallySignedTransaction( - inputs *InputList, rawTx []byte, nonces *MusigNonces, + inputs *InputList, rawTx []byte, userNonces *MusigNonces, ) (*PartiallySignedTransaction, error) { tx := wire.NewMsgTx(0) @@ -120,7 +122,7 @@ func NewPartiallySignedTransaction( return &PartiallySignedTransaction{ tx: tx, inputs: inputs.Inputs(), - nonces: nonces, + nonces: userNonces, }, nil } @@ -375,7 +377,7 @@ type coin interface { FullySignInput(index int, tx *wire.MsgTx, userKey, muunKey *HDPrivateKey) error } -func createCoin(index int, input Input, network *Network, sigHashes *txscriptw.TaprootSigHashes, nonces *MusigNonces) (coin, error) { +func createCoin(index int, input Input, network *Network, sigHashes *txscriptw.TaprootSigHashes, userNonces *MusigNonces) (coin, error) { txID, err := chainhash.NewHash(input.OutPoint().TxId()) if err != nil { return nil, err @@ -389,6 +391,13 @@ func createCoin(index int, input Input, network *Network, sigHashes *txscriptw.T version := input.Address().Version() + if userNonces == nil { + return nil, fmt.Errorf("userNonces cannot be nil") + } + if len(userNonces.sessionIds) <= index { + return nil, fmt.Errorf("not enough nonces were provided") + } + switch version { case addresses.V1: return &coinV1{ @@ -429,7 +438,22 @@ func createCoin(index int, input Input, network *Network, sigHashes *txscriptw.T OutPoint: outPoint, KeyPath: keyPath, Amount: amount, - UserSessionId: nonces.sessionIds[index], + UserSessionId: userNonces.sessionIds[index], + MuunPubNonce: nonce, + MuunPartialSig: muunPartialSig, + SigHashes: sigHashes, + }, nil + case addresses.V6: + var nonce [66]byte + copy(nonce[:], input.MuunPublicNonce()) + var muunPartialSig [32]byte + copy(muunPartialSig[:], input.MuunSignature()) + return &coinV6{ + Network: network.network, + OutPoint: outPoint, + KeyPath: keyPath, + Amount: amount, + UserSessionId: userNonces.sessionIds[index], MuunPubNonce: nonce, MuunPartialSig: muunPartialSig, SigHashes: sigHashes, diff --git a/libwallet/partiallysignedtransaction_test.go b/libwallet/partiallysignedtransaction_test.go index 87e42423..8951575f 100644 --- a/libwallet/partiallysignedtransaction_test.go +++ b/libwallet/partiallysignedtransaction_test.go @@ -1,5 +1,10 @@ package libwallet +// Tests matching PartiallySignedTransaction_Sign* are generated using the mobile +// app directly or Houston's OperationTest::generateTestForLibwallet. +// those tests will ensure that the backend is creating a valid partially signed +// transaction matching the intent of our inputs + import ( "bytes" "encoding/hex" @@ -7,6 +12,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/test-go/testify/require" "github.com/muun/libwallet/addresses" "github.com/muun/libwallet/walletdb" @@ -24,6 +30,7 @@ type input struct { submarineSwapV1 inputSubmarineSwapV1 submarineSwapV2 inputSubmarineSwapV2 incomingSwap inputIncomingSwap + muunPublicNonce []byte } func (i *input) OutPoint() Outpoint { @@ -55,7 +62,7 @@ func (i *input) IncomingSwap() InputIncomingSwap { } func (i *input) MuunPublicNonce() []byte { - return nil + return i.muunPublicNonce } type outpoint struct { @@ -175,6 +182,26 @@ func (i *inputIncomingSwap) CollectInSats() int64 { return i.collectInSats } +// generates test nonces using fixed sessionIds and a list of inputs +func createTestNonces(t *testing.T, userKey *HDPrivateKey, inputList *InputList, userSessionIds []string) *MusigNonces { + nonces := EmptyMusigNonces() + for index := range inputList.inputs { + input := inputList.inputs[index] + inputSignerKey, err := userKey.DeriveTo(input.Address().DerivationPath()) + require.NoError(t, err) + + var sessionId [32]byte + copy(sessionId[:], hexToBytes(userSessionIds[index])) + + nonces.generateStaticNonce( + input.Address().Version(), + inputSignerKey.PublicKey().Raw(), + sessionId, + ) + } + return nonces +} + func TestPartiallySignedTransaction_SignV1(t *testing.T) { const ( hexTx = "0100000001706bcabdcdcfd519bdb4534f8ace9f8a3cd614e7b00f074cce0a58913eadfffb0100000000ffffffff022cf46905000000001976a914072b22dfb34153d4e084dce8c6655430d37f12d088aca4de8b00000000001976a914fded0987447ef3273cde87bf8b65a11d1fd9caca88ac00000000" @@ -199,7 +226,8 @@ func TestPartiallySignedTransaction_SignV1(t *testing.T) { inputList := &InputList{inputs: inputs} rawTx, _ := hex.DecodeString(hexTx) - partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nil) + nonces := GenerateMusigNonces(len(inputList.inputs)) + partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nonces) userKey, _ := NewHDPrivateKeyFromString(encodedUserKey, basePath, Regtest()) // We dont need to use the muunKey in V1 @@ -282,7 +310,8 @@ func TestPartiallySignedTransaction_SignV2(t *testing.T) { inputList := &InputList{inputs: inputs} rawTx, _ := hex.DecodeString(hexTx) - partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nil) + nonces := GenerateMusigNonces(len(inputList.inputs)) + partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nonces) muunKey, _ := NewHDPublicKeyFromString(encodedMuunKey, basePath, Regtest()) userKey, _ := NewHDPrivateKeyFromString(encodedUserKey, basePath, Regtest()) @@ -331,7 +360,8 @@ func TestPartiallySignedTransaction_SignV3(t *testing.T) { inputList := &InputList{inputs: inputs} rawTx, _ := hex.DecodeString(hexTx) - partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nil) + nonces := GenerateMusigNonces(1) + partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nonces) muunKey, _ := NewHDPublicKeyFromString(encodedMuunKey, basePath, Regtest()) userKey, _ := NewHDPrivateKeyFromString(encodedUserKey, basePath, Regtest()) @@ -347,6 +377,164 @@ func TestPartiallySignedTransaction_SignV3(t *testing.T) { verifyInput(t, signedTx, hexTx1, txIndex1, 0) } +func TestPartiallySignedTransaction_SignV5(t *testing.T) { + var ( + encodedUserKey = "tprv8e6WDju7yhq6vuL8raFiMpCMVYpNEpjggzqcX3qW4zsjNBVnwKgAUmQ7vs7bDeHu598aG9teh7or5H8ifLJ2qhZGocBnDEBqAsTs3Gd6wG6" + encodedMuunKey = "tpubDBS2rf9CeryjGstPrQVSzQhLGLqFVqEq78xtK26h9fsN7udiokAMuu6DbSzwhSzqCwszcfC2L2zMYoFm9uoiJpkEwyUCuNr3j1XswbHcgAB" + hexTx = "0100000001239fc65d1212989754b0bb146ccc77db370de8db913a5bcbb1e5257ae75e03450100000000ffffffff0174850100000000002251203e3c9519c91c87e84de71a64f65fa481639c900da4e01ba4c23be539c9065ad400000000" + userSessionIds = []string{ + "3afcd7f2cc568aa60552866f7ee8d1de6a6b18293ae9a4cda167434588267f73", + } + inputTxRaw = []string{ + "020000000001019da62d921c36e14d8c8567adec86580e95154cddb922f28ed47552f1850272c10000000000feffffff028b4d042a01000000160014290cfc7b1cb593b8b7cfb70e8a5315cb04015fd7a0860100000000002251206814f05813d2505f80ec5a71ca6fbbd99df3aac5e0ed3a506a1c376beb484e9102473044022040d7a4204b14cf1ea32021040d373221f60fcfb42e4da82723b0eb3b7d7bd940022064160bd834d6b530292d9f6f17c07e032a619b88c8ffcb5ef6622a731236ba46012103ff6f07c524f89457d05a1ee9e2b8d1ae2520916748b2da0c7c1d3655f271855e25050000", + } + ) + inputList := &InputList{inputs: []Input{ + &input{ + outpoint: outpoint{index: 1, amount: 100000, txId: hexToBytes("45035ee77a25e5b1cb5b3a91dbe80d37db77cc6c14bbb054979812125dc69f23")}, + address: addresses.New(5, "m/schema:1'/recovery:1'/external:1/0", "bcrt1pdq20qkqn6fg9lq8vtfcu5mammxwl82k9urkn55r2rsmkh66gf6gsumc8uw"), + muunPublicNonce: hexToBytes("02b44aef04d3ada7270e1304f4ba1fbf20cca0ca81a80e23be9bf8f3aea7c0a62103f6c8cd699fc2339a33df60f142bb89807c081632a2241bd4af583961a29da9a0"), + muunSignature: hexToBytes("591f2e7afd46a8b234e94428e582f13324cd541d436bde62cef760354771a377"), + }, + }} + + userKey, _ := NewHDPrivateKeyFromString(encodedUserKey, basePath, Regtest()) + muunKey, _ := NewHDPublicKeyFromString(encodedMuunKey, basePath, Regtest()) + + nonces := createTestNonces(t, userKey, inputList, userSessionIds) + partial, _ := NewPartiallySignedTransaction(inputList, hexToBytes(hexTx), nonces) + signedRawTx, err := partial.Sign(userKey, muunKey) + + if err != nil { + t.Fatalf("failed to sign tx due to %v", err) + } + + signedTx := wire.NewMsgTx(0) + signedTx.Deserialize(bytes.NewReader(signedRawTx.Bytes)) + + // validate input signatures + for index := range inputList.inputs { + verifyInput(t, signedTx, inputTxRaw[index], inputList.inputs[index].OutPoint().Index(), index) + } +} + +func TestPartiallySignedTransaction_SignV6(t *testing.T) { + var ( + encodedUserKey = "tprv8dUpNFvQ6NpkxtuYDoDaibgQYbvUnHBqz8GM3zBL4DjNeh9uzhXC49xKx2VksbyxaW3dSFviExbUw4GmkEKJoiTx7UXXi6pPnXMWpB5Lmtf" + encodedMuunKey = "tpubDBmgp5wQ4SYkroyXQG3SxUXVZsdmJnL89exWksCAEq9xzujjCd6jpKbYQyyVXLiQk4gBq8AaUULZDbwxFF8DhcTEPzDFYY8g2dsJ1x3xPwN" + hexTx = "01000000017e282eab04b710e5f149e6a3f7dfb8dcb5af006aefecc7e5d2ad1f4bbb4d78db0000000000ffffffff017485010000000000225120e67c60c89364bae43a399e6417a1cce9d2e0498e5eb0646f52d3f279833a2b6000000000" + userSessionIds = []string{ + "98ca651de1178c9a656dfc51e00bb6ff3dd958922a15a84ef270a81c96bcf510", + } + inputTxRaw = []string{ + "02000000000101f8880208d09f48d849e68d042194b3295401a88dcc9fa5131ecc78130b7480840000000000feffffff02a086010000000000225120c6eae34415dcc0f4e40367348f483e7d6a30f969a54586fb699b5684988ddb1e8b4d042a01000000160014287cd946de86caf84c507e749f2fbe586bce57b30247304402203c30d6522a082228b6b9ee1bc93a576a46d37e17017db2ae7506c8f288ae181f02206f84ed0e17f70e8b49900450c29cfff84d321850a8e4f662216a4d77c3e80795012103ff6f07c524f89457d05a1ee9e2b8d1ae2520916748b2da0c7c1d3655f271855ef9040000", + } + ) + inputList := &InputList{inputs: []Input{ + &input{ + outpoint: outpoint{index: 0, amount: 100000, txId: hexToBytes("db784dbb4b1fadd2e5c7ecef6a00afb5dcb8dff7a3e649f1e510b704ab2e287e")}, + address: addresses.New(6, "m/schema:1'/recovery:1'/external:1/0", "bcrt1pcm4wx3q4mnq0feqrvu6g7jp7044rp7tf54zcd7mfndtgfxydmv0qnnmrdl"), + muunPublicNonce: hexToBytes("03722e555ae015f5e5b07ff8915fcf9a155ad74f77e27d5908b5a6c5ea313d71db0319258976bd5f967317c61ce634cf182ead2931fc54e31cc05729014b4d210f66"), + muunSignature: hexToBytes("f4d1fee38aebb1d17c0dc85b7a0e48474c0b1de351dfc7f0baa6e9330765881b"), + }, + }} + + userKey, _ := NewHDPrivateKeyFromString(encodedUserKey, basePath, Regtest()) + muunKey, _ := NewHDPublicKeyFromString(encodedMuunKey, basePath, Regtest()) + + nonces := createTestNonces(t, userKey, inputList, userSessionIds) + partial, _ := NewPartiallySignedTransaction(inputList, hexToBytes(hexTx), nonces) + signedRawTx, err := partial.Sign(userKey, muunKey) + + if err != nil { + t.Fatalf("failed to sign tx due to %v", err) + } + + signedTx := wire.NewMsgTx(0) + signedTx.Deserialize(bytes.NewReader(signedRawTx.Bytes)) + + // validate input signatures + for index := range inputList.inputs { + verifyInput(t, signedTx, inputTxRaw[index], inputList.inputs[index].OutPoint().Index(), index) + } +} + +func TestPartiallySignedTransaction_SignAll(t *testing.T) { + var ( + encodedUserKey = "tprv8dhZ55jWbg1oQHf7BkxL8AFJMWB4gZhyp2tzbHtLQd3g3L7b2MBuq3dEJMdgRevAxQ8BFSjgCRoC5jp9zpDnphGvjq8pT5Q2aA111dg5pxS" + encodedMuunKey = "tpubDBCFedMe1hS3ba6qSTivfi2f6MieNcgms1b6b9KK1xD6wtvG82dSvYQFgQcF2MBs4kyWEp7MB8tXgYzxiYpxDBnSU2F1sxrict9bNikM9kc" + hexTx = "010000000676cc7d617672313b795a1cac0afe1b045aa2a56face4c61202b733fa543cb4c40000000000ffffffffb3f49f5cfcb0dbaee92be859a79ebd3120964ad3208fc178e3fec5968a194f4b0100000000ffffffff6854626a1687a052653df7630b272446e87bb8eb57a74e537d8ff116248821420100000000ffffffff51849b950f0becdc008da1d072250bd65ddeabfd4a80feed09e25bd0a9ca1e5f0100000000ffffffff7d69cb9ee91913b5d96a4f8c5a32e86a96560c4db3e40f0496772a51186bf0280100000000ffffffffe894cf036db7f07d56b003036477d5c8ed494bbd1cec8a38c355fc88609573140100000000ffffffff01b820090000000000225120a21f34d36f71805071d0e3cb9cd92b0f8267b7ac95db0d60a60d7ec6bbff12db00000000" + userSessionIds = []string{ + "bdc72b8cb2f8278ac5de74a585c8e60729d82635c574a0621e4f086121274227", + "edb7f1cad77fabe63751fcc30040a91c02675f8d31df424c7a0c723e2134878a", + "cdac321cead1a473bfdf17329f6c31baf30b9f6bd5931282e3dda5f7b2c16f61", + "e8a79000514f74990e764e185e5729ec77e17052ff903b8c3788cba3b9e151f3", + "a71d46b2292b209147776ecc2f72d9e1d8f815e993d566fc168ccb81663155b2", + "0250df31823ed2e8fda46e928645a23ccbafddf39f7649579807c9e49fe5b143", + } + inputTxRaw = []string{ + "0200000000010148823860c5e3f3db0f1bd0cc70d02f0d773c878c0a366ffcd01be6444d3d0cc00000000000feffffff02a08601000000000017a914d88e83d1a5d3238decb7d4442c24f7bb3b8da52b87b04f042a01000000160014e21f5442bce128b113a9706a0ce53cc3f43943610247304402203a316d5941a810b5aa33cc08d10a88d754285e98fd88e26b6861b5f404b0b546022017024d58a8ab1ca0c33d081c5e6ca3192f3b1ba72676731e52270a5fb74ecb58012103ff6f07c524f89457d05a1ee9e2b8d1ae2520916748b2da0c7c1d3655f271855e25050000", + "02000000000101873c68ac3f94b583efe49c3f00ae3cc7f77c9a78bccc3fdf61920edd45545b7f0000000000feffffff02b04f042a01000000160014d66fdb6789560b3fd7b0d000da0d6f22b9c70252a08601000000000017a914b82751531d1ff4fefd7fa348b900ba66d17cdcc7870247304402204bddec0acf2a0d33f7ef89f1202c6d7d4c8151756d368cc45b1c3ed3a828e7800220245589b38c63e6f32333f14d6694fc613bd6354ae354f9309f6cd3fae677c50c012103ff6f07c524f89457d05a1ee9e2b8d1ae2520916748b2da0c7c1d3655f271855e25050000", + "0200000000010161e13760dbe0f3fcab9ed507538890d017812655f218f9bf2e1b38e5792841f80000000000feffffff028b4d042a01000000160014b00f4fabc6dec5f23326c693d92b65573c9433daa0860100000000002200204eb582ca70159962cff162ada5d0c4f6cdfe7eef173bf4c970ad177fe69ada6402473044022012d3ae236f46c2484be4e19792fb9ce7b4f51c76f6b808000e15f8872a280c7502202195bceec3fdb1d7333badf9be8950ae3210438e5ffb2e30a1573302ddeef36a012103ff6f07c524f89457d05a1ee9e2b8d1ae2520916748b2da0c7c1d3655f271855e25050000", + "020000000001019dcb550b07a53ec866ec2558f94d8b801ef60200378cd78afcde2f123a9d619a0000000000feffffff024d4f042a01000000160014af568af3154b649bcfd7c3bfdb814043412e817fa0860100000000001976a914320cf4affd84615973e948a47a96f9103d68f8c288ac02473044022075bf561de3708e652cc9910edfd56c0158e86d99e8b3e4e1284c1bef13e72d1802203ff834bd90443dafef0488793cc6a007946ec781d4ac0dea5947a28794dfc748012103ff6f07c524f89457d05a1ee9e2b8d1ae2520916748b2da0c7c1d3655f271855e25050000", + "02000000000101204b88f3b89ad992a9ce63bf857d08f26ee20a0e6bb4e912ddb41f56d145078c0000000000feffffff028b4d042a01000000160014032c403be2d0a8bb0f3db634a68b31d31fb498c9a0860100000000002251205cedceb4e29b9632835293f5b68fd0de48d93420e6c461b0a1c10dc8ced91be50247304402202523ae6ed227cc7b80f34eb7662d3e90681666828b9ae437249d125dfc14dba602207a82b7ac2ae18ed0e742245e8277364b6bec00a0c7dc428085b577c9a3e98ffe012103ff6f07c524f89457d05a1ee9e2b8d1ae2520916748b2da0c7c1d3655f271855e25050000", + "02000000000101e33e3ed531e1956474279b70325c8ad5283706fe8bd29b1358f1e1d4d0aac8a30000000000feffffff028b4d042a01000000160014da492a8cc860dab06f59d941c2dec6e88a29b545a08601000000000022512074300b4a8179bfe6eacd7e478c506cd00445ae3dd489634684626c9b04e4ec8d02473044022037b78bd154dac8dcda7ae9f109f4612add9396731bb01ad44f58e14011f7be0f022042b5b13e03815d9ae5748d28d9e70274d9474a315e928b37c4d5a3916572c0af012103ff6f07c524f89457d05a1ee9e2b8d1ae2520916748b2da0c7c1d3655f271855e1b050000", + } + ) + inputList := &InputList{inputs: []Input{ + &input{ + outpoint: outpoint{index: 0, amount: 100000, txId: hexToBytes("c4b43c54fa33b70212c6e4ac6fa5a25a041bfe0aac1c5a793b317276617dcc76")}, + address: addresses.New(3, "m/schema:1'/recovery:1'/external:1/2", "2NCzGfq4MurQzhodoSFtB2VSEJ8Tc5MN82j"), + muunSignature: hexToBytes("3045022100860c48eb2374dbb67ed68dd91198994407a4e933f92e4f3187706f93debf246b02206b7d0607eaad478f6323cb707344739ce0ba8583266a39b617dd0e5bb77633b501"), + }, + &input{ + outpoint: outpoint{index: 1, amount: 100000, txId: hexToBytes("4b4f198a96c5fee378c18f20d34a962031bd9ea759e82be9aedbb0fc5c9ff4b3")}, + address: addresses.New(2, "m/schema:1'/recovery:1'/external:1/1", "2NA2wRRMNCsfECwvjHwAdPmciFRGrBKzc6W"), + muunSignature: hexToBytes("3045022100c907055dd0033f4f28113f566f68a9c0c400f804d83faca36f5f77a92696bf960220379da01e954f5cdee9fc69dfdf59ac33d36b4af5ea801fd011c2911df2ace2b501"), + }, + &input{ + outpoint: outpoint{index: 1, amount: 100000, txId: hexToBytes("4221882416f18f7d534ea757ebb87be84624270b63f73d6552a087166a625468")}, + address: addresses.New(4, "m/schema:1'/recovery:1'/external:1/3", "bcrt1qf66c9jnszkvk9nl3v2k6t5xy7mxlulh0zualfjts45thle56mfjqvkvgh0"), + muunSignature: hexToBytes("3045022100bb2a22510930bc31ddb6b9245eebe5d22499c54d08b326eebd249403f9f6128b02207c10ec0fb8a336230f352b4d2ed10be91cc31d883fd545dfc5124ce8c63a01bf01"), + }, + &input{ + outpoint: outpoint{index: 1, amount: 100000, txId: hexToBytes("5f1ecaa9d05be209edfe804afdabde5dd60b2572d0a18d00dcec0b0f959b8451")}, + address: addresses.New(1, "m/schema:1'/recovery:1'/external:1/0", "mk5bbq9qZ6Hh6u34wxvA4Hs6cEc4AF2HfL"), + }, + &input{ + outpoint: outpoint{index: 1, amount: 100000, txId: hexToBytes("28f06b18512a7796040fe4b34d0c56966ae8325a8c4f6ad9b51319e99ecb697d")}, + address: addresses.New(6, "m/schema:1'/recovery:1'/external:1/5", "bcrt1ptnkuad8znwtr9q6jj06mdr7smeydjdpqumzxrv9pcyxu3nker0js0h0dfc"), + muunPublicNonce: hexToBytes("020129f74df468dadc7a1af0305f12a550a4792559b260f756b67277d1de8d0d6e03fb1229bf2eb848532714bf50d79770b2790c589926b3893c4697106dec17a27f"), + muunSignature: hexToBytes("1be2d8c9f6781372950374bf6986ea32d40473beac5329ca9fe1fe459ce07e26"), + }, + &input{ + outpoint: outpoint{index: 1, amount: 100000, txId: hexToBytes("1473956088fc55c3388aec1cbd4b49edc8d577640303b0567df0b76d03cf94e8")}, + address: addresses.New(5, "m/schema:1'/recovery:1'/external:1/4", "bcrt1pwscqkj5p0xl7d6kd0ercc5rv6qzytt3a6jykx35yvfkfkp8yajxs77vg44"), + muunPublicNonce: hexToBytes("03b844359aea6df24a8c7e1766b6cc81d86827f6a0239eef26bdd02d19d0c7fed103b5d431f46f51b73572a619355d60592cec11f16491220810070fd5150216fbcc"), + muunSignature: hexToBytes("ffc0c05c178fedc4ab2c4edc80dbb1a79d0ef5e6c0e3bfb7744215540c70d53c"), + }, + }} + + userKey, _ := NewHDPrivateKeyFromString(encodedUserKey, basePath, Regtest()) + muunKey, _ := NewHDPublicKeyFromString(encodedMuunKey, basePath, Regtest()) + + nonces := createTestNonces(t, userKey, inputList, userSessionIds) + partial, _ := NewPartiallySignedTransaction(inputList, hexToBytes(hexTx), nonces) + signedRawTx, err := partial.Sign(userKey, muunKey) + + if err != nil { + t.Fatalf("failed to sign tx due to %v", err) + } + + signedTx := wire.NewMsgTx(0) + signedTx.Deserialize(bytes.NewReader(signedRawTx.Bytes)) + + // validate input signatures + for index := range inputList.inputs { + verifyInput(t, signedTx, inputTxRaw[index], inputList.inputs[index].OutPoint().Index(), index) + } +} + func TestPartiallySignedTransaction_SignSubmarineSwapV1(t *testing.T) { const ( hexTx = "01000000021a608c7d6e40586806c33b3b1036fbd305c37e9d38990d912cc02de7e7cec05e0000000000fffffffff18bce10875329410641316bf7c4d984e00780174b6983080e9225dc26e5bd8c0100000000feffffff01705bc0230000000017a91470fcbc29723c85fdbf9fb5189220f279e9be4508878f030000" @@ -401,7 +589,8 @@ func TestPartiallySignedTransaction_SignSubmarineSwapV1(t *testing.T) { inputList := &InputList{inputs: inputs} rawTx, _ := hex.DecodeString(hexTx) - partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nil) + nonces := GenerateMusigNonces(len(inputList.inputs)) + partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nonces) muunKey, _ := NewHDPublicKeyFromString(encodedMuunKey, basePath, Regtest()) userKey, _ := NewHDPrivateKeyFromString(encodedUserKey, basePath, Regtest()) @@ -437,7 +626,8 @@ func verifyInput(t *testing.T, signedTx *wire.MsgTx, hexPrevTx string, prevIndex txscript.ScriptVerifyStrictEncoding | txscript.ScriptVerifyLowS | txscript.ScriptVerifyWitness | txscript.ScriptVerifyCheckLockTimeVerify - vm, err := txscript.NewEngine(prevTx.TxOut[prevIndex].PkScript, signedTx, index, flags, nil, nil, prevTx.TxOut[prevIndex].Value) + prevOutFetcher := txscript.NewCannedPrevOutputFetcher(prevTx.TxOut[prevIndex].PkScript, prevTx.TxOut[prevIndex].Value) + vm, err := txscript.NewEngine(prevTx.TxOut[prevIndex].PkScript, signedTx, index, flags, nil, nil, prevTx.TxOut[prevIndex].Value, prevOutFetcher) if err != nil { t.Fatalf("failed to build script engine: %v", err) } @@ -492,7 +682,8 @@ func TestPartiallySignedTransaction_SignSubmarineSwapV2(t *testing.T) { inputList := &InputList{inputs: inputs} rawTx, _ := hex.DecodeString(hexTx) - partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nil) + nonces := GenerateMusigNonces(len(inputList.inputs)) + partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nonces) signedRawTx, err := partial.Sign(userKey, muunKey) @@ -575,7 +766,8 @@ func TestPartiallySignedTransaction_SignIncomingSwap(t *testing.T) { State: walletdb.InvoiceStateUsed, }) - partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nil) + nonces := GenerateMusigNonces(len(inputList.inputs)) + partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nonces) signedRawTx, err := partial.Sign(userKey, muunKey) @@ -647,7 +839,8 @@ func TestPartiallySignedTransaction_SignIncomingSwapCollaboratively(t *testing.T setup() - partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nil) + nonces := GenerateMusigNonces(len(inputList.inputs)) + partial, _ := NewPartiallySignedTransaction(inputList, rawTx, nonces) signedRawTx, err := partial.Sign(userKey, muunKey) @@ -898,7 +1091,8 @@ func TestPartiallySignedTransaction_Verify(t *testing.T) { t.Run(tt.name, func(t *testing.T) { inputList := &InputList{inputs: tt.fields.inputs} rawTx, _ := hex.DecodeString(tt.fields.tx) - p, err := NewPartiallySignedTransaction(inputList, rawTx, nil) + nonces := GenerateMusigNonces(len(inputList.inputs)) + p, err := NewPartiallySignedTransaction(inputList, rawTx, nonces) if err != nil { panic(err) } diff --git a/libwallet/publickey.go b/libwallet/publickey.go index f9c48121..60340b52 100644 --- a/libwallet/publickey.go +++ b/libwallet/publickey.go @@ -3,7 +3,7 @@ package libwallet import ( "fmt" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" ) type PublicKey struct { @@ -11,7 +11,7 @@ type PublicKey struct { } func NewPublicKeyFromBytes(bytes []byte) (*PublicKey, error) { - key, err := btcec.ParsePubKey(bytes, btcec.S256()) + key, err := btcec.ParsePubKey(bytes) if err != nil { return nil, fmt.Errorf("NewPublicKeyFromBytes: failed to parse pub key: %w", err) } diff --git a/libwallet/recoverycode/recoverycode.go b/libwallet/recoverycode/recoverycode.go index 027d12af..af025519 100644 --- a/libwallet/recoverycode/recoverycode.go +++ b/libwallet/recoverycode/recoverycode.go @@ -11,7 +11,7 @@ import ( "golang.org/x/crypto/scrypt" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" ) const ( @@ -113,7 +113,7 @@ func ConvertToKey(code, salt string) (*btcec.PrivateKey, error) { } // 2nd return value is the pub key which we don't need right now - priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), input) + priv, _ := btcec.PrivKeyFromBytes(input) return priv, nil } diff --git a/libwallet/recoverycode_test.go b/libwallet/recoverycode_test.go index 7873143a..01056892 100644 --- a/libwallet/recoverycode_test.go +++ b/libwallet/recoverycode_test.go @@ -17,8 +17,8 @@ func TestRecoveryCodeToKey(t *testing.T) { wantErr bool }{ { - name: "boop", - args: args{ + name: "boop", + args: args{ code: "3V4N-R9EC-V3TQ-NRB3-Q7NY-9HXP-CSDC-B5BC", salt: "63f701fda4fc0b0c", }, diff --git a/libwallet/segwit.go b/libwallet/segwit.go index 66152488..5a27b308 100644 --- a/libwallet/segwit.go +++ b/libwallet/segwit.go @@ -4,19 +4,20 @@ import ( "crypto/sha256" "fmt" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" + lndinput "github.com/lightningnetwork/lnd/input" ) -func signNativeSegwitInput(index int, tx *wire.MsgTx, privateKey *HDPrivateKey, witnessScript []byte, amount btcutil.Amount) ([]byte, error) { +func signNativeSegwitInputV0(index int, tx *wire.MsgTx, privateKey *HDPrivateKey, witnessScript []byte, amount btcutil.Amount) ([]byte, error) { privKey, err := privateKey.key.ECPrivKey() if err != nil { return nil, fmt.Errorf("failed to produce EC priv key for signing: %w", err) } - sigHashes := txscript.NewTxSigHashes(tx) + sigHashes := lndinput.NewTxSigHashesV0Only(tx) sig, err := txscript.RawTxInWitnessSignature(tx, sigHashes, index, int64(amount), witnessScript, txscript.SigHashAll, privKey) if err != nil { return nil, fmt.Errorf("failed to sign V4 input: %w", err) @@ -35,7 +36,7 @@ func createNonNativeSegwitRedeemScript(witnessScript []byte) ([]byte, error) { return builder.Script() } -func signNonNativeSegwitInput(index int, tx *wire.MsgTx, privateKey *HDPrivateKey, +func signNonNativeSegwitInputV0(index int, tx *wire.MsgTx, privateKey *HDPrivateKey, redeemScript, witnessScript []byte, amount btcutil.Amount) ([]byte, error) { txInput := tx.TxIn[index] @@ -53,7 +54,7 @@ func signNonNativeSegwitInput(index int, tx *wire.MsgTx, privateKey *HDPrivateKe return nil, fmt.Errorf("failed to produce EC priv key for signing: %w", err) } - sigHashes := txscript.NewTxSigHashes(tx) + sigHashes := lndinput.NewTxSigHashesV0Only(tx) // TODO: validate that segwit V0 is enough for this input sig, err := txscript.RawTxInWitnessSignature( tx, sigHashes, index, int64(amount), witnessScript, txscript.SigHashAll, privKey) if err != nil { diff --git a/libwallet/sphinx/sphinx.go b/libwallet/sphinx/sphinx.go index f50ff242..e0c8b2c6 100644 --- a/libwallet/sphinx/sphinx.go +++ b/libwallet/sphinx/sphinx.go @@ -5,8 +5,9 @@ import ( "errors" "fmt" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btclog" lndsphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lnwire" @@ -23,21 +24,29 @@ func Validate( amount lnwire.MilliSatoshi, net *chaincfg.Params, ) error { - router := lndsphinx.NewRouter(nodeKey, net, lndsphinx.NewMemoryReplayLog()) + hop.UseLogger(btclog.Disabled) + router := lndsphinx.NewRouter(&lndsphinx.PrivKeyECDH{PrivKey: nodeKey}, net, lndsphinx.NewMemoryReplayLog()) if err := router.Start(); err != nil { return fmt.Errorf("could not start router for validating onion blob: %w", err) } onionProcessor := hop.NewOnionProcessor(router) - onionProcessor.Start() - iterator, code := onionProcessor.DecodeHopIterator( + err := onionProcessor.Start() + if err != nil { + return err + } + defer onionProcessor.Stop() + + iterator, err := onionProcessor.ReconstructHopIterator( bytes.NewReader(onionBlob), paymentHash, - expiry, + hop.ReconstructBlindingInfo{ + IncomingExpiry: expiry, + }, ) - if code != lnwire.CodeNone { - return fmt.Errorf("failed decode sphinx due to %v", code.String()) + if err != nil { + return fmt.Errorf("failed decode sphinx due to %w", err) } - payload, err := iterator.HopPayload() + payload, _, err := iterator.HopPayload() if err != nil { return err } diff --git a/libwallet/submarineSwapV1.go b/libwallet/submarineSwapV1.go index 3650e32d..84d6e075 100644 --- a/libwallet/submarineSwapV1.go +++ b/libwallet/submarineSwapV1.go @@ -4,9 +4,9 @@ import ( "errors" "fmt" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" "github.com/muun/libwallet/swaps" ) @@ -45,7 +45,7 @@ func (c *coinSubmarineSwapV1) SignInput(index int, tx *wire.MsgTx, userKey *HDPr return fmt.Errorf("failed to build reedem script for signing: %w", err) } - sig, err := signNonNativeSegwitInput( + sig, err := signNonNativeSegwitInputV0( index, tx, userKey, redeemScript, witnessScript, c.Amount) if err != nil { return err diff --git a/libwallet/submarineSwapV2.go b/libwallet/submarineSwapV2.go index 47fc8d3d..3f7f9af4 100644 --- a/libwallet/submarineSwapV2.go +++ b/libwallet/submarineSwapV2.go @@ -4,9 +4,9 @@ import ( "errors" "fmt" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" "github.com/muun/libwallet/swaps" ) @@ -45,7 +45,7 @@ func (c *coinSubmarineSwapV2) SignInput(index int, tx *wire.MsgTx, userKey *HDPr return err } - sig, err := signNativeSegwitInput( + sig, err := signNativeSegwitInputV0( index, tx, userKey, witnessScript, c.Amount) if err != nil { return err diff --git a/libwallet/swaps/swaps.go b/libwallet/swaps/swaps.go index cedfddda..b1907abe 100644 --- a/libwallet/swaps/swaps.go +++ b/libwallet/swaps/swaps.go @@ -6,8 +6,8 @@ import ( "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcutil/hdkeychain" "github.com/muun/libwallet/addresses" "github.com/muun/libwallet/hdpath" hash "golang.org/x/crypto/ripemd160" //lint:ignore SA1019 using deprecated hash function for compatibility @@ -68,7 +68,7 @@ func (d *KeyDescriptor) DeriveTo(path string) (*hdkeychain.ExtendedKey, error) { if index.Hardened { modifier = hdkeychain.HardenedKeyStart } - key, err = key.Child(index.Index | modifier) + key, err = key.Derive(index.Index | modifier) if err != nil { return nil, err } diff --git a/libwallet/swaps/v1.go b/libwallet/swaps/v1.go index 036b5c32..a05eda51 100644 --- a/libwallet/swaps/v1.go +++ b/libwallet/swaps/v1.go @@ -9,8 +9,8 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/muun/libwallet/btcsuitew/btcutilw" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/zpay32" "github.com/muun/libwallet/addresses" ) diff --git a/libwallet/swaps/v1_test.go b/libwallet/swaps/v1_test.go index cfae33eb..7ab46684 100755 --- a/libwallet/swaps/v1_test.go +++ b/libwallet/swaps/v1_test.go @@ -3,8 +3,8 @@ package swaps import ( "testing" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil/hdkeychain" "github.com/muun/libwallet/addresses" ) diff --git a/libwallet/swaps/v2.go b/libwallet/swaps/v2.go index 4c098605..54d754c3 100644 --- a/libwallet/swaps/v2.go +++ b/libwallet/swaps/v2.go @@ -6,10 +6,10 @@ import ( "encoding/hex" "fmt" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/hdkeychain" "github.com/lightningnetwork/lnd/zpay32" ) diff --git a/libwallet/walletdb/fee_bump_repository.go b/libwallet/walletdb/fee_bump_repository.go new file mode 100644 index 00000000..5e94ac10 --- /dev/null +++ b/libwallet/walletdb/fee_bump_repository.go @@ -0,0 +1,158 @@ +package walletdb + +import ( + "fmt" + "time" + + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/sqlite" + "github.com/muun/libwallet/operation" +) + +type FeeBumpRepository interface { + Store(feeBumpFunctions []*operation.FeeBumpFunction) error + GetAll() ([]*operation.FeeBumpFunction, error) + GetCreationDate() (*time.Time, error) + RemoveAll() error +} + +type FeeBumpFunction struct { + gorm.Model + Position uint + // PartialLinearFunctions establishes a foreign key relationship with the PartialLinearFunction table, + // where 'FunctionPosition' in PartialLinearFunction references 'Position' in FeeBumpFunction. + PartialLinearFunctions []PartialLinearFunction `gorm:"foreignKey:FunctionPosition;references:Position;"` +} + +type PartialLinearFunction struct { + gorm.Model + LeftClosedEndpoint float64 + RightOpenEndpoint float64 + Slope float64 + Intercept float64 + FunctionPosition uint +} + +type GORMFeeBumpRepository struct { + db *gorm.DB +} + +func (r *GORMFeeBumpRepository) Store(feeBumpFunctions []*operation.FeeBumpFunction) error { + dbFeeBumpFunctions := mapToDBFeeBumpFunctions(feeBumpFunctions) + + tx := r.db.Begin() + + // Remove old data before store updated functions + err := removeAllInTransaction(tx) + if err != nil { + return fmt.Errorf("error when trying to remove old fee bump functions: %w", err) + } + + for _, feeBumpFunction := range dbFeeBumpFunctions { + if err := tx.Create(&feeBumpFunction).Error; err != nil { + tx.Rollback() + return err + } + } + + if err := tx.Commit().Error; err != nil { + return fmt.Errorf("failed to save fee bump functions: %w", err) + } + return nil +} + +func (r *GORMFeeBumpRepository) GetAll() ([]*operation.FeeBumpFunction, error) { + var dbFeeBumpFunctions []FeeBumpFunction + + result := r.db.Preload("PartialLinearFunctions").Order("position asc").Find(&dbFeeBumpFunctions) + + if result.Error != nil { + return nil, result.Error + } + + feeBumpFunctions := mapToOperationFeeBumpFunctions(dbFeeBumpFunctions) + + return feeBumpFunctions, nil +} + +func (r *GORMFeeBumpRepository) GetCreationDate() (*time.Time, error) { + var dbFeeBumpFunction FeeBumpFunction + result := r.db.First(&dbFeeBumpFunction) + + if result.Error != nil { + return nil, result.Error + } + + return &dbFeeBumpFunction.CreatedAt, nil +} + +func (r *GORMFeeBumpRepository) RemoveAll() error { + tx := r.db.Begin() + err := removeAllInTransaction(tx) + if err != nil { + return err + } + + return tx.Commit().Error +} + +func removeAllInTransaction(tx *gorm.DB) error { + result := tx.Delete(FeeBumpFunction{}) + if result.Error != nil { + tx.Rollback() + return result.Error + } + + result = tx.Delete(PartialLinearFunction{}) + if result.Error != nil { + tx.Rollback() + return result.Error + } + return nil +} + +func mapToDBFeeBumpFunctions(feeBumpFunctions []*operation.FeeBumpFunction) []FeeBumpFunction { + var dbFeeBumpFunctions []FeeBumpFunction + for i, feeBumpFunction := range feeBumpFunctions { + var dbPartialLinearFunctions []PartialLinearFunction + for _, partialLinearFunction := range feeBumpFunction.PartialLinearFunctions { + dbPartialLinearFunctions = append(dbPartialLinearFunctions, PartialLinearFunction{ + LeftClosedEndpoint: partialLinearFunction.LeftClosedEndpoint, + RightOpenEndpoint: partialLinearFunction.RightOpenEndpoint, + Slope: partialLinearFunction.Slope, + Intercept: partialLinearFunction.Intercept, + FunctionPosition: uint(i), + }) + } + dbFeeBumpFunctions = append(dbFeeBumpFunctions, FeeBumpFunction{ + Position: uint(i), + PartialLinearFunctions: dbPartialLinearFunctions, + }) + } + + return dbFeeBumpFunctions +} + +func mapToOperationFeeBumpFunctions(dbFeeBumpFunctions []FeeBumpFunction) []*operation.FeeBumpFunction { + var feeBumpFunctions []*operation.FeeBumpFunction + for _, dbFeeBumpFunction := range dbFeeBumpFunctions { + var partialLinearFunctions []*operation.PartialLinearFunction + for _, dbPartialLinearFunction := range dbFeeBumpFunction.PartialLinearFunctions { + partialLinearFunctions = append(partialLinearFunctions, &operation.PartialLinearFunction{ + LeftClosedEndpoint: dbPartialLinearFunction.LeftClosedEndpoint, + RightOpenEndpoint: dbPartialLinearFunction.RightOpenEndpoint, + Slope: dbPartialLinearFunction.Slope, + Intercept: dbPartialLinearFunction.Intercept, + }) + } + + feeBumpFunctions = append( + feeBumpFunctions, + &operation.FeeBumpFunction{ + CreatedAt: dbFeeBumpFunction.CreatedAt, + PartialLinearFunctions: partialLinearFunctions, + }, + ) + } + return feeBumpFunctions +} diff --git a/libwallet/walletdb/fee_bump_repository_test.go b/libwallet/walletdb/fee_bump_repository_test.go new file mode 100644 index 00000000..82d765c6 --- /dev/null +++ b/libwallet/walletdb/fee_bump_repository_test.go @@ -0,0 +1,154 @@ +package walletdb + +import ( + "math" + "path" + "reflect" + "testing" + + "github.com/muun/libwallet/operation" +) + +func TestCreateFeeBumpFunctions(t *testing.T) { + db, err := setupTestDb(t) + if err != nil { + t.Fatalf("failed to set up test db: %v", err) + } + defer db.Close() + + repository := db.NewFeeBumpRepository() + + expectedFeeBumpFunctions := []*operation.FeeBumpFunction{ + { + PartialLinearFunctions: []*operation.PartialLinearFunction{ + { + LeftClosedEndpoint: 0, + RightOpenEndpoint: 300, + Slope: 2, + Intercept: 300, + }, + { + LeftClosedEndpoint: 300, + RightOpenEndpoint: math.Inf(1), + Slope: 3, + Intercept: 200, + }, + }, + }, + { + PartialLinearFunctions: []*operation.PartialLinearFunction{ + { + LeftClosedEndpoint: 0, + RightOpenEndpoint: 100, + Slope: 2, + Intercept: 100, + }, + { + LeftClosedEndpoint: 100, + RightOpenEndpoint: math.Inf(1), + Slope: 3, + Intercept: 500, + }, + }, + }, + { + PartialLinearFunctions: []*operation.PartialLinearFunction{ + { + LeftClosedEndpoint: 0, + RightOpenEndpoint: 1000, + Slope: 2, + Intercept: 1000, + }, + { + LeftClosedEndpoint: 1000, + RightOpenEndpoint: math.Inf(1), + Slope: 3, + Intercept: 1500, + }, + }, + }, + } + + err = repository.Store(expectedFeeBumpFunctions) + if err != nil { + t.Fatalf("failed to save fee bump functions: %v", err) + } + + loadedFeeBumpFunctions, err := repository.GetAll() + if err != nil { + t.Fatalf("failed to load fee bump functions: %v", err) + } + + if len(loadedFeeBumpFunctions) != len(expectedFeeBumpFunctions) { + t.Errorf("expected %d fee bump functions, got %d", len(expectedFeeBumpFunctions), len(loadedFeeBumpFunctions)) + } + + for i, loadedFeeBumpFunction := range loadedFeeBumpFunctions { + if len(loadedFeeBumpFunction.PartialLinearFunctions) != len(expectedFeeBumpFunctions[i].PartialLinearFunctions) { + t.Errorf( + "expected %d intervals, got %d", + len(expectedFeeBumpFunctions[i].PartialLinearFunctions), + len(loadedFeeBumpFunction.PartialLinearFunctions), + ) + } + + for j, loadedpartialLinearFunction := range loadedFeeBumpFunction.PartialLinearFunctions { + expectedPartialLinearFunction := expectedFeeBumpFunctions[i].PartialLinearFunctions[j] + + if !reflect.DeepEqual(loadedpartialLinearFunction, expectedPartialLinearFunction) { + t.Errorf("loaded and expected partial linear functions are not equal") + } + } + } + + creationDate, err := repository.GetCreationDate() + + if err != nil { + t.Fatalf("failed getting creation date: %v", err) + } + + if loadedFeeBumpFunctions[0].CreatedAt != *creationDate { + t.Fatalf("date mismatch: got: %v, expected: %v", *creationDate, loadedFeeBumpFunctions[0].CreatedAt) + } + + err = repository.RemoveAll() + if err != nil { + t.Fatalf("failed removing all fee bump functions: %v", err) + } + + loadedFeeBumpFunctions, err = repository.GetAll() + if err != nil { + t.Fatalf("failed to load fee bump functions: %v", err) + } + + if len(loadedFeeBumpFunctions) != 0 { + t.Fatalf("fee bump functions were not removed") + } + + var dbPartialLinearFunctions []PartialLinearFunction + db.db.Find(&dbPartialLinearFunctions) + if len(dbPartialLinearFunctions) != 0 { + t.Fatalf("partial linear functions were not removed") + } + + creationDate, err = repository.GetCreationDate() + + if err == nil { + t.Fatalf("it should return an error because theare are not records") + } + + if creationDate != nil { + t.Fatalf("creation date should be null when there are not fee bump functions") + } +} + +func setupTestDb(t *testing.T) (*DB, error) { + dir := t.TempDir() + + db, err := Open(path.Join(dir, "test.db")) + if err != nil { + return nil, err + } + + return db, err +} diff --git a/libwallet/walletdb/walletdb.go b/libwallet/walletdb/walletdb.go index e86fe5d2..959e8a84 100644 --- a/libwallet/walletdb/walletdb.go +++ b/libwallet/walletdb/walletdb.go @@ -47,6 +47,10 @@ func Open(path string) (*DB, error) { return &DB{db}, nil } +func (d *DB) NewFeeBumpRepository() FeeBumpRepository { + return &GORMFeeBumpRepository{db: d.db} +} + func migrate(db *gorm.DB) error { opts := gormigrate.Options{ UseTransaction: true, @@ -119,6 +123,31 @@ func migrate(db *gorm.DB) error { return tx.Table("invoices").DropColumn(gorm.ToColumnName("Metadata")).Error }, }, + { + ID: "Init fee bump tables", + Migrate: func(tx *gorm.DB) error { + + type FeeBumpFunction struct { + gorm.Model + Position uint + FeeBumpIntervals []PartialLinearFunction `gorm:"foreignKey:FunctionPosition;references:Position;"` + } + + type PartialLinearFunction struct { + gorm.Model + LeftClosedEndpoint float64 + RightOpenEndpoint float64 + Slope float64 + Intercept float64 + FunctionPosition uint + } + // Create tables FeeBumpFunction and PartialLinearFunction + return tx.AutoMigrate(&FeeBumpFunction{}, &PartialLinearFunction{}).Error + }, + Rollback: func(tx *gorm.DB) error { + return tx.DropTable(&FeeBumpFunction{}, &PartialLinearFunction{}).Error + }, + }, }) return m.Migrate() } diff --git a/libwallet/walletdb/walletdb_test.go b/libwallet/walletdb/walletdb_test.go index 2028330b..d332a0a1 100644 --- a/libwallet/walletdb/walletdb_test.go +++ b/libwallet/walletdb/walletdb_test.go @@ -3,14 +3,14 @@ package walletdb import ( "bytes" "crypto/rand" - "io/ioutil" "math" + "os" "path" "testing" ) func TestOpen(t *testing.T) { - dir, err := ioutil.TempDir("", "libwallet") + dir, err := os.MkdirTemp("", "libwallet") if err != nil { panic(err) } @@ -23,7 +23,7 @@ func TestOpen(t *testing.T) { } func TestInvoices(t *testing.T) { - dir, err := ioutil.TempDir("", "libwallet") + dir, err := os.MkdirTemp("", "libwallet") if err != nil { panic(err) } diff --git a/tools/libwallet-android.sh b/tools/libwallet-android.sh index e835ae72..6205eb6b 100755 --- a/tools/libwallet-android.sh +++ b/tools/libwallet-android.sh @@ -5,9 +5,6 @@ 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 @@ -22,20 +19,10 @@ mkdir -p "$(dirname "$libwallet")" mkdir -p "$build_dir/android" mkdir -p "$build_dir/pkg" -# Line by line explanation -# 1. Use a shared dependency cache between iOS and Android by setting GOMODCACHE -# 2. Run gomobile bind using the version pinned by the go.mod file -# 3. Set output flags -# 4. Use a fixed build cache location -# 5. Opt in to reproducible builds - -if [[ -z $GOMODCACHE ]]; then - GOMODCACHE="$build_dir/pkg" -fi +GOCACHE="$build_dir/android" -if [[ -z $GOCACHE ]]; then - GOCACHE="$build_dir/android" -fi +# Install and setup gomobile on demand (no-op if already installed and up-to-date) +. "$repo_root/tools/bootstrap-gomobile.sh" # 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 @@ -44,8 +31,11 @@ fi 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 \ +# Finally run gomobile bind using the version pinned by the go.mod file. +# We need -androidapi 19 to set the min api targeted by the NDK. +# The -trimpath and -ldflags are passed on to go build and are part of keeping the build reproducible. +# Note that we bind & build two packages top-level libwallet and newop. +go run golang.org/x/mobile/cmd/gomobile bind \ -target="android" -o "$libwallet" \ -androidapi 19 \ -trimpath -ldflags="-buildid=. -v" \