Android & JVM loader in their own modules
This commit is contained in:
		
							parent
							
								
									9e1fc5d5ff
								
							
						
					
					
						commit
						1d9d57ca0a
					
				
							
								
								
									
										259
									
								
								build.gradle.kts
									
									
									
									
									
								
							
							
						
						
									
										259
									
								
								build.gradle.kts
									
									
									
									
									
								
							| @ -1,8 +1,21 @@ | ||||
| plugins { | ||||
|     kotlin("multiplatform") version "1.4-M2-mt" | ||||
|     id("com.android.library") version "4.0.0" | ||||
|     `maven-publish` | ||||
| //    `maven-publish` | ||||
| } | ||||
| 
 | ||||
| buildscript { | ||||
|     repositories { | ||||
|         google() | ||||
|         maven("https://dl.bintray.com/kotlin/kotlin-eap") | ||||
|         jcenter() | ||||
|     } | ||||
| 
 | ||||
|     dependencies { | ||||
|         classpath("com.android.tools.build:gradle:4.0.0") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| allprojects { | ||||
|     group = "fr.acinq.secp256k1" | ||||
|     version = "0.2.1-1.4-M2" | ||||
| 
 | ||||
| @ -11,6 +24,7 @@ repositories { | ||||
|         google() | ||||
|         maven(url = "https://dl.bintray.com/kotlin/kotlin-eap") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| val currentOs = org.gradle.internal.os.OperatingSystem.current() | ||||
| 
 | ||||
| @ -29,60 +43,40 @@ kotlin { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     val jvmAndAndroidMain by sourceSets.creating { | ||||
|         dependsOn(commonMain) | ||||
|         dependencies { | ||||
|             implementation(kotlin("stdlib-jdk8")) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     jvm { | ||||
|         compilations.all { | ||||
|             kotlinOptions.jvmTarget = "1.8" | ||||
|         } | ||||
|         (tasks[compilations["main"].processResourcesTaskName] as ProcessResources).apply{ | ||||
|             dependsOn("copyJni") | ||||
|             from(buildDir.resolve("jniResources")) | ||||
|         compilations["main"].dependencies { | ||||
|             implementation(kotlin("stdlib-jdk8")) | ||||
|         } | ||||
|         compilations["main"].defaultSourceSet.dependsOn(jvmAndAndroidMain) | ||||
|         compilations["test"].dependencies { | ||||
|             implementation(project(":jni")) | ||||
|             implementation(kotlin("stdlib-jdk8")) | ||||
|             implementation(kotlin("test-junit")) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     android { | ||||
|         publishLibraryVariants("release", "debug") | ||||
|         compilations.all { | ||||
|             kotlinOptions.jvmTarget = "1.8" | ||||
|         } | ||||
|         sourceSets["androidMain"].dependsOn(jvmAndAndroidMain) | ||||
|         sourceSets["androidTest"].dependencies { | ||||
|             implementation(kotlin("test-junit")) | ||||
|             implementation("androidx.test.ext:junit:1.1.1") | ||||
|             implementation("androidx.test.espresso:espresso-core:3.2.0") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget.secp256k1CInterop() { | ||||
|     fun org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget.secp256k1CInterop(target: String) { | ||||
|         compilations["main"].cinterops { | ||||
|             val libsecp256k1 by creating { | ||||
|                 includeDirs.headerFilterOnly(project.file("native/secp256k1/include/")) | ||||
|                 tasks[interopProcessingTaskName].dependsOn("buildSecp256k1Ios") | ||||
|                 tasks[interopProcessingTaskName].dependsOn(":native:buildSecp256k1${target.capitalize()}") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     val nativeMain by sourceSets.creating { dependsOn(commonMain) } | ||||
| 
 | ||||
|     linuxX64 { | ||||
|         secp256k1CInterop() | ||||
|     linuxX64("linux") { | ||||
|         secp256k1CInterop("linux") | ||||
|         // https://youtrack.jetbrains.com/issue/KT-39396 | ||||
|         compilations["main"].kotlinOptions.freeCompilerArgs += listOf("-include-binary", "$rootDir/native/build/linux/libsecp256k1.a") | ||||
|         compilations["main"].defaultSourceSet.dependsOn(nativeMain) | ||||
|     } | ||||
| 
 | ||||
|     ios { | ||||
|         secp256k1CInterop() | ||||
|         secp256k1CInterop("ios") | ||||
|         // https://youtrack.jetbrains.com/issue/KT-39396 | ||||
|         compilations["main"].kotlinOptions.freeCompilerArgs += listOf("-include-binary", "$rootDir/native/build/ios/libsecp256k1.a") | ||||
|         compilations["main"].defaultSourceSet.dependsOn(nativeMain) | ||||
| @ -120,166 +114,43 @@ afterEvaluate { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| android { | ||||
|     defaultConfig { | ||||
|         compileSdkVersion(30) | ||||
|         minSdkVersion(21) | ||||
|         testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" | ||||
|         externalNativeBuild { | ||||
|             cmake {} | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     compileOptions { | ||||
|         sourceCompatibility = JavaVersion.VERSION_1_8 | ||||
|         targetCompatibility = JavaVersion.VERSION_1_8 | ||||
|     } | ||||
| 
 | ||||
|     externalNativeBuild { | ||||
|         cmake { | ||||
|             setPath("src/androidMain/CMakeLists.txt") | ||||
|         } | ||||
|     } | ||||
|     ndkVersion = "21.3.6528147" | ||||
| 
 | ||||
|     sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") | ||||
| 
 | ||||
|     afterEvaluate { | ||||
|         tasks.withType<com.android.build.gradle.tasks.factory.AndroidUnitTest>().all { | ||||
|             enabled = false | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| val buildSecp256k1 by tasks.creating { group = "build" } | ||||
| sealed class Cross { | ||||
|     abstract fun cmd(target: String, nativeDir: File): List<String> | ||||
|     class DockCross(val cross: String) : Cross() { | ||||
|         override fun cmd(target: String, nativeDir: File): List<String> = listOf("./dockcross-$cross", "bash", "-c", "CROSS=1 TARGET=$target ./build.sh") | ||||
|     } | ||||
|     class MultiArch(val crossTriple: String) : Cross() { | ||||
|         override fun cmd(target: String, nativeDir: File): List<String> { | ||||
|             val uid = Runtime.getRuntime().exec("id -u").inputStream.use { it.reader().readText() }.trim().toInt() | ||||
|             return listOf( | ||||
|                 "docker", "run", "--rm", "-v", "${nativeDir.absolutePath}:/workdir", | ||||
|                 "-e", "CROSS_TRIPLE=$crossTriple", "-e", "TARGET=$target", "-e", "TO_UID=$uid", "-e", "CROSS=1", | ||||
|                 "multiarch/crossbuild", "./build.sh" | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| fun creatingBuildSecp256k1(target: String, cross: Cross?) = tasks.creating(Exec::class) { | ||||
|     group = "build" | ||||
|     buildSecp256k1.dependsOn(this) | ||||
| 
 | ||||
|     inputs.files(projectDir.resolve("native/build.sh")) | ||||
|     outputs.dir(projectDir.resolve("native/build/$target")) | ||||
| 
 | ||||
|     workingDir = projectDir.resolve("native") | ||||
|     environment("TARGET", target) | ||||
|     commandLine((cross?.cmd(target, workingDir) ?: emptyList()) + "./build.sh") | ||||
| } | ||||
| val buildSecp256k1Darwin by creatingBuildSecp256k1("darwin", if (currentOs.isMacOsX) null else Cross.MultiArch("x86_64-apple-darwin")) | ||||
| val buildSecp256k1Linux by creatingBuildSecp256k1("linux", if (currentOs.isLinux) null else Cross.DockCross("linux-x64")) | ||||
| val buildSecp256k1Mingw by creatingBuildSecp256k1("mingw", if (currentOs.isWindows) null else Cross.DockCross("windows-x64")) | ||||
| 
 | ||||
| val copyJni by tasks.creating(Sync::class) { | ||||
|     dependsOn(buildSecp256k1) | ||||
|     from(projectDir.resolve("native/build/linux/libsecp256k1-jni.so")) { rename { "libsecp256k1-jni-linux-x86_64.so" } } | ||||
|     from(projectDir.resolve("native/build/darwin/libsecp256k1-jni.dylib")) { rename { "libsecp256k1-jni-darwin-x86_64.dylib" } } | ||||
|     from(projectDir.resolve("native/build/mingw/secp256k1-jni.dll")) { rename { "secp256k1-jni-mingw-x86_64.dll" } } | ||||
|     into(buildDir.resolve("jniResources/fr/acinq/secp256k1/native")) | ||||
| } | ||||
| 
 | ||||
| val buildSecp256k1Ios by tasks.creating(Exec::class) { | ||||
|     group = "build" | ||||
|     buildSecp256k1.dependsOn(this) | ||||
| 
 | ||||
|     onlyIf { currentOs.isMacOsX } | ||||
| 
 | ||||
|     inputs.files(projectDir.resolve("native/build-ios.sh")) | ||||
|     outputs.dir(projectDir.resolve("native/build/ios")) | ||||
| 
 | ||||
|     workingDir = projectDir.resolve("native") | ||||
|     commandLine("./build-ios.sh") | ||||
| } | ||||
| 
 | ||||
| val buildSecp256k1Android by tasks.creating { | ||||
|     group = "build" | ||||
|     buildSecp256k1.dependsOn(this) | ||||
| } | ||||
| fun creatingBuildSecp256k1Android(arch: String) = tasks.creating(Exec::class) { | ||||
|     group = "build" | ||||
|     buildSecp256k1Android.dependsOn(this) | ||||
| 
 | ||||
|     inputs.files(projectDir.resolve("native/build-android.sh")) | ||||
|     outputs.dir(projectDir.resolve("native/build/android/$arch")) | ||||
| 
 | ||||
|     workingDir = projectDir.resolve("native") | ||||
| 
 | ||||
|     val toolchain = when { | ||||
|         currentOs.isLinux -> "linux-x86_64" | ||||
|         currentOs.isMacOsX -> "darwin-x86_64" | ||||
|         currentOs.isWindows -> "windows-x86_64" | ||||
|         else -> error("No Android toolchain defined for this OS: $currentOs") | ||||
|     } | ||||
|     environment("TOOLCHAIN", toolchain) | ||||
|     environment("ARCH", arch) | ||||
|     environment("ANDROID_NDK", android.ndkDirectory) | ||||
|     commandLine("./build-android.sh") | ||||
| } | ||||
| val buildSecp256k1AndroidX86_64 by creatingBuildSecp256k1Android("x86_64") | ||||
| val buildSecp256k1AndroidX86 by creatingBuildSecp256k1Android("x86") | ||||
| val buildSecp256k1AndroidArm64v8a by creatingBuildSecp256k1Android("arm64-v8a") | ||||
| val buildSecp256k1AndroidArmeabiv7a by creatingBuildSecp256k1Android("armeabi-v7a") | ||||
| 
 | ||||
| afterEvaluate { | ||||
|     configure(listOf("Debug", "Release").map { tasks["externalNativeBuild$it"] }) { | ||||
|         dependsOn(buildSecp256k1Android) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| tasks["clean"].doLast { | ||||
|     delete(projectDir.resolve("native/build")) | ||||
| } | ||||
| 
 | ||||
| publishing { | ||||
|     val snapshotName: String? by project | ||||
|     val snapshotNumber: String? by project | ||||
| 
 | ||||
|     val bintrayUsername: String? = (properties["bintrayUsername"] as String?) ?: System.getenv("BINTRAY_USER") | ||||
|     val bintrayApiKey: String? = (properties["bintrayApiKey"] as String?) ?: System.getenv("BINTRAY_APIKEY") | ||||
|     if (bintrayUsername == null || bintrayApiKey == null) logger.warn("Skipping bintray configuration as bintrayUsername or bintrayApiKey is not defined") | ||||
|     else { | ||||
|         val btRepo = if (snapshotNumber != null) "snapshots" else "libs" | ||||
|         repositories { | ||||
|             maven { | ||||
|                 name = "bintray" | ||||
|                 setUrl("https://api.bintray.com/maven/acinq/$btRepo/${project.name}/;publish=0") | ||||
|                 credentials { | ||||
|                     username = bintrayUsername | ||||
|                     password = bintrayApiKey | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     publications.withType<MavenPublication>().configureEach { | ||||
|         if (snapshotName != null && snapshotNumber != null) version = "${project.version}-${snapshotName}-${snapshotNumber}" | ||||
|         pom { | ||||
|             description.set("Bitcoin's secp256k1 library ported to Kotlin/Multiplatform for JVM, Android, iOS & Linux") | ||||
|             url.set("https://github.com/ACINQ/secp256k1-kmp") | ||||
|             licenses { | ||||
|                 name.set("Apache License v2.0") | ||||
|                 url.set("https://www.apache.org/licenses/LICENSE-2.0") | ||||
|             } | ||||
|             issueManagement { | ||||
|                 system.set("Github") | ||||
|                 url.set("https://github.com/ACINQ/secp256k1-kmp/issues") | ||||
|             } | ||||
|             scm { | ||||
|                 connection.set("https://github.com/ACINQ/secp256k1-kmp.git") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| //publishing { | ||||
| //    val snapshotName: String? by project | ||||
| //    val snapshotNumber: String? by project | ||||
| // | ||||
| //    val bintrayUsername: String? = (properties["bintrayUsername"] as String?) ?: System.getenv("BINTRAY_USER") | ||||
| //    val bintrayApiKey: String? = (properties["bintrayApiKey"] as String?) ?: System.getenv("BINTRAY_APIKEY") | ||||
| //    if (bintrayUsername == null || bintrayApiKey == null) logger.warn("Skipping bintray configuration as bintrayUsername or bintrayApiKey is not defined") | ||||
| //    else { | ||||
| //        val btRepo = if (snapshotNumber != null) "snapshots" else "libs" | ||||
| //        repositories { | ||||
| //            maven { | ||||
| //                name = "bintray" | ||||
| //                setUrl("https://api.bintray.com/maven/acinq/$btRepo/${project.name}/;publish=0") | ||||
| //                credentials { | ||||
| //                    username = bintrayUsername | ||||
| //                    password = bintrayApiKey | ||||
| //                } | ||||
| //            } | ||||
| //        } | ||||
| //    } | ||||
| // | ||||
| //    publications.withType<MavenPublication>().configureEach { | ||||
| //        if (snapshotName != null && snapshotNumber != null) version = "${project.version}-${snapshotName}-${snapshotNumber}" | ||||
| //        pom { | ||||
| //            description.set("Bitcoin's secp256k1 library ported to Kotlin/Multiplatform for JVM, Android, iOS & Linux") | ||||
| //            url.set("https://github.com/ACINQ/secp256k1-kmp") | ||||
| //            licenses { | ||||
| //                name.set("Apache License v2.0") | ||||
| //                url.set("https://www.apache.org/licenses/LICENSE-2.0") | ||||
| //            } | ||||
| //            issueManagement { | ||||
| //                system.set("Github") | ||||
| //                url.set("https://github.com/ACINQ/secp256k1-kmp/issues") | ||||
| //            } | ||||
| //            scm { | ||||
| //                connection.set("https://github.com/ACINQ/secp256k1-kmp.git") | ||||
| //            } | ||||
| //        } | ||||
| //    } | ||||
| //} | ||||
|  | ||||
							
								
								
									
										103
									
								
								jni/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								jni/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | ||||
| plugins { | ||||
|     kotlin("multiplatform") // version "1.4-M2-mt" | ||||
|     id("com.android.library") | ||||
|     `maven-publish` | ||||
| } | ||||
| 
 | ||||
| val currentOs = org.gradle.internal.os.OperatingSystem.current() | ||||
| 
 | ||||
| kotlin { | ||||
|     explicitApi() | ||||
| 
 | ||||
|     val commonMain by sourceSets.getting { | ||||
|         dependencies { | ||||
|             implementation(kotlin("stdlib-common")) | ||||
|         } | ||||
|     } | ||||
|     val commonTest by sourceSets.getting { | ||||
|         dependencies { | ||||
|             implementation(kotlin("test-common")) | ||||
|             implementation(kotlin("test-annotations-common")) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     jvm { | ||||
|         compilations.all { | ||||
|             kotlinOptions.jvmTarget = "1.8" | ||||
|         } | ||||
|         (tasks[compilations["main"].processResourcesTaskName] as ProcessResources).apply { | ||||
|             dependsOn("copyJni") | ||||
|             from(buildDir.resolve("jniResources")) | ||||
|         } | ||||
|         compilations["main"].dependencies { | ||||
|             implementation(kotlin("stdlib-jdk8")) | ||||
|         } | ||||
|         compilations["test"].dependencies { | ||||
|             implementation(kotlin("test-junit")) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     android { | ||||
|         publishLibraryVariants("release", "debug") | ||||
|         compilations.all { | ||||
|             kotlinOptions.jvmTarget = "1.8" | ||||
|         } | ||||
|         sourceSets["androidMain"].dependencies { | ||||
|             implementation(kotlin("stdlib-jdk8")) | ||||
|         } | ||||
|         sourceSets["androidTest"].dependencies { | ||||
|             implementation(kotlin("test-junit")) | ||||
|             implementation("androidx.test.ext:junit:1.1.1") | ||||
|             implementation("androidx.test.espresso:espresso-core:3.2.0") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     sourceSets.all { | ||||
|         languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| android { | ||||
|     defaultConfig { | ||||
|         compileSdkVersion(30) | ||||
|         minSdkVersion(21) | ||||
|         testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" | ||||
|         externalNativeBuild { | ||||
|             cmake {} | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     compileOptions { | ||||
|         sourceCompatibility = JavaVersion.VERSION_1_8 | ||||
|         targetCompatibility = JavaVersion.VERSION_1_8 | ||||
|     } | ||||
| 
 | ||||
|     externalNativeBuild { | ||||
|         cmake { | ||||
|             setPath("src/androidMain/CMakeLists.txt") | ||||
|         } | ||||
|     } | ||||
|     ndkVersion = "21.3.6528147" | ||||
| 
 | ||||
|     sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") | ||||
| 
 | ||||
|     afterEvaluate { | ||||
|         tasks.withType<com.android.build.gradle.tasks.factory.AndroidUnitTest>().all { | ||||
|             enabled = false | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| val copyJni by tasks.creating(Sync::class) { | ||||
|     dependsOn(":native:buildSecp256k1Jvm") | ||||
|     from(rootDir.resolve("native/build/linux/libsecp256k1-jni.so")) { rename { "libsecp256k1-jni-linux-x86_64.so" } } | ||||
|     from(rootDir.resolve("native/build/darwin/libsecp256k1-jni.dylib")) { rename { "libsecp256k1-jni-darwin-x86_64.dylib" } } | ||||
|     from(rootDir.resolve("native/build/mingw/secp256k1-jni.dll")) { rename { "secp256k1-jni-mingw-x86_64.dll" } } | ||||
|     into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native")) | ||||
| } | ||||
| 
 | ||||
| afterEvaluate { | ||||
|     configure(listOf("Debug", "Release").map { tasks["externalNativeBuild$it"] }) { | ||||
|         dependsOn(":native:buildSecp256k1Android") | ||||
|     } | ||||
| } | ||||
							
								
								
									
										14
									
								
								jni/src/androidMain/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								jni/src/androidMain/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| cmake_minimum_required(VERSION 3.10.0) | ||||
| 
 | ||||
| add_library( secp256k1-jni SHARED | ||||
|     ${CMAKE_CURRENT_LIST_DIR}/../../../native/jni/src/org_bitcoin_NativeSecp256k1.c | ||||
|     ${CMAKE_CURRENT_LIST_DIR}/../../../native/jni/src/org_bitcoin_Secp256k1Context.c | ||||
| ) | ||||
| 
 | ||||
| target_include_directories( secp256k1-jni | ||||
|     PUBLIC ${CMAKE_CURRENT_LIST_DIR}/../../../native/secp256k1 | ||||
| ) | ||||
| 
 | ||||
| target_link_libraries( secp256k1-jni | ||||
|     ${CMAKE_CURRENT_LIST_DIR}/../../../native/build/android/${ANDROID_ABI}/libsecp256k1.a | ||||
| ) | ||||
| @ -0,0 +1,12 @@ | ||||
| package fr.acinq.secp256k1.jni | ||||
| 
 | ||||
| public actual object NativeSecp256k1Loader { | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     @Synchronized | ||||
|     @Throws(Exception::class) | ||||
|     actual fun load() { | ||||
|         System.loadLibrary("secp256k1-jni") | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| package fr.acinq.secp256k1.jni | ||||
| 
 | ||||
| import kotlin.jvm.JvmStatic | ||||
| 
 | ||||
| 
 | ||||
| public expect object NativeSecp256k1Loader { | ||||
| 
 | ||||
|     public fun load() | ||||
| 
 | ||||
| } | ||||
| @ -15,7 +15,7 @@ | ||||
|  */ | ||||
| package org.bitcoin | ||||
| 
 | ||||
| import fr.acinq.secp256k1.Secp256k1Loader.initialize | ||||
| import kotlin.jvm.JvmStatic | ||||
| 
 | ||||
| /** | ||||
|  * This class holds the context reference used in native methods | ||||
| @ -0,0 +1,211 @@ | ||||
| package fr.acinq.secp256k1.jni | ||||
| 
 | ||||
| import java.io.* | ||||
| import java.util.* | ||||
| 
 | ||||
| /** | ||||
| * Set the system properties, org.sqlite.lib.path, org.sqlite.lib.name, | ||||
| * appropriately so that the SQLite JDBC driver can find *.dll, *.jnilib and | ||||
| * *.so files, according to the current OS (win, linux, mac). | ||||
| * The library files are automatically extracted from this project's package (JAR). | ||||
| * usage: call [.initialize] before using SQLite JDBC driver. | ||||
| * | ||||
| * @author leo | ||||
| */ | ||||
| public actual object NativeSecp256k1Loader { | ||||
|    private var extracted = false | ||||
| 
 | ||||
|    /** | ||||
|     * Loads secp256k1 native library. | ||||
|     * | ||||
|     * @return True if secp256k1 native library is successfully loaded; false otherwise. | ||||
|     * @throws Exception if loading fails | ||||
|     */ | ||||
|    @JvmStatic | ||||
|    @Synchronized | ||||
|    @Throws(Exception::class) | ||||
|    public actual fun load() { | ||||
|        // only cleanup before the first extract | ||||
|        if (!extracted) { | ||||
|            cleanup() | ||||
|        } | ||||
|        loadSecp256k1NativeLibrary() | ||||
|    } | ||||
| 
 | ||||
|    private val tempDir: File | ||||
|        get() = File( | ||||
|            System.getProperty( | ||||
|                "fr.acinq.secp256k1.tmpdir", | ||||
|                System.getProperty("java.io.tmpdir") | ||||
|            ) | ||||
|        ) | ||||
| 
 | ||||
|    /** | ||||
|     * Deleted old native libraries e.g. on Windows the DLL file is not removed | ||||
|     * on VM-Exit (bug #80) | ||||
|     */ | ||||
|    @JvmStatic | ||||
|    public fun cleanup() { | ||||
|        val tempFolder = tempDir.absolutePath | ||||
|        val dir = File(tempFolder) | ||||
|        val nativeLibFiles = dir.listFiles(object : FilenameFilter { | ||||
|            private val searchPattern = "secp256k1-" | ||||
|            override fun accept(dir: File, name: String): Boolean { | ||||
|                return name.startsWith(searchPattern) && !name.endsWith(".lck") | ||||
|            } | ||||
|        }) | ||||
|        if (nativeLibFiles != null) { | ||||
|            for (nativeLibFile in nativeLibFiles) { | ||||
|                val lckFile = File(nativeLibFile.absolutePath + ".lck") | ||||
|                if (!lckFile.exists()) { | ||||
|                    try { | ||||
|                        nativeLibFile.delete() | ||||
|                    } catch (e: SecurityException) { | ||||
|                        System.err.println("Failed to delete old native lib" + e.message) | ||||
|                    } | ||||
|                } | ||||
|            } | ||||
|        } | ||||
|    } | ||||
| 
 | ||||
|    @Throws(IOException::class) | ||||
|    private fun InputStream.contentsEquals(other: InputStream): Boolean { | ||||
|        val bufThis = this as? BufferedInputStream | ||||
|            ?: BufferedInputStream(this) | ||||
|        val bufOther = other as? BufferedInputStream | ||||
|            ?: BufferedInputStream(other) | ||||
|        var ch = bufThis.read() | ||||
|        while (ch != -1) { | ||||
|            val ch2 = bufOther.read() | ||||
|            if (ch != ch2) return false | ||||
|            ch = bufThis.read() | ||||
|        } | ||||
|        val ch2 = bufOther.read() | ||||
|        return ch2 == -1 | ||||
|    } | ||||
| 
 | ||||
|    /** | ||||
|     * Extracts and loads the specified library file to the target folder | ||||
|     * | ||||
|     * @param libDir Library path. | ||||
|     * @param libFileName       Library name. | ||||
|     * @param targetDirectory          Target folder. | ||||
|     * @return | ||||
|     */ | ||||
|    private fun extractAndLoadLibraryFile(libDir: String, libFileName: String, targetDirectory: String): Boolean { | ||||
|        val libPath = "$libDir/$libFileName" | ||||
|        // Include architecture name in temporary filename in order to avoid conflicts | ||||
|        // when multiple JVMs with different architectures running at the same time | ||||
|        val uuid = UUID.randomUUID().toString() | ||||
|        val extractedLibFileName = String.format("secp256k1-%s-%s", uuid, libFileName) | ||||
|        val extractedLckFileName = "$extractedLibFileName.lck" | ||||
|        val extractedLibFile = File(targetDirectory, extractedLibFileName) | ||||
|        val extractedLckFile = File(targetDirectory, extractedLckFileName) | ||||
|        return try { | ||||
|            // Extract a native library file into the target directory | ||||
|            val reader = NativeSecp256k1Loader::class.java.getResourceAsStream(libPath) | ||||
|            if (!extractedLckFile.exists()) { | ||||
|                FileOutputStream(extractedLckFile).close() | ||||
|            } | ||||
|            val writer = FileOutputStream(extractedLibFile) | ||||
|            try { | ||||
|                val buffer = ByteArray(8192) | ||||
|                var bytesRead = reader.read(buffer) | ||||
|                while (bytesRead != -1) { | ||||
|                    writer.write(buffer, 0, bytesRead) | ||||
|                    bytesRead = reader.read(buffer) | ||||
|                } | ||||
|            } finally { | ||||
|                // Delete the extracted lib file on JVM exit. | ||||
|                extractedLibFile.deleteOnExit() | ||||
|                extractedLckFile.deleteOnExit() | ||||
|                writer.close() | ||||
|                reader.close() | ||||
|            } | ||||
| 
 | ||||
|            // Set executable (x) flag to enable Java to load the native library | ||||
|            extractedLibFile.setReadable(true) | ||||
|            extractedLibFile.setWritable(true, true) | ||||
|            extractedLibFile.setExecutable(true) | ||||
| 
 | ||||
|            // Check whether the contents are properly copied from the resource folder | ||||
|            NativeSecp256k1Loader::class.java.getResourceAsStream(libPath).use { nativeIn -> | ||||
|                FileInputStream(extractedLibFile).use { extractedLibIn -> | ||||
|                    if (!nativeIn.contentsEquals(extractedLibIn)) { | ||||
|                        throw RuntimeException( | ||||
|                            String.format( | ||||
|                                "Failed to write a native library file at %s", | ||||
|                                extractedLibFile | ||||
|                            ) | ||||
|                        ) | ||||
|                    } | ||||
|                } | ||||
|            } | ||||
| 
 | ||||
|            loadNativeLibrary(targetDirectory, extractedLibFileName) | ||||
|        } catch (e: IOException) { | ||||
|            System.err.println(e.message) | ||||
|            false | ||||
|        } | ||||
|    } | ||||
| 
 | ||||
|    /** | ||||
|     * Loads native library using the given path and name of the library. | ||||
|     * | ||||
|     * @param path Path of the native library. | ||||
|     * @param name Name  of the native library. | ||||
|     * @return True for successfully loading; false otherwise. | ||||
|     */ | ||||
|    private fun loadNativeLibrary(path: String, name: String): Boolean { | ||||
|        val libPath = File(path, name) | ||||
|        return if (libPath.exists()) { | ||||
|            try { | ||||
|                System.load(File(path, name).absolutePath) | ||||
|                true | ||||
|            } catch (e: UnsatisfiedLinkError) { | ||||
|                System.err.println("Failed to load native library:$name. osinfo: ${OSInfo.nativeSuffix}") | ||||
|                System.err.println(e) | ||||
|                false | ||||
|            } | ||||
|        } else { | ||||
|            false | ||||
|        } | ||||
|    } | ||||
| 
 | ||||
|    /** | ||||
|     * Loads secp256k1 native library using given path and name of the library. | ||||
|     * | ||||
|     * @throws | ||||
|     */ | ||||
|    private fun loadSecp256k1NativeLibrary() { | ||||
|        if (extracted) { | ||||
|            return | ||||
|        } | ||||
| 
 | ||||
|        // Try loading library from fr.acinq.secp256k1.lib.path library path */ | ||||
|        val libraryPath = System.getProperty("fr.acinq.secp256k1.lib.path") | ||||
|        val libraryName = System.getProperty("fr.acinq.secp256k1.lib.name") ?: System.mapLibraryName("secp256k1-jni-${OSInfo.nativeSuffix}") | ||||
|        if (libraryPath != null) { | ||||
|            if (loadNativeLibrary(libraryPath, libraryName)) { | ||||
|                extracted = true | ||||
|                return | ||||
|            } | ||||
|        } | ||||
| 
 | ||||
|        // Load the os-dependent library from the jar file | ||||
|        val packagePath = NativeSecp256k1Loader::class.java.getPackage().name.replace("\\.".toRegex(), "/") | ||||
|        val embeddedLibraryPath = "/$packagePath/native" | ||||
|        val hasNativeLib = NativeSecp256k1Loader::class.java.getResource("$embeddedLibraryPath/$libraryName") != null | ||||
|        if (!hasNativeLib) { | ||||
|            error("No native library found: at $embeddedLibraryPath/$libraryName") | ||||
|        } | ||||
| 
 | ||||
|        // Try extracting the library from jar | ||||
|        if (extractAndLoadLibraryFile(embeddedLibraryPath, libraryName, tempDir.absolutePath)) { | ||||
|            extracted = true | ||||
|            return | ||||
|        } | ||||
|        extracted = false | ||||
|        return | ||||
|    } | ||||
| } | ||||
							
								
								
									
										214
									
								
								jni/src/jvmMain/kotlin/fr/acinq/secp256k1/jni/OSInfo.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								jni/src/jvmMain/kotlin/fr/acinq/secp256k1/jni/OSInfo.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,214 @@ | ||||
| package fr.acinq.secp256k1.jni | ||||
| 
 | ||||
| import java.io.ByteArrayOutputStream | ||||
| import java.io.IOException | ||||
| import java.util.* | ||||
| 
 | ||||
| /** | ||||
| * Provides OS name and architecture name. | ||||
| * | ||||
| * @author leo | ||||
| */ | ||||
| @Suppress("DuplicatedCode") | ||||
| internal object OSInfo { | ||||
|    private val archMapping = HashMap<String, String>() | ||||
|    private const val X86 = "x86" | ||||
|    private const val X86_64 = "x86_64" | ||||
|    private const val IA64_32 = "ia64_32" | ||||
|    private const val IA64 = "ia64" | ||||
|    private const val PPC = "ppc" | ||||
|    private const val PPC64 = "ppc64" | ||||
| 
 | ||||
|    @JvmStatic val nativeSuffix: String get() = "$os-$arch" | ||||
| 
 | ||||
|    @JvmStatic val os: String get() = translateOSName(System.getProperty("os.name")) | ||||
| 
 | ||||
|    @JvmStatic val hardwareName: String get() = | ||||
|        try { | ||||
|            val p = Runtime.getRuntime().exec("uname -m") | ||||
|            p.waitFor() | ||||
|            val input = p.inputStream | ||||
|            input.use { | ||||
|                val b = ByteArrayOutputStream() | ||||
|                val buf = ByteArray(32) | ||||
|                var readLen = it.read(buf, 0, buf.size) | ||||
|                while (readLen >= 0) { | ||||
|                    b.write(buf, 0, readLen) | ||||
|                    readLen = it.read(buf, 0, buf.size) | ||||
|                } | ||||
|                b.toString() | ||||
|            } | ||||
|        } catch (e: Throwable) { | ||||
|            System.err.println("Error while running uname -m: " + e.message) | ||||
|            "unknown" | ||||
|        } | ||||
| 
 | ||||
|    @JvmStatic | ||||
|    private fun resolveArmArchType(): String { | ||||
|        if (System.getProperty("os.name").contains("Linux")) { | ||||
|            val armType = hardwareName | ||||
|            // armType (uname -m) can be armv5t, armv5te, armv5tej, armv5tejl, armv6, armv7, armv7l, aarch64, i686// ignored: fall back to "arm" arch (soft-float ABI) | ||||
|            // ignored: fall back to "arm" arch (soft-float ABI) | ||||
|            // determine if first JVM found uses ARM hard-float ABI | ||||
|            when { | ||||
|                armType.startsWith("armv6") -> { | ||||
|                    // Raspberry PI | ||||
|                    return "armv6" | ||||
|                } | ||||
|                armType.startsWith("armv7") -> { | ||||
|                    // Generic | ||||
|                    return "armv7" | ||||
|                } | ||||
|                armType.startsWith("armv5") -> { | ||||
|                    // Use armv5, soft-float ABI | ||||
|                    return "arm" | ||||
|                } | ||||
|                armType == "aarch64" -> { | ||||
|                    // Use arm64 | ||||
|                    return "arm64" | ||||
|                } | ||||
| 
 | ||||
|                // Java 1.8 introduces a system property to determine armel or armhf | ||||
|                // http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8005545 | ||||
| 
 | ||||
|                // For java7, we stil need to if run some shell commands to determine ABI of JVM | ||||
|                else -> { | ||||
|                    val abi = System.getProperty("sun.arch.abi") | ||||
|                    if (abi != null && abi.startsWith("gnueabihf")) { | ||||
|                        return "armv7" | ||||
|                    } | ||||
| 
 | ||||
|                    // For java7, we stil need to if run some shell commands to determine ABI of JVM | ||||
|                    val javaHome = System.getProperty("java.home") | ||||
|                    try { | ||||
|                        // determine if first JVM found uses ARM hard-float ABI | ||||
|                        var exitCode = Runtime.getRuntime().exec("which readelf").waitFor() | ||||
|                        if (exitCode == 0) { | ||||
|                            val cmdarray = arrayOf( | ||||
|                                "/bin/sh", "-c", "find '" + javaHome + | ||||
|                                        "' -name 'libjvm.so' | head -1 | xargs readelf -A | " + | ||||
|                                        "grep 'Tag_ABI_VFP_args: VFP registers'" | ||||
|                            ) | ||||
|                            exitCode = Runtime.getRuntime().exec(cmdarray).waitFor() | ||||
|                            if (exitCode == 0) { | ||||
|                                return "armv7" | ||||
|                            } | ||||
|                        } else { | ||||
|                            System.err.println( | ||||
|                                "WARNING! readelf not found. Cannot check if running on an armhf system, " + | ||||
|                                        "armel architecture will be presumed." | ||||
|                            ) | ||||
|                        } | ||||
|                    } catch (e: IOException) { | ||||
|                        // ignored: fall back to "arm" arch (soft-float ABI) | ||||
|                    } catch (e: InterruptedException) { | ||||
|                        // ignored: fall back to "arm" arch (soft-float ABI) | ||||
|                    } | ||||
|                } | ||||
|            } | ||||
| 
 | ||||
|            // Java 1.8 introduces a system property to determine armel or armhf | ||||
|            // http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8005545 | ||||
|            val abi = System.getProperty("sun.arch.abi") | ||||
|            if (abi != null && abi.startsWith("gnueabihf")) { | ||||
|                return "armv7" | ||||
|            } | ||||
| 
 | ||||
|            // For java7, we stil need to if run some shell commands to determine ABI of JVM | ||||
|            val javaHome = System.getProperty("java.home") | ||||
|            try { | ||||
|                // determine if first JVM found uses ARM hard-float ABI | ||||
|                var exitCode = Runtime.getRuntime().exec("which readelf").waitFor() | ||||
|                if (exitCode == 0) { | ||||
|                    val cmdarray = arrayOf( | ||||
|                        "/bin/sh", "-c", "find '" + javaHome + | ||||
|                                "' -name 'libjvm.so' | head -1 | xargs readelf -A | " + | ||||
|                                "grep 'Tag_ABI_VFP_args: VFP registers'" | ||||
|                    ) | ||||
|                    exitCode = Runtime.getRuntime().exec(cmdarray).waitFor() | ||||
|                    if (exitCode == 0) { | ||||
|                        return "armv7" | ||||
|                    } | ||||
|                } else { | ||||
|                    System.err.println( | ||||
|                        "WARNING! readelf not found. Cannot check if running on an armhf system, " + | ||||
|                                "armel architecture will be presumed." | ||||
|                    ) | ||||
|                } | ||||
|            } catch (e: IOException) { | ||||
|                // ignored: fall back to "arm" arch (soft-float ABI) | ||||
|            } catch (e: InterruptedException) { | ||||
|                // ignored: fall back to "arm" arch (soft-float ABI) | ||||
|            } | ||||
|        } | ||||
|        // Use armv5, soft-float ABI | ||||
|        return "arm" | ||||
|    } | ||||
| 
 | ||||
|    // For Android | ||||
|    @JvmStatic | ||||
|    val arch: String? | ||||
|        get() { | ||||
|            val systemOsArch = System.getProperty("os.arch") | ||||
|            val osArch = | ||||
|                if (systemOsArch.startsWith("arm")) { | ||||
|                    resolveArmArchType() | ||||
|                } else { | ||||
|                    val lc = systemOsArch.toLowerCase(Locale.US) | ||||
|                    if (archMapping.containsKey(lc)) return archMapping[lc] | ||||
|                    systemOsArch | ||||
|                } | ||||
|            return translateArchNameToFolderName(osArch) | ||||
|        } | ||||
| 
 | ||||
|    @JvmStatic | ||||
|    fun translateOSName(osName: String): String = | ||||
|        when { | ||||
|            osName.contains("Windows") -> "mingw" | ||||
|            osName.contains("Mac") || osName.contains("Darwin") -> "darwin" | ||||
|            osName.contains("Linux") -> "linux" | ||||
|            osName.contains("AIX") -> "aix" | ||||
|            else -> osName.replace("\\W".toRegex(), "") | ||||
|        } | ||||
| 
 | ||||
|    @JvmStatic | ||||
|    fun translateArchNameToFolderName(archName: String): String = archName.replace("\\W".toRegex(), "") | ||||
| 
 | ||||
|    init { | ||||
|        // x86 mappings | ||||
|        archMapping[X86] = X86 | ||||
|        archMapping["i386"] = X86 | ||||
|        archMapping["i486"] = X86 | ||||
|        archMapping["i586"] = X86 | ||||
|        archMapping["i686"] = X86 | ||||
|        archMapping["pentium"] = X86 | ||||
| 
 | ||||
|        // x86_64 mappings | ||||
|        archMapping[X86_64] = X86_64 | ||||
|        archMapping["amd64"] = X86_64 | ||||
|        archMapping["em64t"] = X86_64 | ||||
|        archMapping["universal"] = X86_64 // Needed for openjdk7 in Mac | ||||
| 
 | ||||
|        // Itenium 64-bit mappings | ||||
|        archMapping[IA64] = IA64 | ||||
|        archMapping["ia64w"] = IA64 | ||||
| 
 | ||||
|        // Itenium 32-bit mappings, usually an HP-UX construct | ||||
|        archMapping[IA64_32] = IA64_32 | ||||
|        archMapping["ia64n"] = IA64_32 | ||||
| 
 | ||||
|        // PowerPC mappings | ||||
|        archMapping[PPC] = PPC | ||||
|        archMapping["power"] = PPC | ||||
|        archMapping["powerpc"] = PPC | ||||
|        archMapping["power_pc"] = PPC | ||||
|        archMapping["power_rs"] = PPC | ||||
| 
 | ||||
|        // TODO: PowerPC 64bit mappings | ||||
|        archMapping[PPC64] = PPC64 | ||||
|        archMapping["power64"] = PPC64 | ||||
|        archMapping["powerpc64"] = PPC64 | ||||
|        archMapping["power_pc64"] = PPC64 | ||||
|        archMapping["power_rs64"] = PPC64 | ||||
|    } | ||||
| } | ||||
							
								
								
									
										88
									
								
								native/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								native/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | ||||
| evaluationDependsOn(":jni") | ||||
| 
 | ||||
| val currentOs = org.gradle.internal.os.OperatingSystem.current() | ||||
| 
 | ||||
| val buildSecp256k1 by tasks.creating { group = "build" } | ||||
| 
 | ||||
| sealed class Cross { | ||||
|     abstract fun cmd(target: String, nativeDir: File): List<String> | ||||
|     class DockCross(val cross: String) : Cross() { | ||||
|         override fun cmd(target: String, nativeDir: File): List<String> = listOf("./dockcross-$cross", "bash", "-c", "CROSS=1 TARGET=$target ./build.sh") | ||||
|     } | ||||
|     class MultiArch(val crossTriple: String) : Cross() { | ||||
|         override fun cmd(target: String, nativeDir: File): List<String> { | ||||
|             val uid = Runtime.getRuntime().exec("id -u").inputStream.use { it.reader().readText() }.trim().toInt() | ||||
|             return listOf( | ||||
|                 "docker", "run", "--rm", "-v", "${nativeDir.absolutePath}:/workdir", | ||||
|                 "-e", "CROSS_TRIPLE=$crossTriple", "-e", "TARGET=$target", "-e", "TO_UID=$uid", "-e", "CROSS=1", | ||||
|                 "multiarch/crossbuild", "./build.sh" | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| val buildSecp256k1Jvm by tasks.creating { | ||||
|     group = "build" | ||||
|     buildSecp256k1.dependsOn(this) | ||||
| } | ||||
| fun creatingBuildSecp256k1(target: String, cross: Cross?) = tasks.creating(Exec::class) { | ||||
|     group = "build" | ||||
|     buildSecp256k1Jvm.dependsOn(this) | ||||
| 
 | ||||
|     inputs.files(projectDir.resolve("build.sh")) | ||||
|     outputs.dir(projectDir.resolve("build/$target")) | ||||
| 
 | ||||
|     workingDir = projectDir | ||||
|     environment("TARGET", target) | ||||
|     commandLine((cross?.cmd(target, workingDir) ?: emptyList()) + "./build.sh") | ||||
| } | ||||
| val buildSecp256k1Darwin by creatingBuildSecp256k1("darwin", if (currentOs.isMacOsX) null else Cross.MultiArch("x86_64-apple-darwin")) | ||||
| val buildSecp256k1Linux by creatingBuildSecp256k1("linux", if (currentOs.isLinux) null else Cross.DockCross("linux-x64")) | ||||
| val buildSecp256k1Mingw by creatingBuildSecp256k1("mingw", if (currentOs.isWindows) null else Cross.DockCross("windows-x64")) | ||||
| 
 | ||||
| val buildSecp256k1Ios by tasks.creating(Exec::class) { | ||||
|     group = "build" | ||||
|     buildSecp256k1.dependsOn(this) | ||||
| 
 | ||||
|     onlyIf { currentOs.isMacOsX } | ||||
| 
 | ||||
|     inputs.files(projectDir.resolve("build-ios.sh")) | ||||
|     outputs.dir(projectDir.resolve("build/ios")) | ||||
| 
 | ||||
|     workingDir = projectDir | ||||
|     commandLine("./build-ios.sh") | ||||
| } | ||||
| 
 | ||||
| val buildSecp256k1Android by tasks.creating { | ||||
|     group = "build" | ||||
|     buildSecp256k1.dependsOn(this) | ||||
| } | ||||
| fun creatingBuildSecp256k1Android(arch: String) = tasks.creating(Exec::class) { | ||||
|     group = "build" | ||||
|     buildSecp256k1Android.dependsOn(this) | ||||
| 
 | ||||
|     inputs.files(projectDir.resolve("build-android.sh")) | ||||
|     outputs.dir(projectDir.resolve("build/android/$arch")) | ||||
| 
 | ||||
|     workingDir = projectDir | ||||
| 
 | ||||
|     val toolchain = when { | ||||
|         currentOs.isLinux -> "linux-x86_64" | ||||
|         currentOs.isMacOsX -> "darwin-x86_64" | ||||
|         currentOs.isWindows -> "windows-x86_64" | ||||
|         else -> error("No Android toolchain defined for this OS: $currentOs") | ||||
|     } | ||||
|     environment("TOOLCHAIN", toolchain) | ||||
|     environment("ARCH", arch) | ||||
|     environment("ANDROID_NDK", (project(":jni").extensions["android"] as com.android.build.gradle.LibraryExtension).ndkDirectory) | ||||
|     commandLine("./build-android.sh") | ||||
| } | ||||
| val buildSecp256k1AndroidX86_64 by creatingBuildSecp256k1Android("x86_64") | ||||
| val buildSecp256k1AndroidX86 by creatingBuildSecp256k1Android("x86") | ||||
| val buildSecp256k1AndroidArm64v8a by creatingBuildSecp256k1Android("arm64-v8a") | ||||
| val buildSecp256k1AndroidArmeabiv7a by creatingBuildSecp256k1Android("armeabi-v7a") | ||||
| 
 | ||||
| val clean by tasks.creating { | ||||
|     doLast { | ||||
|         delete(projectDir.resolve("build")) | ||||
|     } | ||||
| } | ||||
| @ -5,12 +5,10 @@ pluginManagement { | ||||
|         gradlePluginPortal() | ||||
|         jcenter() | ||||
|     } | ||||
| 
 | ||||
|     resolutionStrategy { | ||||
|         eachPlugin { | ||||
|             if (requested.id.id == "com.android.library") useModule("com.android.tools.build:gradle:${requested.version}") | ||||
|         } | ||||
|     } | ||||
| } | ||||
| rootProject.name = "secp256k1-kmp" | ||||
| 
 | ||||
| include( | ||||
|     ":native", | ||||
|     ":jni" | ||||
| ) | ||||
| @ -1,14 +0,0 @@ | ||||
| cmake_minimum_required(VERSION 3.10.0) | ||||
| 
 | ||||
| add_library( secp256k1-jni SHARED | ||||
|     ${CMAKE_CURRENT_LIST_DIR}/../../native/jni/src/org_bitcoin_NativeSecp256k1.c | ||||
|     ${CMAKE_CURRENT_LIST_DIR}/../../native/jni/src/org_bitcoin_Secp256k1Context.c | ||||
| ) | ||||
| 
 | ||||
| target_include_directories( secp256k1-jni | ||||
|     PUBLIC ${CMAKE_CURRENT_LIST_DIR}/../../native/secp256k1 | ||||
| ) | ||||
| 
 | ||||
| target_link_libraries( secp256k1-jni | ||||
|     ${CMAKE_CURRENT_LIST_DIR}/../../native/build/android/${ANDROID_ABI}/libsecp256k1.a | ||||
| ) | ||||
| @ -1,15 +0,0 @@ | ||||
| package fr.acinq.secp256k1 | ||||
| 
 | ||||
| import java.io.* | ||||
| import java.util.* | ||||
| 
 | ||||
| internal actual object Secp256k1Loader { | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     @Synchronized | ||||
|     @Throws(Exception::class) | ||||
|     actual fun initialize() { | ||||
|         System.loadLibrary("secp256k1-jni") | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -17,7 +17,6 @@ | ||||
| package fr.acinq.secp256k1 | ||||
| 
 | ||||
| import kotlin.jvm.JvmStatic | ||||
| import kotlin.jvm.Synchronized | ||||
| 
 | ||||
| public enum class SigFormat(internal val size: Int) { COMPACT(64), DER(72) } | ||||
| 
 | ||||
| @ -25,37 +24,54 @@ public enum class PubKeyFormat(internal val size: Int) { COMPRESSED(33), UNCOMPR | ||||
| 
 | ||||
| public expect object Secp256k1 { | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun verify(data: ByteArray, signature: ByteArray, pub: ByteArray): Boolean | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun sign(data: ByteArray, sec: ByteArray, format: SigFormat): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun signatureNormalize(sig: ByteArray, format: SigFormat): Pair<ByteArray, Boolean> | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun secKeyVerify(seckey: ByteArray): Boolean | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun computePubkey(seckey: ByteArray, format: PubKeyFormat): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun parsePubkey(pubkey: ByteArray, format: PubKeyFormat): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun cleanup() | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun privKeyNegate(privkey: ByteArray): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun pubKeyNegate(pubkey: ByteArray): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun createECDHSecret(seckey: ByteArray, pubkey: ByteArray): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int, format: PubKeyFormat): ByteArray | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun randomize(seed: ByteArray): Boolean | ||||
| } | ||||
| @ -1,228 +0,0 @@ | ||||
| package fr.acinq.secp256k1 | ||||
| 
 | ||||
| import java.io.ByteArrayOutputStream | ||||
| import java.io.IOException | ||||
| import java.util.* | ||||
| 
 | ||||
| /*-------------------------------------------------------------------------- | ||||
|  *  Copyright 2008 Taro L. Saito | ||||
|  * | ||||
|  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *  you may not use this file except in compliance with the License. | ||||
|  *  You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *  Unless required by applicable law or agreed to in writing, software | ||||
|  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *  See the License for the specific language governing permissions and | ||||
|  *  limitations under the License. | ||||
|  *--------------------------------------------------------------------------*/ /** | ||||
|  * Provides OS name and architecture name. | ||||
|  * | ||||
|  * @author leo | ||||
|  */ | ||||
| @Suppress("DuplicatedCode") | ||||
| internal object OSInfo { | ||||
|     private val archMapping = HashMap<String, String>() | ||||
|     private const val X86 = "x86" | ||||
|     private const val X86_64 = "x86_64" | ||||
|     private const val IA64_32 = "ia64_32" | ||||
|     private const val IA64 = "ia64" | ||||
|     private const val PPC = "ppc" | ||||
|     private const val PPC64 = "ppc64" | ||||
| 
 | ||||
|     @JvmStatic val nativeSuffix: String get() = "$os-$arch" | ||||
| 
 | ||||
|     @JvmStatic val os: String get() = translateOSName(System.getProperty("os.name")) | ||||
| 
 | ||||
|     @JvmStatic val hardwareName: String get() = | ||||
|         try { | ||||
|             val p = Runtime.getRuntime().exec("uname -m") | ||||
|             p.waitFor() | ||||
|             val input = p.inputStream | ||||
|             input.use { | ||||
|                 val b = ByteArrayOutputStream() | ||||
|                 val buf = ByteArray(32) | ||||
|                 var readLen = it.read(buf, 0, buf.size) | ||||
|                 while (readLen >= 0) { | ||||
|                     b.write(buf, 0, readLen) | ||||
|                     readLen = it.read(buf, 0, buf.size) | ||||
|                 } | ||||
|                 b.toString() | ||||
|             } | ||||
|         } catch (e: Throwable) { | ||||
|             System.err.println("Error while running uname -m: " + e.message) | ||||
|             "unknown" | ||||
|         } | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     private fun resolveArmArchType(): String { | ||||
|         if (System.getProperty("os.name").contains("Linux")) { | ||||
|             val armType = hardwareName | ||||
|             // armType (uname -m) can be armv5t, armv5te, armv5tej, armv5tejl, armv6, armv7, armv7l, aarch64, i686// ignored: fall back to "arm" arch (soft-float ABI) | ||||
|             // ignored: fall back to "arm" arch (soft-float ABI) | ||||
|             // determine if first JVM found uses ARM hard-float ABI | ||||
|             when { | ||||
|                 armType.startsWith("armv6") -> { | ||||
|                     // Raspberry PI | ||||
|                     return "armv6" | ||||
|                 } | ||||
|                 armType.startsWith("armv7") -> { | ||||
|                     // Generic | ||||
|                     return "armv7" | ||||
|                 } | ||||
|                 armType.startsWith("armv5") -> { | ||||
|                     // Use armv5, soft-float ABI | ||||
|                     return "arm" | ||||
|                 } | ||||
|                 armType == "aarch64" -> { | ||||
|                     // Use arm64 | ||||
|                     return "arm64" | ||||
|                 } | ||||
| 
 | ||||
|                 // Java 1.8 introduces a system property to determine armel or armhf | ||||
|                 // http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8005545 | ||||
| 
 | ||||
|                 // For java7, we stil need to if run some shell commands to determine ABI of JVM | ||||
|                 else -> { | ||||
|                     val abi = System.getProperty("sun.arch.abi") | ||||
|                     if (abi != null && abi.startsWith("gnueabihf")) { | ||||
|                         return "armv7" | ||||
|                     } | ||||
| 
 | ||||
|                     // For java7, we stil need to if run some shell commands to determine ABI of JVM | ||||
|                     val javaHome = System.getProperty("java.home") | ||||
|                     try { | ||||
|                         // determine if first JVM found uses ARM hard-float ABI | ||||
|                         var exitCode = Runtime.getRuntime().exec("which readelf").waitFor() | ||||
|                         if (exitCode == 0) { | ||||
|                             val cmdarray = arrayOf( | ||||
|                                 "/bin/sh", "-c", "find '" + javaHome + | ||||
|                                         "' -name 'libjvm.so' | head -1 | xargs readelf -A | " + | ||||
|                                         "grep 'Tag_ABI_VFP_args: VFP registers'" | ||||
|                             ) | ||||
|                             exitCode = Runtime.getRuntime().exec(cmdarray).waitFor() | ||||
|                             if (exitCode == 0) { | ||||
|                                 return "armv7" | ||||
|                             } | ||||
|                         } else { | ||||
|                             System.err.println( | ||||
|                                 "WARNING! readelf not found. Cannot check if running on an armhf system, " + | ||||
|                                         "armel architecture will be presumed." | ||||
|                             ) | ||||
|                         } | ||||
|                     } catch (e: IOException) { | ||||
|                         // ignored: fall back to "arm" arch (soft-float ABI) | ||||
|                     } catch (e: InterruptedException) { | ||||
|                         // ignored: fall back to "arm" arch (soft-float ABI) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // Java 1.8 introduces a system property to determine armel or armhf | ||||
|             // http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8005545 | ||||
|             val abi = System.getProperty("sun.arch.abi") | ||||
|             if (abi != null && abi.startsWith("gnueabihf")) { | ||||
|                 return "armv7" | ||||
|             } | ||||
| 
 | ||||
|             // For java7, we stil need to if run some shell commands to determine ABI of JVM | ||||
|             val javaHome = System.getProperty("java.home") | ||||
|             try { | ||||
|                 // determine if first JVM found uses ARM hard-float ABI | ||||
|                 var exitCode = Runtime.getRuntime().exec("which readelf").waitFor() | ||||
|                 if (exitCode == 0) { | ||||
|                     val cmdarray = arrayOf( | ||||
|                         "/bin/sh", "-c", "find '" + javaHome + | ||||
|                                 "' -name 'libjvm.so' | head -1 | xargs readelf -A | " + | ||||
|                                 "grep 'Tag_ABI_VFP_args: VFP registers'" | ||||
|                     ) | ||||
|                     exitCode = Runtime.getRuntime().exec(cmdarray).waitFor() | ||||
|                     if (exitCode == 0) { | ||||
|                         return "armv7" | ||||
|                     } | ||||
|                 } else { | ||||
|                     System.err.println( | ||||
|                         "WARNING! readelf not found. Cannot check if running on an armhf system, " + | ||||
|                                 "armel architecture will be presumed." | ||||
|                     ) | ||||
|                 } | ||||
|             } catch (e: IOException) { | ||||
|                 // ignored: fall back to "arm" arch (soft-float ABI) | ||||
|             } catch (e: InterruptedException) { | ||||
|                 // ignored: fall back to "arm" arch (soft-float ABI) | ||||
|             } | ||||
|         } | ||||
|         // Use armv5, soft-float ABI | ||||
|         return "arm" | ||||
|     } | ||||
| 
 | ||||
|     // For Android | ||||
|     @JvmStatic | ||||
|     val arch: String? | ||||
|         get() { | ||||
|             val systemOsArch = System.getProperty("os.arch") | ||||
|             val osArch = | ||||
|                 if (systemOsArch.startsWith("arm")) { | ||||
|                     resolveArmArchType() | ||||
|                 } else { | ||||
|                     val lc = systemOsArch.toLowerCase(Locale.US) | ||||
|                     if (archMapping.containsKey(lc)) return archMapping[lc] | ||||
|                     systemOsArch | ||||
|                 } | ||||
|             return translateArchNameToFolderName(osArch) | ||||
|         } | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     fun translateOSName(osName: String): String = | ||||
|         when { | ||||
|             osName.contains("Windows") -> "mingw" | ||||
|             osName.contains("Mac") || osName.contains("Darwin") -> "darwin" | ||||
|             osName.contains("Linux") -> "linux" | ||||
|             osName.contains("AIX") -> "aix" | ||||
|             else -> osName.replace("\\W".toRegex(), "") | ||||
|         } | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     fun translateArchNameToFolderName(archName: String): String = archName.replace("\\W".toRegex(), "") | ||||
| 
 | ||||
|     init { | ||||
|         // x86 mappings | ||||
|         archMapping[X86] = X86 | ||||
|         archMapping["i386"] = X86 | ||||
|         archMapping["i486"] = X86 | ||||
|         archMapping["i586"] = X86 | ||||
|         archMapping["i686"] = X86 | ||||
|         archMapping["pentium"] = X86 | ||||
| 
 | ||||
|         // x86_64 mappings | ||||
|         archMapping[X86_64] = X86_64 | ||||
|         archMapping["amd64"] = X86_64 | ||||
|         archMapping["em64t"] = X86_64 | ||||
|         archMapping["universal"] = X86_64 // Needed for openjdk7 in Mac | ||||
| 
 | ||||
|         // Itenium 64-bit mappings | ||||
|         archMapping[IA64] = IA64 | ||||
|         archMapping["ia64w"] = IA64 | ||||
| 
 | ||||
|         // Itenium 32-bit mappings, usually an HP-UX construct | ||||
|         archMapping[IA64_32] = IA64_32 | ||||
|         archMapping["ia64n"] = IA64_32 | ||||
| 
 | ||||
|         // PowerPC mappings | ||||
|         archMapping[PPC] = PPC | ||||
|         archMapping["power"] = PPC | ||||
|         archMapping["powerpc"] = PPC | ||||
|         archMapping["power_pc"] = PPC | ||||
|         archMapping["power_rs"] = PPC | ||||
| 
 | ||||
|         // TODO: PowerPC 64bit mappings | ||||
|         archMapping[PPC64] = PPC64 | ||||
|         archMapping["power64"] = PPC64 | ||||
|         archMapping["powerpc64"] = PPC64 | ||||
|         archMapping["power_pc64"] = PPC64 | ||||
|         archMapping["power_rs64"] = PPC64 | ||||
|     } | ||||
| } | ||||
| @ -17,15 +17,18 @@ | ||||
| package fr.acinq.secp256k1 | ||||
| 
 | ||||
| import org.bitcoin.NativeSecp256k1 | ||||
| 
 | ||||
| internal expect object Secp256k1Loader { | ||||
|     fun initialize() | ||||
| } | ||||
| import java.lang.IllegalStateException | ||||
| 
 | ||||
| public actual object Secp256k1 { | ||||
| 
 | ||||
|     init { | ||||
|         Secp256k1Loader.initialize() | ||||
|         try { | ||||
|             val cls = Class.forName("fr.acinq.secp256k1.jni.NativeSecp256k1Loader") | ||||
|             val load = cls.getMethod("load") | ||||
|             load.invoke(null) | ||||
|         } catch (ex: ClassNotFoundException) { | ||||
|             throw IllegalStateException("Could not load native Secp256k1 JNI library. Have you added the JNI dependency?", ex) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public actual fun verify(data: ByteArray, signature: ByteArray, pub: ByteArray): Boolean = NativeSecp256k1.verify(data, signature, pub) | ||||
| @ -1,218 +0,0 @@ | ||||
| package fr.acinq.secp256k1 | ||||
| 
 | ||||
| import java.io.* | ||||
| import java.util.* | ||||
| 
 | ||||
| /*-------------------------------------------------------------------------- | ||||
|  *  Copyright 2007 Taro L. Saito | ||||
|  * | ||||
|  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *  you may not use this file except in compliance with the License. | ||||
|  *  You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *  Unless required by applicable law or agreed to in writing, software | ||||
|  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *  See the License for the specific language governing permissions and | ||||
|  *  limitations under the License. | ||||
|  *--------------------------------------------------------------------------*/ /** | ||||
|  * Set the system properties, org.sqlite.lib.path, org.sqlite.lib.name, | ||||
|  * appropriately so that the SQLite JDBC driver can find *.dll, *.jnilib and | ||||
|  * *.so files, according to the current OS (win, linux, mac). | ||||
|  * The library files are automatically extracted from this project's package (JAR). | ||||
|  * usage: call [.initialize] before using SQLite JDBC driver. | ||||
|  * | ||||
|  * @author leo | ||||
|  */ | ||||
| internal actual object Secp256k1Loader { | ||||
|     private var extracted = false | ||||
| 
 | ||||
|     /** | ||||
|      * Loads secp256k1 native library. | ||||
|      * | ||||
|      * @return True if secp256k1 native library is successfully loaded; false otherwise. | ||||
|      * @throws Exception if loading fails | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     @Synchronized | ||||
|     @Throws(Exception::class) | ||||
|     actual fun initialize() { | ||||
|         // only cleanup before the first extract | ||||
|         if (!extracted) { | ||||
|             cleanup() | ||||
|         } | ||||
|         loadSecp256k1NativeLibrary() | ||||
|     } | ||||
| 
 | ||||
|     private val tempDir: File | ||||
|         get() = File(System.getProperty("fr.acinq.secp256k1.tmpdir", System.getProperty("java.io.tmpdir"))) | ||||
| 
 | ||||
|     /** | ||||
|      * Deleted old native libraries e.g. on Windows the DLL file is not removed | ||||
|      * on VM-Exit (bug #80) | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     fun cleanup() { | ||||
|         val tempFolder = tempDir.absolutePath | ||||
|         val dir = File(tempFolder) | ||||
|         val nativeLibFiles = dir.listFiles(object : FilenameFilter { | ||||
|             private val searchPattern = "secp256k1-" | ||||
|             override fun accept(dir: File, name: String): Boolean { | ||||
|                 return name.startsWith(searchPattern) && !name.endsWith(".lck") | ||||
|             } | ||||
|         }) | ||||
|         if (nativeLibFiles != null) { | ||||
|             for (nativeLibFile in nativeLibFiles) { | ||||
|                 val lckFile = File(nativeLibFile.absolutePath + ".lck") | ||||
|                 if (!lckFile.exists()) { | ||||
|                     try { | ||||
|                         nativeLibFile.delete() | ||||
|                     } catch (e: SecurityException) { | ||||
|                         System.err.println("Failed to delete old native lib" + e.message) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Throws(IOException::class) | ||||
|     private fun InputStream.contentsEquals(other: InputStream): Boolean { | ||||
|         val bufThis = this as? BufferedInputStream ?: BufferedInputStream(this) | ||||
|         val bufOther = other as? BufferedInputStream ?: BufferedInputStream(other) | ||||
|         var ch = bufThis.read() | ||||
|         while (ch != -1) { | ||||
|             val ch2 = bufOther.read() | ||||
|             if (ch != ch2) return false | ||||
|             ch = bufThis.read() | ||||
|         } | ||||
|         val ch2 = bufOther.read() | ||||
|         return ch2 == -1 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Extracts and loads the specified library file to the target folder | ||||
|      * | ||||
|      * @param libDir Library path. | ||||
|      * @param libFileName       Library name. | ||||
|      * @param targetDirectory          Target folder. | ||||
|      * @return | ||||
|      */ | ||||
|     private fun extractAndLoadLibraryFile(libDir: String, libFileName: String, targetDirectory: String): Boolean { | ||||
|         val libPath = "$libDir/$libFileName" | ||||
|         // Include architecture name in temporary filename in order to avoid conflicts | ||||
|         // when multiple JVMs with different architectures running at the same time | ||||
|         val uuid = UUID.randomUUID().toString() | ||||
|         val extractedLibFileName = String.format("secp256k1-%s-%s", uuid, libFileName) | ||||
|         val extractedLckFileName = "$extractedLibFileName.lck" | ||||
|         val extractedLibFile = File(targetDirectory, extractedLibFileName) | ||||
|         val extractedLckFile = File(targetDirectory, extractedLckFileName) | ||||
|         return try { | ||||
|             // Extract a native library file into the target directory | ||||
|             val reader = Secp256k1Loader::class.java.getResourceAsStream(libPath) | ||||
|             if (!extractedLckFile.exists()) { | ||||
|                 FileOutputStream(extractedLckFile).close() | ||||
|             } | ||||
|             val writer = FileOutputStream(extractedLibFile) | ||||
|             try { | ||||
|                 val buffer = ByteArray(8192) | ||||
|                 var bytesRead = reader.read(buffer) | ||||
|                 while (bytesRead != -1) { | ||||
|                     writer.write(buffer, 0, bytesRead) | ||||
|                     bytesRead = reader.read(buffer) | ||||
|                 } | ||||
|             } finally { | ||||
|                 // Delete the extracted lib file on JVM exit. | ||||
|                 extractedLibFile.deleteOnExit() | ||||
|                 extractedLckFile.deleteOnExit() | ||||
|                 writer.close() | ||||
|                 reader.close() | ||||
|             } | ||||
| 
 | ||||
|             // Set executable (x) flag to enable Java to load the native library | ||||
|             extractedLibFile.setReadable(true) | ||||
|             extractedLibFile.setWritable(true, true) | ||||
|             extractedLibFile.setExecutable(true) | ||||
| 
 | ||||
|             // Check whether the contents are properly copied from the resource folder | ||||
|             Secp256k1Loader::class.java.getResourceAsStream(libPath).use { nativeIn -> | ||||
|                 FileInputStream(extractedLibFile).use { extractedLibIn -> | ||||
|                     if (!nativeIn.contentsEquals(extractedLibIn)) { | ||||
|                         throw RuntimeException( | ||||
|                             String.format( | ||||
|                                 "Failed to write a native library file at %s", | ||||
|                                 extractedLibFile | ||||
|                             ) | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             loadNativeLibrary(targetDirectory, extractedLibFileName) | ||||
|         } catch (e: IOException) { | ||||
|             System.err.println(e.message) | ||||
|             false | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Loads native library using the given path and name of the library. | ||||
|      * | ||||
|      * @param path Path of the native library. | ||||
|      * @param name Name  of the native library. | ||||
|      * @return True for successfully loading; false otherwise. | ||||
|      */ | ||||
|     private fun loadNativeLibrary(path: String, name: String): Boolean { | ||||
|         val libPath = File(path, name) | ||||
|         return if (libPath.exists()) { | ||||
|             try { | ||||
|                 System.load(File(path, name).absolutePath) | ||||
|                 true | ||||
|             } catch (e: UnsatisfiedLinkError) { | ||||
|                 System.err.println("Failed to load native library:$name. osinfo: ${OSInfo.nativeSuffix}") | ||||
|                 System.err.println(e) | ||||
|                 false | ||||
|             } | ||||
|         } else { | ||||
|             false | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Loads secp256k1 native library using given path and name of the library. | ||||
|      * | ||||
|      * @throws | ||||
|      */ | ||||
|     private fun loadSecp256k1NativeLibrary() { | ||||
|         if (extracted) { | ||||
|             return | ||||
|         } | ||||
| 
 | ||||
|         // Try loading library from fr.acinq.secp256k1.lib.path library path */ | ||||
|         val libraryPath = System.getProperty("fr.acinq.secp256k1.lib.path") | ||||
|         val libraryName = System.getProperty("fr.acinq.secp256k1.lib.name") ?: System.mapLibraryName("secp256k1-jni-${OSInfo.nativeSuffix}") | ||||
|         if (libraryPath != null) { | ||||
|             if (loadNativeLibrary(libraryPath, libraryName)) { | ||||
|                 extracted = true | ||||
|                 return | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Load the os-dependent library from the jar file | ||||
|         val packagePath = Secp256k1Loader::class.java.getPackage().name.replace("\\.".toRegex(), "/") | ||||
|         val embeddedLibraryPath = "/$packagePath/native" | ||||
|         val hasNativeLib = Secp256k1Loader::class.java.getResource("$embeddedLibraryPath/$libraryName") != null | ||||
|         if (!hasNativeLib) { | ||||
|             error("No native library found: at $embeddedLibraryPath/$libraryName") | ||||
|         } | ||||
| 
 | ||||
|         // Try extracting the library from jar | ||||
|         if (extractAndLoadLibraryFile(embeddedLibraryPath, libraryName, tempDir.absolutePath)) { | ||||
|             extracted = true | ||||
|             return | ||||
|         } | ||||
|         extracted = false | ||||
|         return | ||||
|     } | ||||
| } | ||||
							
								
								
									
										565
									
								
								src/jvmMain/kotlin/org/bitcoin/NativeSecp256k1.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										565
									
								
								src/jvmMain/kotlin/org/bitcoin/NativeSecp256k1.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,565 @@ | ||||
| /* | ||||
|  * Copyright 2013 Google Inc. | ||||
|  * Copyright 2014-2016 the libsecp256k1 contributors | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *    http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| package org.bitcoin | ||||
| 
 | ||||
| import org.bitcoin.NativeSecp256k1Util.AssertFailException | ||||
| import java.math.BigInteger | ||||
| import java.nio.ByteBuffer | ||||
| import java.nio.ByteOrder | ||||
| import java.util.concurrent.locks.Lock | ||||
| import java.util.concurrent.locks.ReentrantReadWriteLock | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  * This class holds native methods to handle ECDSA verification. | ||||
|  * | ||||
|  * | ||||
|  * You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1 | ||||
|  * | ||||
|  * | ||||
|  * To build secp256k1 for use with bitcoinj, run | ||||
|  * `./configure --enable-jni --enable-experimental --enable-module-ecdh` | ||||
|  * and `make` then copy `.libs/libsecp256k1.so` to your system library path | ||||
|  * or point the JVM to the folder containing it with -Djava.library.path | ||||
|  * | ||||
|  */ | ||||
| public object NativeSecp256k1 { | ||||
|     private val rwl = ReentrantReadWriteLock() | ||||
|     private val r: Lock = rwl.readLock() | ||||
|     private val w: Lock = rwl.writeLock() | ||||
|     private val nativeECDSABuffer = ThreadLocal<ByteBuffer?>() | ||||
| 
 | ||||
|     private fun pack(vararg buffers: ByteArray): ByteBuffer { | ||||
|         var size = 0 | ||||
|         for (i in buffers.indices) { | ||||
|             size += buffers[i].size | ||||
|         } | ||||
| 
 | ||||
|         val byteBuff = nativeECDSABuffer.get()?.takeIf { it.capacity() >= size } | ||||
|             ?: ByteBuffer.allocateDirect(size).also { | ||||
|                 it.order(ByteOrder.nativeOrder()) | ||||
|                 nativeECDSABuffer.set(it) | ||||
|             } | ||||
|         byteBuff.rewind() | ||||
|         for (i in buffers.indices) { | ||||
|             byteBuff.put(buffers[i]) | ||||
|         } | ||||
|         return byteBuff | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Verifies the given secp256k1 signature in native code. | ||||
|      * Calling when enabled == false is undefined (probably library not loaded) | ||||
|      * | ||||
|      * @param data      The data which was signed, must be exactly 32 bytes | ||||
|      * @param signature The signature | ||||
|      * @param pub       The public key which did the signing | ||||
|      * @return true if the signature is valid | ||||
|      * @throws AssertFailException in case of failure | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun verify(data: ByteArray, signature: ByteArray, pub: ByteArray): Boolean { | ||||
|         require(data.size == 32 && signature.size <= 520 && pub.size <= 520) | ||||
|         val byteBuff = pack(data, signature, pub) | ||||
|         r.lock() | ||||
|         return try { | ||||
|             secp256k1_ecdsa_verify( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext(), | ||||
|                 signature.size, | ||||
|                 pub.size | ||||
|             ) == 1 | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * libsecp256k1 Create an ECDSA signature. | ||||
|      * | ||||
|      * @param data    Message hash, 32 bytes | ||||
|      * @param sec     Secret key, 32 bytes | ||||
|      * @param compact True for compact signature, false for DER | ||||
|      * @return a signature, or an empty array is signing failed | ||||
|      * @throws AssertFailException in case of failure | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun sign(data: ByteArray, sec: ByteArray, compact: Boolean): ByteArray { | ||||
|         require(data.size == 32 && sec.size <= 32) | ||||
|         val byteBuff = pack(data, sec) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_ecdsa_sign( | ||||
|                 byteBuff, | ||||
|                 compact, | ||||
|                 Secp256k1Context.getContext() | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val sigArr = retByteArray[0] | ||||
|         val sigLen = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals( | ||||
|             sigArr.size, | ||||
|             sigLen, | ||||
|             "Got bad signature length." | ||||
|         ) | ||||
|         return if (retVal == 0) ByteArray(0) else sigArr | ||||
|     } | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun signatureNormalize(sig: ByteArray, compact: Boolean): Pair<ByteArray, Boolean> { | ||||
|         require(sig.size == 64 || sig.size in 70..73) | ||||
|         val byteBuff = pack(sig) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_ecdsa_normalize( | ||||
|                 byteBuff, | ||||
|                 sig.size, | ||||
|                 compact, | ||||
|                 Secp256k1Context.getContext() | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val sigArr = retByteArray[0] | ||||
|         val sigLen = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() | ||||
|         val retBool = BigInteger(byteArrayOf(retByteArray[1][2])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals( | ||||
|             sigArr.size, | ||||
|             sigLen, | ||||
|             "Got bad signature length." | ||||
|         ) | ||||
|         return (if (retVal == 0) ByteArray(0) else sigArr) to (retBool == 1) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid | ||||
|      * | ||||
|      * @param seckey ECDSA Secret key, 32 bytes | ||||
|      * @return true if seckey is valid | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     public fun secKeyVerify(seckey: ByteArray): Boolean { | ||||
|         require(seckey.size == 32) | ||||
|         val byteBuff = pack(seckey) | ||||
|         r.lock() | ||||
|         return try { | ||||
|             secp256k1_ec_seckey_verify( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext() | ||||
|             ) == 1 | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * libsecp256k1 Compute Pubkey - computes public key from secret key | ||||
|      * | ||||
|      * @param seckey ECDSA Secret key, 32 bytes | ||||
|      * @throws AssertFailException if parameters are not valid | ||||
|      * @return the corresponding public key (uncompressed) | ||||
|      */ | ||||
|     //TODO add a 'compressed' arg | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun computePubkey(seckey: ByteArray, compressed: Boolean): ByteArray { | ||||
|         require(seckey.size == 32) | ||||
|         val byteBuff = pack(seckey) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_ec_pubkey_create( | ||||
|                 byteBuff, | ||||
|                 compressed, | ||||
|                 Secp256k1Context.getContext() | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val pubArr = retByteArray[0] | ||||
|         val pubLen = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.") | ||||
|         return if (retVal == 0) ByteArray(0) else pubArr | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param pubkey public key | ||||
|      * @return the input public key (uncompressed) if valid, or an empty array | ||||
|      * @throws AssertFailException in case of failure | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun parsePubkey(pubkey: ByteArray, compressed: Boolean): ByteArray { | ||||
|         require(pubkey.size == 33 || pubkey.size == 65) | ||||
|         val byteBuff = pack(pubkey) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_ec_pubkey_parse( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext(), | ||||
|                 pubkey.size, | ||||
|                 compressed | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val pubArr = retByteArray[0] | ||||
|         BigInteger(byteArrayOf(retByteArray[1][0])).toInt() | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals( | ||||
|             pubArr.size, | ||||
|             if (compressed) 33 else 65, | ||||
|             "Got bad pubkey length." | ||||
|         ) | ||||
|         return if (retVal == 0) ByteArray(0) else pubArr | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * libsecp256k1 Cleanup - This destroys the secp256k1 context object | ||||
|      * This should be called at the end of the program for proper cleanup of the context. | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     @Synchronized | ||||
|     public fun cleanup() { | ||||
|         w.lock() | ||||
|         try { | ||||
|             secp256k1_destroy_context(Secp256k1Context.getContext()) | ||||
|         } finally { | ||||
|             w.unlock() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun privKeyNegate(privkey: ByteArray): ByteArray { | ||||
|         require(privkey.size == 32) | ||||
|         val byteBuff = pack(privkey) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_privkey_negate( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext() | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val privArr = retByteArray[0] | ||||
|         val privLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals( | ||||
|             privArr.size, | ||||
|             privLen, | ||||
|             "Got bad privkey length." | ||||
|         ) | ||||
|         NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") | ||||
|         return privArr | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it | ||||
|      * | ||||
|      * @param privkey 32-byte seckey | ||||
|      * @param tweak   some bytes to tweak with | ||||
|      * @return privkey * tweak | ||||
|      * @throws AssertFailException in case of failure | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray { | ||||
|         require(privkey.size == 32) | ||||
|         val byteBuff = pack(privkey, tweak) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_privkey_tweak_mul( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext() | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val privArr = retByteArray[0] | ||||
|         val privLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals( | ||||
|             privArr.size, | ||||
|             privLen, | ||||
|             "Got bad privkey length." | ||||
|         ) | ||||
|         NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") | ||||
|         return privArr | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it | ||||
|      * | ||||
|      * @param privkey 32-byte seckey | ||||
|      * @param tweak  some bytes to tweak with | ||||
|      * @return privkey + tweak | ||||
|      * @throws AssertFailException in case of failure | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray { | ||||
|         require(privkey.size == 32) | ||||
|         val byteBuff = pack(privkey, tweak) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_privkey_tweak_add( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext() | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val privArr = retByteArray[0] | ||||
|         val privLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals(privArr.size, privLen, "Got bad pubkey length.") | ||||
|         NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") | ||||
|         return privArr | ||||
|     } | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun pubKeyNegate(pubkey: ByteArray): ByteArray { | ||||
|         require(pubkey.size == 33 || pubkey.size == 65) | ||||
|         val byteBuff = pack(pubkey) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_pubkey_negate( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext(), | ||||
|                 pubkey.size | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val pubArr = retByteArray[0] | ||||
|         val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.") | ||||
|         NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") | ||||
|         return pubArr | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it | ||||
|      * | ||||
|      * @param tweak  some bytes to tweak with | ||||
|      * @param pubkey 32-byte seckey | ||||
|      * @return pubkey + tweak | ||||
|      * @throws AssertFailException in case of failure | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray { | ||||
|         require(pubkey.size == 33 || pubkey.size == 65) | ||||
|         val byteBuff = pack(pubkey, tweak) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_pubkey_tweak_add( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext(), | ||||
|                 pubkey.size | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val pubArr = retByteArray[0] | ||||
|         val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.") | ||||
|         NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") | ||||
|         return pubArr | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it | ||||
|      * | ||||
|      * @param tweak  some bytes to tweak with | ||||
|      * @param pubkey 32-byte seckey | ||||
|      * @return pubkey * tweak | ||||
|      * @throws AssertFailException in case of failure | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray { | ||||
|         require(pubkey.size == 33 || pubkey.size == 65) | ||||
|         val byteBuff = pack(pubkey, tweak) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_pubkey_tweak_mul( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext(), | ||||
|                 pubkey.size | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val pubArr = retByteArray[0] | ||||
|         val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.") | ||||
|         NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") | ||||
|         return pubArr | ||||
|     } | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray { | ||||
|         require(pubkey1.size == 33 || pubkey1.size == 65) | ||||
|         require(pubkey2.size == 33 || pubkey2.size == 65) | ||||
|         val byteBuff = pack(pubkey1, pubkey2) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_ec_pubkey_add( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext(), | ||||
|                 pubkey1.size, | ||||
|                 pubkey2.size | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val pubArr = retByteArray[0] | ||||
|         val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals(pubkey1.size, pubLen, "Got bad pubkey length.") | ||||
|         NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") | ||||
|         return pubArr | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * libsecp256k1 create ECDH secret - constant time ECDH calculation | ||||
|      * | ||||
|      * @param seckey byte array of secret key used in exponentiaion | ||||
|      * @param pubkey byte array of public key used in exponentiaion | ||||
|      * @return ecdh(sedckey, pubkey) | ||||
|      * @throws AssertFailException in case of failure | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun createECDHSecret(seckey: ByteArray, pubkey: ByteArray): ByteArray { | ||||
|         require(seckey.size <= 32 && pubkey.size <= 65) | ||||
|         val byteBuff = pack(seckey, pubkey) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_ecdh( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext(), | ||||
|                 pubkey.size | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val resArr = retByteArray[0] | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals(resArr.size, 32, "Got bad result length.") | ||||
|         NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") | ||||
|         return resArr | ||||
|     } | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int, compressed: Boolean): ByteArray { | ||||
|         require(sig.size == 64) | ||||
|         require(message.size == 32) | ||||
|         val byteBuff = pack(sig, message) | ||||
|         val retByteArray: Array<ByteArray> | ||||
|         r.lock() | ||||
|         retByteArray = try { | ||||
|             secp256k1_ecdsa_recover( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext(), | ||||
|                 recid, | ||||
|                 compressed | ||||
|             ) | ||||
|         } finally { | ||||
|             r.unlock() | ||||
|         } | ||||
|         val resArr = retByteArray[0] | ||||
|         val retVal = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() | ||||
|         NativeSecp256k1Util.assertEquals( | ||||
|             resArr.size, | ||||
|             if (compressed) 33 else 65, | ||||
|             "Got bad result length." | ||||
|         ) | ||||
|         NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.") | ||||
|         return resArr | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * libsecp256k1 randomize - updates the context randomization | ||||
|      * | ||||
|      * @param seed 32-byte random seed | ||||
|      * @return true if successful | ||||
|      * @throws AssertFailException in case of failure | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     @Synchronized | ||||
|     @Throws(AssertFailException::class) | ||||
|     public fun randomize(seed: ByteArray): Boolean { | ||||
|         require(seed.size == 32) | ||||
|         val byteBuff = pack(seed) | ||||
|         w.lock() | ||||
|         return try { | ||||
|             secp256k1_context_randomize( | ||||
|                 byteBuff, | ||||
|                 Secp256k1Context.getContext() | ||||
|             ) == 1 | ||||
|         } finally { | ||||
|             w.unlock() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @JvmStatic private external fun secp256k1_context_randomize(byteBuff: ByteBuffer, context: Long): Int | ||||
|     @JvmStatic private external fun secp256k1_privkey_negate(byteBuff: ByteBuffer, context: Long): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_privkey_tweak_add(byteBuff: ByteBuffer, context: Long): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_privkey_tweak_mul(byteBuff: ByteBuffer, context: Long): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_pubkey_negate(byteBuff: ByteBuffer, context: Long, pubLen: Int): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_pubkey_tweak_add(byteBuff: ByteBuffer, context: Long, pubLen: Int): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_pubkey_tweak_mul(byteBuff: ByteBuffer, context: Long, pubLen: Int): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_destroy_context(context: Long) | ||||
|     @JvmStatic private external fun secp256k1_ecdsa_verify(byteBuff: ByteBuffer, context: Long, sigLen: Int, pubLen: Int): Int | ||||
|     @JvmStatic private external fun secp256k1_ecdsa_sign(byteBuff: ByteBuffer, compact: Boolean, context: Long): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_ecdsa_normalize(byteBuff: ByteBuffer, sigLen: Int, compact: Boolean, context: Long): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_ec_seckey_verify(byteBuff: ByteBuffer, context: Long): Int | ||||
|     @JvmStatic private external fun secp256k1_ec_pubkey_create(byteBuff: ByteBuffer, compressed: Boolean, context: Long): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_ec_pubkey_parse(byteBuff: ByteBuffer, context: Long, inputLen: Int, compressed: Boolean): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_ec_pubkey_add(byteBuff: ByteBuffer, context: Long, lent1: Int, len2: Int): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_ecdh(byteBuff: ByteBuffer, context: Long, inputLen: Int): Array<ByteArray> | ||||
|     @JvmStatic private external fun secp256k1_ecdsa_recover(byteBuff: ByteBuffer, context: Long, recid: Int, compressed: Boolean): Array<ByteArray> | ||||
| } | ||||
							
								
								
									
										28
									
								
								src/jvmMain/kotlin/org/bitcoin/NativeSecp256k1Util.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/jvmMain/kotlin/org/bitcoin/NativeSecp256k1Util.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * Copyright 2014-2016 the libsecp256k1 contributors | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *    http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| package org.bitcoin | ||||
| 
 | ||||
| import kotlin.jvm.Throws | ||||
| import java.lang.Exception | ||||
| 
 | ||||
| internal object NativeSecp256k1Util { | ||||
|     @Throws(AssertFailException::class) | ||||
|     fun assertEquals(val1: Int, val2: Int, message: String) { | ||||
|         if (val1 != val2) throw AssertFailException("FAIL: $message") | ||||
|     } | ||||
| 
 | ||||
|     class AssertFailException(message: String?) : Exception(message) | ||||
| } | ||||
							
								
								
									
										39
									
								
								src/jvmMain/kotlin/org/bitcoin/Secp256k1Context.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/jvmMain/kotlin/org/bitcoin/Secp256k1Context.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| /* | ||||
|  * Copyright 2014-2016 the libsecp256k1 contributors | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *    http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| package org.bitcoin | ||||
| 
 | ||||
| /** | ||||
|  * This class holds the context reference used in native methods | ||||
|  * to handle ECDSA operations. | ||||
|  */ | ||||
| public object Secp256k1Context { | ||||
|     @JvmStatic | ||||
|     public val isEnabled: Boolean //true if the library is loaded | ||||
|     private val context: Long //ref to pointer to context obj | ||||
| 
 | ||||
|     @JvmStatic | ||||
|     public fun getContext(): Long { | ||||
|         return if (!isEnabled) -1 else context //sanity check | ||||
|     } | ||||
| 
 | ||||
|     @JvmStatic private external fun secp256k1_init_context(): Long | ||||
| 
 | ||||
|     init { //static initializer | ||||
|         isEnabled = true | ||||
|         context = | ||||
|             secp256k1_init_context() | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user