Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5e1655ff0 | ||
|
|
8ba5d4652e | ||
|
|
e94e41b896 | ||
|
|
1a4c8b37cb | ||
|
|
f242b4ffe8 | ||
|
|
161da89ee1 | ||
|
|
3706a546a2 | ||
|
|
ffcaaf1b64 | ||
|
|
6ef94df247 | ||
|
|
5169073a92 | ||
|
|
317e086cba | ||
|
|
7c7aabba80 | ||
|
|
b6823cbda6 | ||
|
|
d50d9060c2 | ||
|
|
6fedb1577c | ||
|
|
94bb2d67cf | ||
|
|
bf05a001fe | ||
|
|
d9e5fda600 | ||
|
|
8c984678be | ||
|
|
840de25c5f | ||
|
|
cec3fb385f | ||
|
|
d59def1c79 | ||
|
|
52d73951e6 | ||
|
|
08669500b6 |
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
echo "ANDROID_HOME=$ANDROID_HOME" >> $GITHUB_ENV
|
||||
echo "ANDROID_NDK_VERSION=21.4.7075529" >> $GITHUB_ENV
|
||||
echo "ANDROID_NDK_VERSION=25.2.9519653" >> $GITHUB_ENV
|
||||
- name: Cached Android NDK
|
||||
if: matrix.os != 'windows-latest'
|
||||
uses: actions/cache@v2
|
||||
@@ -58,15 +58,11 @@ jobs:
|
||||
base-devel
|
||||
autotools
|
||||
mingw-w64-x86_64-gcc
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
- name: Setup Android
|
||||
if: matrix.os != 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
$ANDROID_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
- name: Setup Android
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
@@ -86,7 +82,7 @@ jobs:
|
||||
- name: Check Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew linuxTest
|
||||
run: ./gradlew linuxX64Test
|
||||
- name: Check iOS
|
||||
if: matrix.os == 'macOS-latest'
|
||||
shell: bash
|
||||
@@ -96,15 +92,14 @@ jobs:
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 27
|
||||
emulator-build: 7425822 # workaround to emulator bug: https://github.com/ReactiveCircus/android-emulator-runner/issues/160
|
||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
ndk: ${{ env.ANDROID_NDK_VERSION }}
|
||||
cmake: 3.10.2.4988404
|
||||
cmake: 3.22.1
|
||||
script: ./gradlew connectedCheck
|
||||
- name: Publish Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew publishLinuxPublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal
|
||||
run: ./gradlew publishLinuxX64PublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal
|
||||
- name: Publish Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
|
||||
15
.github/workflows/snapshot.yml
vendored
15
.github/workflows/snapshot.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
echo "ANDROID_HOME=$ANDROID_HOME" >> $GITHUB_ENV
|
||||
echo "ANDROID_NDK_VERSION=21.4.7075529" >> $GITHUB_ENV
|
||||
echo "ANDROID_NDK_VERSION=25.2.9519653" >> $GITHUB_ENV
|
||||
- name: Cached Android NDK
|
||||
if: matrix.os != 'windows-latest'
|
||||
uses: actions/cache@v2
|
||||
@@ -67,15 +67,11 @@ jobs:
|
||||
base-devel
|
||||
autotools
|
||||
mingw-w64-x86_64-gcc
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
- name: Setup Android
|
||||
if: matrix.os != 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
$ANDROID_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
- name: Setup Android
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
@@ -95,7 +91,7 @@ jobs:
|
||||
- name: Check Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew linuxTest
|
||||
run: ./gradlew linuxX64Test
|
||||
- name: Check iOS
|
||||
if: matrix.os == 'macOS-latest'
|
||||
shell: bash
|
||||
@@ -105,15 +101,14 @@ jobs:
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 27
|
||||
-build: 7425822 # workaround to emulator bug: https://github.com/ReactiveCircus/android-emulator-runner/issues/160
|
||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
ndk: ${{ env.ANDROID_NDK_VERSION }}
|
||||
cmake: 3.10.2.4988404
|
||||
cmake: 3.22.1
|
||||
script: ./gradlew connectedCheck
|
||||
- name: Publish Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew publishLinuxPublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal -PsnapshotNumber=${{ github.run_number }} -PgitRef=${{ github.ref }}
|
||||
run: ./gradlew publishLinuxX64PublicationToMavenLocal :jni:jvm:linux:publishJvmPublicationToMavenLocal -PsnapshotNumber=${{ github.run_number }} -PgitRef=${{ github.ref }}
|
||||
- name: Publish Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
|
||||
13
.github/workflows/test.yml
vendored
13
.github/workflows/test.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
echo "ANDROID_HOME=$ANDROID_HOME" >> $GITHUB_ENV
|
||||
echo "ANDROID_NDK_VERSION=21.4.7075529" >> $GITHUB_ENV
|
||||
echo "ANDROID_NDK_VERSION=25.2.9519653" >> $GITHUB_ENV
|
||||
- name: Cached Android NDK
|
||||
if: matrix.os != 'windows-latest'
|
||||
uses: actions/cache@v2
|
||||
@@ -73,15 +73,11 @@ jobs:
|
||||
base-devel
|
||||
autotools
|
||||
mingw-w64-x86_64-gcc
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
- name: Setup Android
|
||||
if: matrix.os != 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
$ANDROID_HOME/tools/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||
- name: Setup Android
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
@@ -101,7 +97,7 @@ jobs:
|
||||
- name: Check Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: ./gradlew linuxTest
|
||||
run: ./gradlew linuxX64Test
|
||||
- name: Check iOS
|
||||
if: matrix.os == 'macOS-latest'
|
||||
shell: bash
|
||||
@@ -111,8 +107,7 @@ jobs:
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 27
|
||||
emulator-build: 7425822 # workaround to emulator bug: https://github.com/ReactiveCircus/android-emulator-runner/issues/160
|
||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
ndk: ${{ env.ANDROID_NDK_VERSION }}
|
||||
cmake: 3.10.2.4988404
|
||||
cmake: 3.22.1
|
||||
script: ./gradlew connectedCheck
|
||||
|
||||
16
README.md
16
README.md
@@ -1,4 +1,4 @@
|
||||
[](http://kotlinlang.org)
|
||||
[](http://kotlinlang.org)
|
||||
[](https://search.maven.org/search?q=g:fr.acinq.secp256k1%20a:secp256k1-kmp*)
|
||||

|
||||
[](https://github.com/ACINQ/secp256k1-kmp/blob/master/LICENSE)
|
||||
@@ -30,19 +30,19 @@ kotlin {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-common"))
|
||||
implementation(kotlin("fr.acinq.secp256k1:secp256k1-kmp:$secp256k1_version"))
|
||||
implementation("fr.acinq.secp256k1:secp256k1-kmp:$secp256k1_version")
|
||||
}
|
||||
}
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(kotlin("fr.acinq.secp256k1:secp256k1-kmp-jni-jvm:$secp256k1_version"))
|
||||
implementation("fr.acinq.secp256k1:secp256k1-kmp-jni-jvm:$secp256k1_version")
|
||||
}
|
||||
}
|
||||
val androidMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(kotlin("fr.acinq.secp256k1:secp256k1-kmp-jni-android:$secp256k1_version"))
|
||||
implementation("fr.acinq.secp256k1:secp256k1-kmp-jni-android:$secp256k1_version")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,9 +58,9 @@ Native targets include libsecp256k1, called through KMP's c-interop, simply add
|
||||
The JVM library uses JNI bindings for libsecp256k1, which is much faster than BouncyCastle. It will extract and load native bindings for your operating system in a temporary directory.
|
||||
|
||||
JNI libraries are included for:
|
||||
- Linux 64 bits
|
||||
- Windows 64 bits
|
||||
- Macos 64 bits
|
||||
- Linux 64 bits (x86_64 and arm64)
|
||||
- Windows 64 bits (x86_64)
|
||||
- Macos 64 bits (x86_64 and arm64)
|
||||
|
||||
Along this library, you **must** specify which JNI native library to use in your dependency manager:
|
||||
|
||||
@@ -138,4 +138,4 @@ To extend this library and support methods that have been added to specific vers
|
||||
|
||||
You may also need to modify build files if you need to compile [secp256k1](https://github.com/bitcoin-core/secp256k1) with custom options
|
||||
|
||||
We use [secp256k1](https://github.com/bitcoin-core/secp256k1) through git submodules so you may also need to change what they point to
|
||||
We use [secp256k1](https://github.com/bitcoin-core/secp256k1) through git submodules so you may also need to change what they point to
|
||||
|
||||
@@ -3,8 +3,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
import org.jetbrains.dokka.Platform
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.6.21"
|
||||
id("org.jetbrains.dokka") version "1.6.21"
|
||||
kotlin("multiplatform") version "1.9.22"
|
||||
id("org.jetbrains.dokka") version "1.9.10"
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:4.2.2")
|
||||
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.6.21")
|
||||
classpath("com.android.tools.build:gradle:7.3.1")
|
||||
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.9.10")
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group = "fr.acinq.secp256k1"
|
||||
version = "0.7.0"
|
||||
version = "0.13.0"
|
||||
|
||||
repositories {
|
||||
google()
|
||||
@@ -52,20 +52,22 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
val nativeMain by sourceSets.creating { dependsOn(commonMain) }
|
||||
val nativeMain by sourceSets.creating
|
||||
|
||||
linuxX64("linux") {
|
||||
linuxX64 {
|
||||
secp256k1CInterop("host")
|
||||
compilations["main"].defaultSourceSet.dependsOn(nativeMain)
|
||||
// https://youtrack.jetbrains.com/issue/KT-39396
|
||||
compilations["main"].kotlinOptions.freeCompilerArgs += listOf("-include-binary", "$rootDir/native/build/linux/libsecp256k1.a")
|
||||
}
|
||||
|
||||
ios {
|
||||
iosX64 {
|
||||
secp256k1CInterop("ios")
|
||||
}
|
||||
|
||||
iosArm64 {
|
||||
secp256k1CInterop("ios")
|
||||
}
|
||||
|
||||
iosSimulatorArm64 {
|
||||
secp256k1CInterop("ios")
|
||||
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 {
|
||||
@@ -80,9 +82,9 @@ allprojects {
|
||||
val currentOs = OperatingSystem.current()
|
||||
val targets = when {
|
||||
currentOs.isLinux -> listOf()
|
||||
currentOs.isMacOsX -> listOf("linux")
|
||||
currentOs.isWindows -> listOf("linux")
|
||||
else -> listOf("linux")
|
||||
currentOs.isMacOsX -> listOf("linuxX64")
|
||||
currentOs.isWindows -> listOf("linuxX64")
|
||||
else -> listOf("linuxX64")
|
||||
}.mapNotNull { kotlin.targets.findByName(it) as? KotlinNativeTarget }
|
||||
|
||||
configure(targets) {
|
||||
@@ -157,6 +159,7 @@ allprojects {
|
||||
Platform.js -> "js"
|
||||
Platform.native -> "native"
|
||||
Platform.common -> "common"
|
||||
Platform.wasm -> "wasm"
|
||||
}
|
||||
displayName.set(platformName)
|
||||
|
||||
|
||||
@@ -15,12 +15,9 @@ dependencies {
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
compileSdkVersion(30)
|
||||
minSdkVersion(21)
|
||||
compileSdk = 33
|
||||
minSdk = 21
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {}
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@@ -30,10 +27,12 @@ android {
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version = "3.22.1"
|
||||
path("src/main/CMakeLists.txt")
|
||||
}
|
||||
}
|
||||
ndkVersion = "21.4.7075529"
|
||||
|
||||
ndkVersion = "25.2.9519653"
|
||||
|
||||
afterEvaluate {
|
||||
tasks.withType<com.android.build.gradle.tasks.factory.AndroidUnitTest>().all {
|
||||
@@ -43,8 +42,8 @@ android {
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
configure(listOf("Debug", "Release").map { tasks["externalNativeBuild$it"] }) {
|
||||
dependsOn(":native:buildSecp256k1Android")
|
||||
tasks.filter { it.name.startsWith("configureCMake") }.forEach {
|
||||
it.dependsOn(":native:buildSecp256k1Android")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
|
||||
project(secp256k1jni)
|
||||
|
||||
add_library( secp256k1-jni SHARED
|
||||
${CMAKE_CURRENT_LIST_DIR}/../../../c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c
|
||||
)
|
||||
|
||||
@@ -6,11 +6,10 @@ import fr.acinq.secp256k1.NativeSecp256k1
|
||||
import java.util.*
|
||||
|
||||
public object NativeSecp256k1AndroidLoader {
|
||||
|
||||
@JvmStatic
|
||||
@Synchronized
|
||||
@Throws(Exception::class)
|
||||
fun load(): Secp256k1 {
|
||||
public fun load(): Secp256k1 {
|
||||
try {
|
||||
System.loadLibrary("secp256k1-jni")
|
||||
return NativeSecp256k1
|
||||
@@ -27,5 +26,4 @@ public object NativeSecp256k1AndroidLoader {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#define SECP256K1_STATIC // needed on windows when linking to a static version of secp256k1
|
||||
#endif
|
||||
#include "include/secp256k1.h"
|
||||
#include "include/secp256k1_ecdh.h"
|
||||
#include "include/secp256k1_recovery.h"
|
||||
@@ -514,6 +517,7 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jpubkeys == NULL) return NULL;
|
||||
|
||||
count = (*penv)->GetArrayLength(penv, jpubkeys);
|
||||
CHECKRESULT(count < 1, "pubkey array cannot be empty")
|
||||
pubkeys = calloc(count, sizeof(secp256k1_pubkey*));
|
||||
|
||||
for(i = 0; i < count; i++) {
|
||||
@@ -597,6 +601,7 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
|
||||
if (jctx == 0) return NULL;
|
||||
if (jsig == NULL) return NULL;
|
||||
if (jmsg == NULL) return NULL;
|
||||
CHECKRESULT(recid < 0 || recid > 3, "invalid recovery id");
|
||||
|
||||
sigSize = (*penv)->GetArrayLength(penv, jsig);
|
||||
int sigFormat = GetSignatureFormat(sigSize);
|
||||
|
||||
@@ -12,8 +12,12 @@ dependencies {
|
||||
val copyJni by tasks.creating(Sync::class) {
|
||||
onlyIf { org.gradle.internal.os.OperatingSystem.current().isMacOsX }
|
||||
dependsOn(":jni:jvm:buildNativeHost")
|
||||
val arch = when (System.getProperty("os.arch")) {
|
||||
"aarch64" -> "aarch64"
|
||||
else -> "x86_64"
|
||||
}
|
||||
from(rootDir.resolve("jni/jvm/build/darwin/libsecp256k1-jni.dylib"))
|
||||
into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native/darwin-x86_64"))
|
||||
into(buildDir.resolve("jniResources/fr/acinq/secp256k1/jni/native/darwin-$arch"))
|
||||
}
|
||||
|
||||
(tasks["processResources"] as ProcessResources).apply {
|
||||
|
||||
@@ -19,18 +19,16 @@ else
|
||||
fi
|
||||
|
||||
TARGET=$SYS-linux-android
|
||||
TOOLTARGET=$TARGET
|
||||
if [ "$SYS" == "armv7a" ]; then
|
||||
TARGET=armv7a-linux-androideabi
|
||||
TOOLTARGET=arm-linux-androideabi
|
||||
fi
|
||||
|
||||
export CC=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/${TARGET}21-clang
|
||||
export LD=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/$TOOLTARGET-ld
|
||||
export AR=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/$TOOLTARGET-ar
|
||||
export AS=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/$TOOLTARGET-as
|
||||
export RANLIB=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/$TOOLTARGET-ranlib
|
||||
export STRIP=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/$TOOLTARGET-strip
|
||||
export LD=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/ld
|
||||
export AR=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/llvm-ar
|
||||
export AS=$CC
|
||||
export RANLIB=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/llvm-ranlib
|
||||
export STRIP=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/llvm-strip
|
||||
|
||||
cd secp256k1
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ cd secp256k1
|
||||
sh xconfigure.sh --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
|
||||
|
||||
mkdir -p ../build/ios
|
||||
cp -v _build/universal/* ../build/ios/
|
||||
cp -v _build/universal/ios/* ../build/ios/
|
||||
|
||||
mkdir -p ../build/iosSimulatorArm64
|
||||
cp -v _build/universal/iosSimulatorArm64/* ../build/iosSimulatorArm64/
|
||||
|
||||
rm -rf _build
|
||||
make clean
|
||||
|
||||
@@ -16,7 +16,7 @@ if [ "$TARGET" == "mingw" ]; then
|
||||
elif [ "$TARGET" == "linux" ]; then
|
||||
CONF_OPTS="CFLAGS=-fPIC"
|
||||
elif [ "$TARGET" == "darwin" ]; then
|
||||
CONF_OPTS="--host=x86_64-w64-darwin"
|
||||
CONF_OPTS=""
|
||||
else
|
||||
echo "Unknown TARGET=$TARGET"
|
||||
exit 1
|
||||
|
||||
Submodule native/secp256k1 updated: 44c2452fd3...1ad5185cd4
@@ -69,9 +69,22 @@ HOST_FLAGS="${ARCH_FLAGS} -mios-simulator-version-min=${MIN_IOS_VERSION} -isysro
|
||||
CHOST="x86_64-apple-darwin"
|
||||
Build "$@"
|
||||
|
||||
## Build for iphone M1/M2/Mx simulators
|
||||
SDK="iphonesimulator"
|
||||
PLATFORM="arm64-sim"
|
||||
PLATFORM_SIM_ARM=${PLATFORM}
|
||||
ARCH_FLAGS="-arch arm64"
|
||||
HOST_FLAGS="${ARCH_FLAGS} -mios-simulator-version-min=${MIN_IOS_VERSION} -isysroot $(xcrun --sdk ${SDK} --show-sdk-path)"
|
||||
CHOST="arm-apple-darwin"
|
||||
Build "$@"
|
||||
|
||||
# Create universal binary
|
||||
cd "${PLATFORMS}/${PLATFORM_ARM}/lib"
|
||||
LIB_NAME=`find . -iname *.a`
|
||||
cd -
|
||||
mkdir -p "${UNIVERSAL}" &> /dev/null
|
||||
lipo -create -output "${UNIVERSAL}/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ARM}/lib/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ISIM}/lib/${LIB_NAME}"
|
||||
mkdir -p "${UNIVERSAL}/ios" &> /dev/null
|
||||
mkdir -p "${UNIVERSAL}/iosSimulatorArm64" &> /dev/null
|
||||
lipo -create -output "${UNIVERSAL}/ios/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ARM}/lib/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ISIM}/lib/${LIB_NAME}"
|
||||
|
||||
# create a specific library for arm64 simulator: it cannot be included in the lib above which already contains an arm64 lib
|
||||
lipo -create -output "${UNIVERSAL}/iosSimulatorArm64/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_SIM_ARM}/lib/${LIB_NAME}"
|
||||
@@ -27,7 +27,9 @@ You must edit `secp256k1-kmp-staging-upload.sh` and add your sonatype credential
|
||||
## Adding custom JNI bindings
|
||||
|
||||
Github CI currently generates JNI bindings for Windows x64, Linux x64 and iOS x64. But it is possible to add custom bindings to JNI packages before
|
||||
they are published to maven central. This is how we add linux arm64 bindings:
|
||||
they are published to maven central.
|
||||
|
||||
This is how we add linux arm64 bindings:
|
||||
- compile JNI bindings for Linux Arm64 (on a Linux Arm64 machine, cross-compilation is not supported)
|
||||
- git clone --recursive https://github.com/ACINQ/secp256k1-kmp.git
|
||||
- cd secp256k1-kmp
|
||||
@@ -37,6 +39,19 @@ they are published to maven central. This is how we add linux arm64 bindings:
|
||||
- JNI library is: jni/jvm/build/linux/libsecp256k1-jni.so
|
||||
- copy libsecp256k1-jni.so to fr/acinq/secp256k1/jni/native/linux-aarch64/libsecp256k1-jni.so
|
||||
- run `secp256k1-kmp-add-linuxarm64.sh` and specify either `release` or `snapshot` and the `VERSION` environment variable, for example:
|
||||
- VERSION=0.6.4-SNAPSHOT ./secp256k1-kmp-add-linuxarm64.sh snapshot
|
||||
- VERSION=0.6.3 ./secp256k1-kmp-add-linuxarm64.sh release
|
||||
- VERSION=0.9.0-SNAPSHOT ./secp256k1-kmp-add-linuxarm64.sh snapshot
|
||||
- VERSION=0.9.0 ./secp256k1-kmp-add-linuxarm64.sh release
|
||||
|
||||
This is how we add macos arm64 (M1/M2) bindings:
|
||||
- compile JNI bindings for macos Arm64 (on a macos Arm64 machine, cross-compilation is not supported)
|
||||
- git clone --recursive https://github.com/ACINQ/secp256k1-kmp.git
|
||||
- cd secp256k1-kmp
|
||||
- TARGET=darwin ./native/build.sh
|
||||
- mkdir -p jni/jvm/build/darwin
|
||||
- TARGET=darwin ./jni/jvm/build.sh
|
||||
- JNI library is: jni/jvm/build/darwin/libsecp256k1-jni.dylib
|
||||
- copy libsecp256k1-jni.dylib to fr/acinq/secp256k1/jni/native/darwin-aarch64/libsecp256k1-jni.dylib
|
||||
- run `secp256k1-kmp-add-darwinaarch64.sh` and specify either `release` or `snapshot` and the `VERSION` environment variable, for example:
|
||||
- VERSION=0.9.0-SNAPSHOT ./secp256k1-kmp-add-darwinaarch64.sh snapshot
|
||||
- VERSION=0.9.0 ./secp256k1-kmp-add-darwinaarch64.sh release
|
||||
|
||||
|
||||
17
publishing/secp256k1-kmp-add-darwinaarch64.sh
Executable file
17
publishing/secp256k1-kmp-add-darwinaarch64.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
echo "specify either snapshot or release"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# add aarch64 (ARM64) library to the darwin jar
|
||||
if [ -e fr/acinq/secp256k1/jni/native/darwin-aarch64/libsecp256k1-jni.dylib ]
|
||||
then
|
||||
jar -uf $1/fr/acinq/secp256k1/secp256k1-kmp-jni-jvm-darwin/$VERSION/secp256k1-kmp-jni-jvm-darwin-$VERSION.jar fr || exit
|
||||
else
|
||||
libsecp256k1-jni.dylib for arch64 is missing
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -2,52 +2,64 @@
|
||||
|
||||
GROUP_ID=fr.acinq.secp256k1
|
||||
ARTIFACT_ID_BASE=secp256k1-kmp
|
||||
VERSION=0.6.3-SNAPSHOT
|
||||
|
||||
if [[ -z "${VERSION}" ]]; then
|
||||
echo "VERSION is not defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd snapshot
|
||||
pushd .
|
||||
cd fr/acinq/secp256k1/secp256k1-kmp/$VERSION
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$VERSION.jar \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$VERSION.module,$ARTIFACT_ID_BASE-$VERSION-kotlin-tooling-metadata.json \
|
||||
-Dtypes=module,json \
|
||||
-Dclassifiers=,kotlin-tooling-metadata \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$VERSION-javadoc.jar
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$VERSION.jar \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$VERSION.module,$ARTIFACT_ID_BASE-$VERSION-kotlin-tooling-metadata.json \
|
||||
-Dtypes=module,json \
|
||||
-Dclassifiers=,kotlin-tooling-metadata \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$VERSION-javadoc.jar
|
||||
popd
|
||||
pushd .
|
||||
for i in iosarm64 iosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract jni-jvm-linux jni-jvm-mingw jni-jvm jvm linux
|
||||
do
|
||||
cd fr/acinq/secp256k1/secp256k1-kmp-$i/$VERSION
|
||||
if [ $i == iosarm64 ] || [ $i == iosx64 ] || [ $i == linux ]; then
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
|
||||
-Dtypes=module,klib \
|
||||
-Dclassifiers=,cinterop-libsecp256k1 \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
elif [ $i == jni-android ]; then
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.aar \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
|
||||
-Dtypes=module \
|
||||
-Dclassifiers= \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
else
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.jar \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
|
||||
-Dtypes=module \
|
||||
-Dclassifiers= \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
fi
|
||||
popd
|
||||
pushd .
|
||||
for i in iosarm64 iossimulatorarm64 iosx64 jni-android jni-common jni-jvm-darwin jni-jvm-extract jni-jvm-linux jni-jvm-mingw jni-jvm jvm linuxx64; do
|
||||
cd fr/acinq/secp256k1/secp256k1-kmp-$i/$VERSION
|
||||
if [ $i == iosarm64 ] || [ $i == iossimulatorarm64 ] || [ $i == iosx64 ]; then
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION-metadata.jar,$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
|
||||
-Dtypes=jar,module,klib \
|
||||
-Dclassifiers=metadata,,cinterop-libsecp256k1 \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
elif [ $i == linuxx64 ]; then
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.klib \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module,$ARTIFACT_ID_BASE-$i-$VERSION-cinterop-libsecp256k1.klib \
|
||||
-Dtypes=module,klib \
|
||||
-Dclassifiers=,cinterop-libsecp256k1 \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
elif [ $i == jni-android ]; then
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.aar \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
|
||||
-Dtypes=module \
|
||||
-Dclassifiers= \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
else
|
||||
mvn deploy:deploy-file -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/content/repositories/snapshots/ \
|
||||
-DpomFile=$ARTIFACT_ID_BASE-$i-$VERSION.pom \
|
||||
-Dfile=$ARTIFACT_ID_BASE-$i-$VERSION.jar \
|
||||
-Dfiles=$ARTIFACT_ID_BASE-$i-$VERSION.module \
|
||||
-Dtypes=module \
|
||||
-Dclassifiers= \
|
||||
-Dsources=$ARTIFACT_ID_BASE-$i-$VERSION-sources.jar \
|
||||
-Djavadoc=$ARTIFACT_ID_BASE-$i-$VERSION-javadoc.jar
|
||||
fi
|
||||
popd
|
||||
pushd .
|
||||
done
|
||||
|
||||
@@ -3,6 +3,13 @@ pluginManagement {
|
||||
google()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
resolutionStrategy {
|
||||
eachPlugin {
|
||||
if (requested.id.namespace == "com.android" || requested.id.name == "kotlin-android-extensions") {
|
||||
useModule("com.android.tools.build:gradle:7.3.1")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rootProject.name = "secp256k1-kmp"
|
||||
|
||||
|
||||
@@ -3,8 +3,12 @@ package = secp256k1
|
||||
headers = secp256k1.h secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h
|
||||
headerFilter = secp256k1/** secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1.h
|
||||
|
||||
libraryPaths.linux = c/secp256k1/build/linux/
|
||||
staticLibraries.linux = libsecp256k1.a
|
||||
libraryPaths.linux = c/secp256k1/build/linux/ native/build/linux/ native/build/darwin/
|
||||
linkerOpts.linux = -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/local/lib
|
||||
|
||||
libraryPaths.ios = c/secp256k1/build/ios/ /usr/local/lib
|
||||
staticLibraries.ios = libsecp256k1.a
|
||||
libraryPaths.ios_x64 = c/secp256k1/build/ios/ /usr/local/lib native/build/ios/
|
||||
libraryPaths.ios_arm64 = c/secp256k1/build/ios/ /usr/local/lib native/build/ios/
|
||||
libraryPaths.ios_simulator_arm64 = c/secp256k1/build/ios/ /usr/local/lib native/build/iosSimulatorArm64/
|
||||
linkerOpts.ios = -framework Security -framework Foundation
|
||||
|
||||
@@ -4,7 +4,7 @@ import kotlinx.cinterop.*
|
||||
import platform.posix.size_tVar
|
||||
import secp256k1.*
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
@OptIn(ExperimentalUnsignedTypes::class, ExperimentalForeignApi::class)
|
||||
public object Secp256k1Native : Secp256k1 {
|
||||
|
||||
private val ctx: CPointer<secp256k1_context> by lazy {
|
||||
@@ -125,6 +125,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
|
||||
public override fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray {
|
||||
require(privkey.size == 32)
|
||||
require(tweak.size == 32)
|
||||
memScoped {
|
||||
val added = privkey.copyOf()
|
||||
val natAdd = toNat(added)
|
||||
@@ -136,6 +137,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
|
||||
public override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray {
|
||||
require(privkey.size == 32)
|
||||
require(tweak.size == 32)
|
||||
memScoped {
|
||||
val multiplied = privkey.copyOf()
|
||||
val natMul = toNat(multiplied)
|
||||
@@ -156,6 +158,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
|
||||
public override fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray {
|
||||
require(pubkey.size == 33 || pubkey.size == 65)
|
||||
require(tweak.size == 32)
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
val nTweak = toNat(tweak)
|
||||
@@ -166,6 +169,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
|
||||
public override fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray {
|
||||
require(pubkey.size == 33 || pubkey.size == 65)
|
||||
require(tweak.size == 32)
|
||||
memScoped {
|
||||
val nPubkey = allocPublicKey(pubkey)
|
||||
val nTweak = toNat(tweak)
|
||||
@@ -175,6 +179,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
}
|
||||
|
||||
public override fun pubKeyCombine(pubkeys: Array<ByteArray>): ByteArray {
|
||||
require(pubkeys.isNotEmpty())
|
||||
pubkeys.forEach { require(it.size == 33 || it.size == 65) }
|
||||
memScoped {
|
||||
val nPubkeys = pubkeys.map { allocPublicKey(it).ptr }
|
||||
@@ -199,6 +204,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
public override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray {
|
||||
require(sig.size == 64)
|
||||
require(message.size == 32)
|
||||
require(recid in 0..3)
|
||||
memScoped {
|
||||
val nSig = toNat(sig)
|
||||
val rSig = alloc<secp256k1_ecdsa_recoverable_signature>()
|
||||
@@ -232,7 +238,7 @@ public object Secp256k1Native : Secp256k1 {
|
||||
secp256k1_xonly_pubkey_parse(ctx, pubkey.ptr, nPub).requireSuccess("secp256k1_xonly_pubkey_parse() failed")
|
||||
val nData = toNat(data)
|
||||
val nSig = toNat(signature)
|
||||
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32, pubkey.ptr) == 1
|
||||
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32u, pubkey.ptr) == 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
if (System.getProperty("includeAndroid")?.toBoolean() == true) {
|
||||
@@ -36,14 +35,14 @@ kotlin {
|
||||
}
|
||||
|
||||
if (includeAndroid) {
|
||||
android {
|
||||
androidTarget {
|
||||
compilations.all {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
sourceSets["androidMain"].dependencies {
|
||||
implementation(project(":jni:android"))
|
||||
}
|
||||
sourceSets["androidTest"].dependencies {
|
||||
sourceSets["androidUnitTest"].dependencies {
|
||||
implementation(kotlin("test-junit"))
|
||||
implementation("androidx.test.ext:junit:1.1.2")
|
||||
implementation("androidx.test.espresso:espresso-core:3.3.0")
|
||||
@@ -51,17 +50,18 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
linuxX64("linux")
|
||||
|
||||
ios()
|
||||
linuxX64()
|
||||
iosX64()
|
||||
iosArm64()
|
||||
iosSimulatorArm64()
|
||||
}
|
||||
|
||||
val includeAndroid = System.getProperty("includeAndroid")?.toBoolean() ?: true
|
||||
if (includeAndroid) {
|
||||
extensions.configure<com.android.build.gradle.LibraryExtension>("android") {
|
||||
defaultConfig {
|
||||
compileSdkVersion(30)
|
||||
minSdkVersion(21)
|
||||
compileSdk = 30
|
||||
minSdk = 21
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package fr.acinq.secp256k1
|
||||
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
||||
class AndroidTest {}
|
||||
@@ -352,6 +352,38 @@ class Secp256k1Test {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInvalidArguments() {
|
||||
assertFails {
|
||||
Secp256k1.pubkeyCreate(ByteArray(32))
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.pubkeyCreate(Hex.decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.pubkeyParse(ByteArray(33))
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.pubkeyParse(Hex.decode("03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.pubKeyCombine(arrayOf())
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.pubKeyCombine(arrayOf(ByteArray(0)))
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.signSchnorr(ByteArray(0), Hex.decode("0101010101010101010101010101010101010101010101010101010101010101"), null)
|
||||
}
|
||||
assertFails {
|
||||
Secp256k1.ecdsaRecover(
|
||||
Hex.decode("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101"),
|
||||
Hex.decode("0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
-1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fuzzEcdsaSignVerify() {
|
||||
val random = Random.Default
|
||||
|
||||
Reference in New Issue
Block a user