6 Commits

Author SHA1 Message Date
Salomon BRYS
d702925e40 Kotlin 1.4.0 (#11)
Co-authored-by: Salomon BRYS <salomon@kodein.net>
2020-08-18 10:52:42 +02:00
sstone
7a33c81c01 Use Kotlin 1.4 2020-08-18 09:51:20 +02:00
Romain Boisselle
2615187b87 Kotlin 1.4.0-rc (#10)
* Move to kotlin 1.4.0-rc

* Removing broken WSL bash

see https://github.com/actions/virtual-environments/issues/50

Co-authored-by: Salomon BRYS <salomon@kodein.net>
2020-08-02 20:08:02 +02:00
Salomon BRYS
dbf8301f34 Android loading may fallback to standard JVM (#9)
* Android loading may fallback to standard JVM

* Java code must target JVM 1.8 in order to be compatible Android.

Co-authored-by: Salomon BRYS <salomon@kodein.net>
2020-07-16 22:21:30 +02:00
Fabrice Drouin
0cc4c251f9 Fixup for #6 (native signature format detection) (#8)
* Fixup for #6

Behaviour was changed in the JNI wapper but not in Kotlin native code.

* Set version to 0.2.1-1.4-M3
2020-07-09 20:16:39 +02:00
Salomon BRYS
eeac972785 Update README.md (#7)
The package in bintray has lost the `-kmp`.
The badge needs to be updated to avoid image not found.
2020-07-09 17:54:22 +02:00
19 changed files with 118 additions and 82 deletions

View File

@@ -37,7 +37,9 @@ jobs:
run: brew install automake run: brew install automake
- name: Set up shell - name: Set up shell
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
run: echo ::add-path::C:\msys64\usr\bin\ run: |
echo ::add-path::C:\msys64\usr\bin\
rm.exe "C:/WINDOWS/system32/bash.EXE"
- name: Check JVM - name: Check JVM
shell: bash shell: bash
run: ./gradlew jvmTest run: ./gradlew jvmTest

View File

@@ -45,7 +45,9 @@ jobs:
run: brew install automake run: brew install automake
- name: Set up shell - name: Set up shell
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
run: echo ::add-path::C:\msys64\usr\bin\ run: |
echo ::add-path::C:\msys64\usr\bin\
rm.exe "C:/WINDOWS/system32/bash.EXE"
- name: Check JVM - name: Check JVM
shell: bash shell: bash
run: ./gradlew jvmTest run: ./gradlew jvmTest

View File

@@ -51,7 +51,9 @@ jobs:
run: brew install automake run: brew install automake
- name: Set up shell - name: Set up shell
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
run: echo ::add-path::C:\msys64\usr\bin\ run: |
echo ::add-path::C:\msys64\usr\bin\
rm.exe "C:/WINDOWS/system32/bash.EXE"
- name: Check JVM - name: Check JVM
shell: bash shell: bash
run: ./gradlew jvmTest run: ./gradlew jvmTest

View File

@@ -4,7 +4,7 @@ Kotlin/Multiplatform wrapper for Bitcoin Core's secp256k1 library. Targets: JVM,
## Installation ## Installation
[ ![Download](https://api.bintray.com/packages/acinq/libs/secp256k1-kmp/images/download.svg) ](https://bintray.com/acinq/libs/secp256k1-kmp/) [ ![Download](https://api.bintray.com/packages/acinq/libs/secp256k1/images/download.svg) ](https://bintray.com/acinq/libs/secp256k1-kmp/)
First, you need to add the ACINQ libraries repository: First, you need to add the ACINQ libraries repository:

View File

@@ -4,56 +4,49 @@ import org.apache.http.entity.ContentType
import org.apache.http.entity.StringEntity import org.apache.http.entity.StringEntity
import org.apache.http.impl.auth.BasicScheme import org.apache.http.impl.auth.BasicScheme
import org.apache.http.auth.UsernamePasswordCredentials import org.apache.http.auth.UsernamePasswordCredentials
import org.gradle.internal.os.OperatingSystem
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins { plugins {
kotlin("multiplatform") version "1.4-M3" kotlin("multiplatform") version "1.4.0"
`maven-publish` `maven-publish`
} }
buildscript { buildscript {
repositories { repositories {
google() google()
maven("https://dl.bintray.com/kotlin/kotlin-eap")
jcenter() jcenter()
} }
dependencies { dependencies {
classpath("com.android.tools.build:gradle:4.0.0") classpath("com.android.tools.build:gradle:4.0.1")
} }
} }
allprojects { allprojects {
group = "fr.acinq.secp256k1" group = "fr.acinq.secp256k1"
version = "0.2.0-1.4-M3" version = "0.3.0"
repositories { repositories {
jcenter() jcenter()
google() google()
maven(url = "https://dl.bintray.com/kotlin/kotlin-eap")
} }
} }
val currentOs = org.gradle.internal.os.OperatingSystem.current() val currentOs = OperatingSystem.current()
kotlin { kotlin {
explicitApi() explicitApi()
val commonMain by sourceSets.getting { val commonMain by sourceSets.getting
dependencies {
implementation(kotlin("stdlib-common"))
}
}
jvm { jvm {
compilations.all { compilations.all {
kotlinOptions.jvmTarget = "1.8" kotlinOptions.jvmTarget = "1.8"
} }
compilations["main"].dependencies {
implementation(kotlin("stdlib-jdk8"))
}
} }
fun org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget.secp256k1CInterop(target: String) { fun KotlinNativeTarget.secp256k1CInterop(target: String) {
compilations["main"].cinterops { compilations["main"].cinterops {
val libsecp256k1 by creating { val libsecp256k1 by creating {
includeDirs.headerFilterOnly(project.file("native/secp256k1/include/")) includeDirs.headerFilterOnly(project.file("native/secp256k1/include/"))
@@ -66,16 +59,17 @@ kotlin {
linuxX64("linux") { linuxX64("linux") {
secp256k1CInterop("host") secp256k1CInterop("host")
compilations["main"].defaultSourceSet.dependsOn(nativeMain)
// https://youtrack.jetbrains.com/issue/KT-39396 // https://youtrack.jetbrains.com/issue/KT-39396
compilations["main"].kotlinOptions.freeCompilerArgs += listOf("-include-binary", "$rootDir/native/build/linux/libsecp256k1.a") compilations["main"].kotlinOptions.freeCompilerArgs += listOf("-include-binary", "$rootDir/native/build/linux/libsecp256k1.a")
compilations["main"].defaultSourceSet.dependsOn(nativeMain)
} }
ios { ios {
secp256k1CInterop("ios") secp256k1CInterop("ios")
// https://youtrack.jetbrains.com/issue/KT-39396 // 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) compilations["main"].defaultSourceSet.dependsOn(nativeMain)
// https://youtrack.jetbrains.com/issue/KT-39396
compilations["main"].kotlinOptions.freeCompilerArgs += listOf("-include-binary", "$rootDir/native/build/ios/libsecp256k1.a")
} }
sourceSets.all { sourceSets.all {
@@ -88,13 +82,13 @@ kotlin {
allprojects { allprojects {
plugins.withId("org.jetbrains.kotlin.multiplatform") { plugins.withId("org.jetbrains.kotlin.multiplatform") {
afterEvaluate { afterEvaluate {
val currentOs = org.gradle.internal.os.OperatingSystem.current() val currentOs = OperatingSystem.current()
val targets = when { val targets = when {
currentOs.isLinux -> listOf() currentOs.isLinux -> listOf()
currentOs.isMacOsX -> listOf("linux") currentOs.isMacOsX -> listOf("linux")
currentOs.isWindows -> listOf("linux") currentOs.isWindows -> listOf("linux")
else -> listOf("linux") else -> listOf("linux")
}.mapNotNull { kotlin.targets.findByName(it) as? org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget } }.mapNotNull { kotlin.targets.findByName(it) as? KotlinNativeTarget }
configure(targets) { configure(targets) {
compilations.all { compilations.all {
@@ -189,12 +183,14 @@ if (hasBintray) {
} }
} }
afterEvaluate { allprojects {
tasks.withType<AbstractTestTask>() { afterEvaluate {
testLogging { tasks.withType<AbstractTestTask>() {
events("passed", "skipped", "failed", "standard_out", "standard_error") testLogging {
showExceptions = true events("passed", "skipped", "failed", "standard_out", "standard_error")
showStackTraces = true showExceptions = true
showStackTraces = true
}
} }
} }
} }

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -10,7 +10,6 @@ kotlin {
dependencies { dependencies {
api(project(":jni")) api(project(":jni"))
implementation(kotlin("stdlib-jdk8"))
} }
android { android {
@@ -48,18 +47,12 @@ afterEvaluate {
} }
} }
android { afterEvaluate {
afterEvaluate { publishing {
publishing { publications {
publications { create<MavenPublication>("android") {
create<MavenPublication>("android") { artifactId = "secp256k1-jni-android"
artifactId = "secp256k1-jni-android" from(components["release"])
from(components["release"])
}
// create<MavenPublication>("androidDebug") {
// artifactId = "secp256k1-jni-android-debug"
// from(components["debug"])
// }
} }
} }
} }

View File

@@ -0,0 +1,31 @@
package fr.acinq.secp256k1.jni
import android.util.Log
import fr.acinq.secp256k1.Secp256k1
import fr.acinq.secp256k1.NativeSecp256k1
import java.util.*
public object NativeSecp256k1AndroidLoader {
@JvmStatic
@Synchronized
@Throws(Exception::class)
fun load(): Secp256k1 {
try {
System.loadLibrary("secp256k1-jni")
return NativeSecp256k1
} catch (ex: UnsatisfiedLinkError) {
// Purposefully not using Android Log
println("Could not load Android Secp256k1. Trying to extract JVM platform specific version.")
try {
val cls = Class.forName("fr.acinq.secp256k1.jni.NativeSecp256k1JvmLoader")
val load = cls.getMethod("load")
return load.invoke(null) as Secp256k1
} catch (_: ClassNotFoundException) {
throw ex
}
}
}
}

View File

@@ -1,16 +0,0 @@
package fr.acinq.secp256k1.jni
import fr.acinq.secp256k1.Secp256k1
import fr.acinq.secp256k1.NativeSecp256k1
public object NativeSecp256k1Loader {
@JvmStatic
@Synchronized
@Throws(Exception::class)
fun load(): Secp256k1 {
System.loadLibrary("secp256k1-jni")
return NativeSecp256k1
}
}

View File

@@ -3,13 +3,17 @@ plugins {
`maven-publish` `maven-publish`
} }
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlin { kotlin {
explicitApi() explicitApi()
} }
dependencies { dependencies {
api(rootProject) api(rootProject)
implementation(kotlin("stdlib-jdk8"))
} }
val generateHeaders by tasks.creating(JavaCompile::class) { val generateHeaders by tasks.creating(JavaCompile::class) {

View File

@@ -1,7 +1,6 @@
plugins { plugins {
`java-library` `java-library`
// `maven-publish` `maven-publish`
id("ru.vyarus.pom") version "2.1.0"
} }
dependencies { dependencies {

View File

@@ -28,7 +28,6 @@ val buildNativeHost by tasks.creating(Exec::class) {
dependencies { dependencies {
api(project(":jni")) api(project(":jni"))
implementation(kotlin("stdlib-jdk8"))
} }
publishing { publishing {

View File

@@ -14,7 +14,7 @@ import java.util.*
* *
* @author leo * @author leo
*/ */
public object NativeSecp256k1Loader { public object NativeSecp256k1JvmLoader {
private var extracted = false private var extracted = false
/** /**
@@ -106,7 +106,7 @@ public object NativeSecp256k1Loader {
val extractedLckFile = File(targetDirectory, extractedLckFileName) val extractedLckFile = File(targetDirectory, extractedLckFileName)
return try { return try {
// Extract a native library file into the target directory // Extract a native library file into the target directory
val reader = NativeSecp256k1Loader::class.java.getResourceAsStream(libPath) val reader = NativeSecp256k1JvmLoader::class.java.getResourceAsStream(libPath)
if (!extractedLckFile.exists()) { if (!extractedLckFile.exists()) {
FileOutputStream(extractedLckFile).close() FileOutputStream(extractedLckFile).close()
} }
@@ -132,7 +132,7 @@ public object NativeSecp256k1Loader {
extractedLibFile.setExecutable(true) extractedLibFile.setExecutable(true)
// Check whether the contents are properly copied from the resource folder // Check whether the contents are properly copied from the resource folder
NativeSecp256k1Loader::class.java.getResourceAsStream(libPath).use { nativeIn -> NativeSecp256k1JvmLoader::class.java.getResourceAsStream(libPath).use { nativeIn ->
FileInputStream(extractedLibFile).use { extractedLibIn -> FileInputStream(extractedLibFile).use { extractedLibIn ->
if (!nativeIn.contentsEquals(extractedLibIn)) { if (!nativeIn.contentsEquals(extractedLibIn)) {
throw RuntimeException( throw RuntimeException(
@@ -196,9 +196,9 @@ public object NativeSecp256k1Loader {
} }
// Load the os-dependent library from the jar file // Load the os-dependent library from the jar file
val packagePath = NativeSecp256k1Loader::class.java.getPackage().name.replace("\\.".toRegex(), "/") val packagePath = NativeSecp256k1JvmLoader::class.java.getPackage().name.replace("\\.".toRegex(), "/")
val embeddedLibraryPath = "/$packagePath/native/${OSInfo.nativeSuffix}" val embeddedLibraryPath = "/$packagePath/native/${OSInfo.nativeSuffix}"
val hasNativeLib = NativeSecp256k1Loader::class.java.getResource("$embeddedLibraryPath/$libraryName") != null val hasNativeLib = NativeSecp256k1JvmLoader::class.java.getResource("$embeddedLibraryPath/$libraryName") != null
if (!hasNativeLib) { if (!hasNativeLib) {
error("No native library found: at $embeddedLibraryPath/$libraryName") error("No native library found: at $embeddedLibraryPath/$libraryName")
} }

View File

@@ -1,6 +1,8 @@
import org.gradle.internal.os.OperatingSystem
evaluationDependsOn(":jni:android") evaluationDependsOn(":jni:android")
val currentOs = org.gradle.internal.os.OperatingSystem.current() val currentOs = OperatingSystem.current()
val bash = if (currentOs.isWindows) "bash.exe" else "bash" val bash = if (currentOs.isWindows) "bash.exe" else "bash"
val buildSecp256k1 by tasks.creating { group = "build" } val buildSecp256k1 by tasks.creating { group = "build" }
@@ -13,7 +15,7 @@ val buildSecp256k1Host by tasks.creating(Exec::class) {
currentOs.isLinux -> "linux" currentOs.isLinux -> "linux"
currentOs.isMacOsX -> "darwin" currentOs.isMacOsX -> "darwin"
currentOs.isWindows -> "mingw" currentOs.isWindows -> "mingw"
else -> error("UnsupportedmOS $currentOs") else -> error("Unsupported OS $currentOs")
} }
inputs.files(projectDir.resolve("build.sh")) inputs.files(projectDir.resolve("build.sh"))

View File

@@ -1,7 +1,6 @@
pluginManagement { pluginManagement {
repositories { repositories {
google() google()
maven("https://dl.bintray.com/kotlin/kotlin-eap")
gradlePluginPortal() gradlePluginPortal()
jcenter() jcenter()
} }

View File

@@ -16,15 +16,20 @@
package fr.acinq.secp256k1 package fr.acinq.secp256k1
import java.lang.IllegalStateException import java.util.*
internal actual fun getSecpk256k1(): Secp256k1 { private fun tryLoad(platform: String): Secp256k1? {
try { try {
val cls = Class.forName("fr.acinq.secp256k1.jni.NativeSecp256k1Loader") val cls = Class.forName("fr.acinq.secp256k1.jni.NativeSecp256k1${platform.capitalize(Locale.ROOT)}Loader")
val load = cls.getMethod("load") val load = cls.getMethod("load")
return load.invoke(null) as Secp256k1 return load.invoke(null) as Secp256k1
} catch (ex: ClassNotFoundException) { } catch (ex: ClassNotFoundException) {
throw IllegalStateException("Could not load native Secp256k1 JNI library. Have you added the JNI dependency?", ex) return null
} }
} }
internal actual fun getSecpk256k1(): Secp256k1 =
tryLoad("android")
?: tryLoad("jvm")
?: error("Could not load native Secp256k1 JNI library. Have you added the JNI dependency?")

View File

@@ -18,10 +18,10 @@ public object Secp256k1Native : Secp256k1 {
val sig = alloc<secp256k1_ecdsa_signature>() val sig = alloc<secp256k1_ecdsa_signature>()
val nativeBytes = toNat(input) val nativeBytes = toNat(input)
val result = when (input.size) { val result = when {
64 -> secp256k1_ecdsa_signature_parse_compact(ctx, sig.ptr, nativeBytes) input.size == 64 -> secp256k1_ecdsa_signature_parse_compact(ctx, sig.ptr, nativeBytes)
in 70..73 -> secp256k1_ecdsa_signature_parse_der(ctx, sig.ptr, nativeBytes, input.size.convert()) input.size < 64 -> throw Secp256k1Exception("Unknown signature format")
else -> throw Secp256k1Exception("Unknown signature format") else -> secp256k1_ecdsa_signature_parse_der(ctx, sig.ptr, nativeBytes, input.size.convert())
} }
result.requireSuccess("cannot parse signature (size = ${input.size} sig = ${Hex.encode(input)}") result.requireSuccess("cannot parse signature (size = ${input.size} sig = ${Hex.encode(input)}")
return sig return sig

View File

@@ -8,7 +8,6 @@ kotlin {
val commonMain by sourceSets.getting { val commonMain by sourceSets.getting {
dependencies { dependencies {
implementation(kotlin("stdlib-common"))
implementation(rootProject) implementation(rootProject)
} }
} }
@@ -23,7 +22,6 @@ kotlin {
kotlinOptions.jvmTarget = "1.8" kotlinOptions.jvmTarget = "1.8"
} }
compilations["main"].dependencies { compilations["main"].dependencies {
implementation(kotlin("stdlib-jdk8"))
implementation(project(":jni:jvm:all")) implementation(project(":jni:jvm:all"))
} }
compilations["test"].dependencies { compilations["test"].dependencies {
@@ -36,7 +34,6 @@ kotlin {
kotlinOptions.jvmTarget = "1.8" kotlinOptions.jvmTarget = "1.8"
} }
sourceSets["androidMain"].dependencies { sourceSets["androidMain"].dependencies {
implementation(kotlin("stdlib-jdk8"))
implementation(project(":jni:android")) implementation(project(":jni:android"))
} }
sourceSets["androidTest"].dependencies { sourceSets["androidTest"].dependencies {

View File

@@ -1,5 +1,6 @@
package fr.acinq.secp256k1 package fr.acinq.secp256k1
import kotlin.random.Random
import kotlin.test.* import kotlin.test.*
@@ -265,4 +266,24 @@ class Secp256k1Test {
Hex.encode(der).toUpperCase(), Hex.encode(der).toUpperCase(),
) )
} }
@Test
fun testFormatConversion() {
val random = Random.Default
fun randomBytes(length: Int): ByteArray {
val buffer = ByteArray(length)
random.nextBytes(buffer)
return buffer
}
repeat(200) {
val priv = randomBytes(32)
val pub = Secp256k1.pubkeyCreate(priv)
val data = randomBytes(32)
val sig = Secp256k1.sign(data, priv)
val der = Secp256k1.compact2der(sig)
Secp256k1.verify(der, data, pub)
}
}
} }