feat: complete all 12 binding APIs + 9 READMEs + fix package naming
Bindings: - Java: +22 JNI functions + 3 helper classes (RecoverableSignature, WifDecoded, TaprootOutputKeyResult) - Swift: +20 functions (DER, recovery, ECDH, tagged_hash, BIP-32, taproot) - React Native: +15 functions - Python: +3 functions (ctx_clone, last_error, last_error_msg) - Rust: +2 functions (last_error, last_error_msg) - Dart: +1 function (ctx_clone) Documentation: - 9 new binding READMEs: c_api, dart, go, java, php, python, ruby, rust, swift - 3 existing READMEs fixed: nodejs, csharp, react-native (CT/fast architecture note) - Fix incorrect package names across all docs: libsecp256k1-fast* -> libufsecp* (apt, rpm, arch, pkg-config, CMake) secp256k1-fast-cpu -> fastsecp256k1 (linker flags, CMake targets) - Fix INDUSTRIAL_ROADMAP_WORKING.md link -> ROADMAP.md in README - Rename RPM spec: libsecp256k1-fast.spec -> libufsecp.spec - Fix debian/control, debian/changelog, arch/PKGBUILD package names - Fix secp256k1-fast.pc.in linker flag - Fix .github/workflows/packaging.yml comment Selftest: - Add selftest report structs (selftest.hpp) - Refactor tally() in selftest.cpp
This commit is contained in:
parent
ab13fddb7a
commit
03c1263cdb
2
.github/workflows/packaging.yml
vendored
2
.github/workflows/packaging.yml
vendored
@ -7,7 +7,7 @@
|
||||
#
|
||||
# curl -fsSL https://shrec.github.io/UltrafastSecp256k1/KEY.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/ultrafastsecp256k1.gpg
|
||||
# echo "deb [signed-by=/etc/apt/keyrings/ultrafastsecp256k1.gpg] https://shrec.github.io/UltrafastSecp256k1/apt stable main" | sudo tee /etc/apt/sources.list.d/ultrafastsecp256k1.list
|
||||
# sudo apt update && sudo apt install libsecp256k1-fast-dev
|
||||
# sudo apt update && sudo apt install libufsecp-dev
|
||||
#
|
||||
# ============================================================================
|
||||
|
||||
|
||||
24
README.md
24
README.md
@ -122,7 +122,7 @@ Get a working selftest in under a minute:
|
||||
|
||||
**Option A — Linux (apt)**
|
||||
```bash
|
||||
sudo apt install libsecp256k1-fast3
|
||||
sudo apt install libufsecp3
|
||||
ufsecp_selftest # Expected: "OK (version 3.x, backend CPU)"
|
||||
```
|
||||
|
||||
@ -151,7 +151,7 @@ cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release && cmake --build build -
|
||||
|
||||
| Target | Backend | Install / Entry Point | Status |
|
||||
|--------|---------|----------------------|--------|
|
||||
| **Linux x64** | CPU | `apt install libsecp256k1-fast3` | ✅ Stable |
|
||||
| **Linux x64** | CPU | `apt install libufsecp3` | ✅ Stable |
|
||||
| **Windows x64** | CPU | NuGet `UltrafastSecp256k1` / [Release .zip](https://github.com/shrec/UltrafastSecp256k1/releases) | ✅ Stable |
|
||||
| **macOS (x64/ARM64)** | CPU + Metal | `brew install ufsecp` / build from source | ✅ Stable |
|
||||
| **Android ARM64** | CPU | `implementation 'io.github.shrec:ufsecp'` (Maven) | ✅ Stable |
|
||||
@ -179,10 +179,10 @@ echo "deb [signed-by=/etc/apt/keyrings/ultrafastsecp256k1.gpg] https://shrec.git
|
||||
sudo apt update
|
||||
|
||||
# Install (runtime only)
|
||||
sudo apt install libsecp256k1-fast3
|
||||
sudo apt install libufsecp3
|
||||
|
||||
# Install (development — headers, static lib, cmake/pkgconfig)
|
||||
sudo apt install libsecp256k1-fast-dev
|
||||
sudo apt install libufsecp-dev
|
||||
```
|
||||
|
||||
### Linux (RPM — Fedora / RHEL)
|
||||
@ -197,11 +197,11 @@ sudo dnf install ./UltrafastSecp256k1-*.rpm
|
||||
|
||||
```bash
|
||||
# Using yay
|
||||
yay -S libsecp256k1-fast
|
||||
yay -S libufsecp
|
||||
|
||||
# Or manually
|
||||
git clone https://aur.archlinux.org/libsecp256k1-fast.git
|
||||
cd libsecp256k1-fast && makepkg -si
|
||||
git clone https://aur.archlinux.org/libufsecp.git
|
||||
cd libufsecp && makepkg -si
|
||||
```
|
||||
|
||||
### From source (any platform)
|
||||
@ -221,14 +221,14 @@ sudo ldconfig
|
||||
### Use in your CMake project
|
||||
|
||||
```cmake
|
||||
find_package(secp256k1-fast 3 REQUIRED COMPONENTS CPU)
|
||||
target_link_libraries(myapp PRIVATE secp256k1::fastsecp256k1)
|
||||
find_package(ufsecp 3 REQUIRED)
|
||||
target_link_libraries(myapp PRIVATE ufsecp::ufsecp)
|
||||
```
|
||||
|
||||
### Use with pkg-config
|
||||
|
||||
```bash
|
||||
g++ myapp.cpp $(pkg-config --cflags --libs secp256k1-fast) -o myapp
|
||||
g++ myapp.cpp $(pkg-config --cflags --libs ufsecp) -o myapp
|
||||
```
|
||||
|
||||
---
|
||||
@ -348,7 +348,7 @@ See [THREAT_MODEL.md](THREAT_MODEL.md) for a full layer-by-layer risk assessment
|
||||
| **No secret-dependent branches** | All `ct::` functions | ✅ Enforced by design, verified via Clang-Tidy checks |
|
||||
| **No secret-dependent memory access** | All `ct::` table lookups use constant-index cmov | ✅ |
|
||||
| **ASan + UBSan CI** | Every push — catches undefined behavior in CT paths | ✅ CI |
|
||||
| **Timing tests (dudect)** | CPU field/scalar ops | 🔜 Planned (see [roadmap](INDUSTRIAL_ROADMAP_WORKING.md)) |
|
||||
| **Timing tests (dudect)** | CPU field/scalar ops | 🔜 Planned (see [roadmap](ROADMAP.md)) |
|
||||
| **Formal CT verification** | Fiat-Crypto style | 🔜 Planned |
|
||||
|
||||
**Assumptions:** CT guarantees depend on compiler not introducing secret-dependent branches during optimization. Builds use `-O2` with Clang; MSVC may require additional flags. Micro-architectural side channels (Spectre, power analysis) are outside current scope — see [THREAT_MODEL.md](THREAT_MODEL.md).
|
||||
@ -654,7 +654,7 @@ int main() {
|
||||
```
|
||||
|
||||
```bash
|
||||
g++ -std=c++20 example.cpp -lsecp256k1-fast-cpu -o example && ./example
|
||||
g++ -std=c++20 example.cpp -lufsecp -o example && ./example
|
||||
```
|
||||
|
||||
### GPU Batch Multiplication
|
||||
|
||||
53
bindings/c_api/README.md
Normal file
53
bindings/c_api/README.md
Normal file
@ -0,0 +1,53 @@
|
||||
# ultrafast_secp256k1 — C API
|
||||
|
||||
Standalone C header-only API for [UltrafastSecp256k1](https://github.com/shrec/UltrafastSecp256k1) — high-performance secp256k1 elliptic curve cryptography.
|
||||
|
||||
This is a **stateless** API with `secp256k1_*` naming (no context object). It differs from the main `ufsecp_*` context-based API.
|
||||
|
||||
## Features
|
||||
|
||||
- **ECDSA** — sign, verify, DER serialization (RFC 6979)
|
||||
- **Schnorr** — BIP-340 sign/verify
|
||||
- **ECDH** — shared secret
|
||||
- **BIP-32** — HD key derivation
|
||||
- **Taproot** — output key tweaking, commitment verification (BIP-341)
|
||||
- **Addresses** — P2PKH, P2WPKH, P2TR
|
||||
- **WIF** — encode/decode
|
||||
- **Hashing** — SHA-256, HASH160
|
||||
|
||||
## Quick Start
|
||||
|
||||
```c
|
||||
#include "ultrafast_secp256k1.h"
|
||||
|
||||
secp256k1_init();
|
||||
|
||||
uint8_t privkey[32] = {0};
|
||||
privkey[31] = 1;
|
||||
|
||||
uint8_t pubkey[33];
|
||||
secp256k1_ec_pubkey_create(privkey, pubkey);
|
||||
|
||||
uint8_t msg[32] = {0};
|
||||
uint8_t sig[64];
|
||||
secp256k1_ecdsa_sign(msg, privkey, sig);
|
||||
|
||||
int ok = secp256k1_ecdsa_verify(msg, sig, pubkey);
|
||||
```
|
||||
|
||||
## API Differences
|
||||
|
||||
| Feature | `ufsecp_*` (main) | `secp256k1_*` (this) |
|
||||
|---------|-------------------|----------------------|
|
||||
| Context | Required | None (global init) |
|
||||
| Init | `ufsecp_ctx_create()` | `secp256k1_init()` |
|
||||
| Naming | `ufsecp_ecdsa_sign` | `secp256k1_ecdsa_sign` |
|
||||
| Thread safety | Per-context | Global state |
|
||||
|
||||
## Architecture Note
|
||||
|
||||
Both APIs use the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@ -117,9 +117,9 @@ byte[] tagged = Ufsecp.TaggedHash("BIP0340/aux", data); // BIP-340 tagged hash
|
||||
|
||||
Keys, ECDSA (sign/verify/recover/DER), Schnorr BIP-340, ECDH (compressed/xonly/raw), SHA-256, HASH160, Tagged Hash, BIP-32 HD, Taproot (BIP-341), Bitcoin Addresses (P2PKH/P2WPKH/P2TR), WIF, Key Tweaking.
|
||||
|
||||
## Constant-Time Architecture
|
||||
## Architecture Note
|
||||
|
||||
All secret-key operations (signing, ECDH, key derivation) automatically use the constant-time layer — no flags or opt-in required.
|
||||
The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
|
||||
54
bindings/dart/README.md
Normal file
54
bindings/dart/README.md
Normal file
@ -0,0 +1,54 @@
|
||||
# ufsecp — Dart
|
||||
|
||||
Dart FFI binding for [UltrafastSecp256k1](https://github.com/shrec/UltrafastSecp256k1) — high-performance secp256k1 elliptic curve cryptography.
|
||||
|
||||
## Features
|
||||
|
||||
- **ECDSA** — sign, verify, recover, DER serialization (RFC 6979)
|
||||
- **Schnorr** — BIP-340 sign/verify
|
||||
- **ECDH** — compressed, x-only, raw shared secret
|
||||
- **BIP-32** — HD key derivation (master/derive/path/privkey/pubkey)
|
||||
- **Taproot** — output key tweaking, verification (BIP-341)
|
||||
- **Addresses** — P2PKH, P2WPKH, P2TR
|
||||
- **WIF** — encode/decode
|
||||
- **Hashing** — SHA-256 (hardware-accelerated), HASH160, tagged hash
|
||||
- **Key tweaking** — negate, add, multiply
|
||||
|
||||
## Quick Start
|
||||
|
||||
```dart
|
||||
import 'package:ufsecp/ufsecp.dart';
|
||||
|
||||
final ctx = UfsecpContext();
|
||||
|
||||
final privkey = Uint8List(32)..[31] = 1;
|
||||
final pubkey = ctx.pubkeyCreate(privkey);
|
||||
final msgHash = UfsecpContext.sha256(utf8.encode('hello'));
|
||||
final sig = ctx.ecdsaSign(msgHash, privkey);
|
||||
final valid = ctx.ecdsaVerify(msgHash, sig, pubkey);
|
||||
|
||||
ctx.destroy();
|
||||
```
|
||||
|
||||
## ECDSA Recovery
|
||||
|
||||
```dart
|
||||
final rs = ctx.ecdsaSignRecoverable(msgHash, privkey);
|
||||
final recovered = ctx.ecdsaRecover(msgHash, rs.signature, rs.recoveryId);
|
||||
```
|
||||
|
||||
## Taproot (BIP-341)
|
||||
|
||||
```dart
|
||||
final tok = ctx.taprootOutputKey(xonlyPub);
|
||||
final tweaked = ctx.taprootTweakSeckey(privkey);
|
||||
final tapValid = ctx.taprootVerify(tok.outputKeyX, tok.parity, xonlyPub);
|
||||
```
|
||||
|
||||
## Architecture Note
|
||||
|
||||
The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@ -81,6 +81,8 @@ typedef _CtxCreateC = ffi.Int32 Function(ffi.Pointer<ffi.Pointer<ffi.Void>>);
|
||||
typedef _CtxCreateDart = int Function(ffi.Pointer<ffi.Pointer<ffi.Void>>);
|
||||
typedef _CtxDestroyC = ffi.Void Function(ffi.Pointer<ffi.Void>);
|
||||
typedef _CtxDestroyDart = void Function(ffi.Pointer<ffi.Void>);
|
||||
typedef _CtxCloneC = ffi.Int32 Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Pointer<ffi.Void>>);
|
||||
typedef _CtxCloneDart = int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Pointer<ffi.Void>>);
|
||||
|
||||
// Version
|
||||
typedef _VersionC = ffi.Uint32 Function();
|
||||
@ -183,6 +185,7 @@ class UfsecpContext {
|
||||
|
||||
// ── cached lookups ──
|
||||
late final _CtxDestroyDart _ctxDestroy;
|
||||
late final _CtxCloneDart _ctxClone;
|
||||
late final _LastErrorDart _lastError;
|
||||
late final _LastErrorMsgDart _lastErrorMsg;
|
||||
late final _VersionDart _version;
|
||||
@ -258,6 +261,23 @@ class UfsecpContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Deep-copy this context into a new independent context.
|
||||
UfsecpContext._fromPointer(this._lib, this._ctx) {
|
||||
_bindAll();
|
||||
}
|
||||
|
||||
UfsecpContext cloneCtx() {
|
||||
_ensureAlive();
|
||||
final pp = calloc<ffi.Pointer<ffi.Void>>();
|
||||
try {
|
||||
final rc = _ctxClone(_ctx, pp);
|
||||
if (rc != 0) throw UfsecpException('ctx_clone', UfsecpError.fromCode(rc));
|
||||
return UfsecpContext._fromPointer(_lib, pp.value);
|
||||
} finally {
|
||||
calloc.free(pp);
|
||||
}
|
||||
}
|
||||
|
||||
void _ensureAlive() {
|
||||
if (_destroyed) throw StateError('UfsecpContext already destroyed');
|
||||
}
|
||||
@ -854,6 +874,7 @@ class UfsecpContext {
|
||||
|
||||
void _bindAll() {
|
||||
_ctxDestroy = _lib.lookupFunction<_CtxDestroyC, _CtxDestroyDart>('ufsecp_ctx_destroy');
|
||||
_ctxClone = _lib.lookupFunction<_CtxCloneC, _CtxCloneDart>('ufsecp_ctx_clone');
|
||||
_lastError = _lib.lookupFunction<_LastErrorC, _LastErrorDart>('ufsecp_last_error');
|
||||
_lastErrorMsg = _lib.lookupFunction<_LastErrorMsgC, _LastErrorMsgDart>('ufsecp_last_error_msg');
|
||||
_version = _lib.lookupFunction<_VersionC, _VersionDart>('ufsecp_version');
|
||||
|
||||
56
bindings/go/README.md
Normal file
56
bindings/go/README.md
Normal file
@ -0,0 +1,56 @@
|
||||
# ufsecp — Go
|
||||
|
||||
Go (CGo) binding for [UltrafastSecp256k1](https://github.com/shrec/UltrafastSecp256k1) — high-performance secp256k1 elliptic curve cryptography.
|
||||
|
||||
## Features
|
||||
|
||||
- **ECDSA** — sign, verify, recover, DER serialization (RFC 6979)
|
||||
- **Schnorr** — BIP-340 sign/verify
|
||||
- **ECDH** — compressed, x-only, raw shared secret
|
||||
- **BIP-32** — HD key derivation (master/derive/path/privkey/pubkey)
|
||||
- **Taproot** — output key tweaking, verification (BIP-341)
|
||||
- **Addresses** — P2PKH, P2WPKH, P2TR
|
||||
- **WIF** — encode/decode
|
||||
- **Hashing** — SHA-256 (hardware-accelerated), HASH160, tagged hash
|
||||
- **Key tweaking** — negate, add, multiply
|
||||
|
||||
## Quick Start
|
||||
|
||||
```go
|
||||
import "github.com/shrec/UltrafastSecp256k1/bindings/go"
|
||||
|
||||
ctx, err := ufsecp.NewContext()
|
||||
if err != nil { panic(err) }
|
||||
defer ctx.Destroy()
|
||||
|
||||
privkey := make([]byte, 32)
|
||||
privkey[31] = 1
|
||||
|
||||
pubkey, err := ctx.PubkeyCreate(privkey)
|
||||
msgHash, _ := ufsecp.Sha256([]byte("hello"))
|
||||
sig, err := ctx.EcdsaSign(msgHash, privkey)
|
||||
valid, err := ctx.EcdsaVerify(msgHash, sig, pubkey)
|
||||
```
|
||||
|
||||
## ECDSA Recovery
|
||||
|
||||
```go
|
||||
sig, recid, err := ctx.EcdsaSignRecoverable(msgHash, privkey)
|
||||
recovered, err := ctx.EcdsaRecover(msgHash, sig, recid)
|
||||
```
|
||||
|
||||
## Taproot (BIP-341)
|
||||
|
||||
```go
|
||||
outputKey, parity, err := ctx.TaprootOutputKey(xonlyPub, nil)
|
||||
tweaked, err := ctx.TaprootTweakSeckey(privkey, nil)
|
||||
valid, err := ctx.TaprootVerify(outputKey, parity, xonlyPub, nil)
|
||||
```
|
||||
|
||||
## Architecture Note
|
||||
|
||||
The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
71
bindings/java/README.md
Normal file
71
bindings/java/README.md
Normal file
@ -0,0 +1,71 @@
|
||||
# ufsecp — Java
|
||||
|
||||
Java binding for [UltrafastSecp256k1](https://github.com/shrec/UltrafastSecp256k1) — high-performance secp256k1 elliptic curve cryptography via JNI.
|
||||
|
||||
## Features
|
||||
|
||||
- **ECDSA** — sign, verify, recover, DER serialization (RFC 6979)
|
||||
- **Schnorr** — BIP-340 sign/verify
|
||||
- **ECDH** — compressed, x-only, raw shared secret
|
||||
- **BIP-32** — HD key derivation (master/derive/path/privkey/pubkey)
|
||||
- **Taproot** — output key tweaking, verification (BIP-341)
|
||||
- **Addresses** — P2PKH, P2WPKH, P2TR
|
||||
- **WIF** — encode/decode
|
||||
- **Hashing** — SHA-256 (hardware-accelerated), HASH160, tagged hash
|
||||
- **Key tweaking** — negate, add, multiply
|
||||
|
||||
## Quick Start
|
||||
|
||||
```java
|
||||
import com.ultrafast.ufsecp.Ufsecp;
|
||||
|
||||
try (Ufsecp ctx = Ufsecp.create()) {
|
||||
byte[] privkey = new byte[32];
|
||||
privkey[31] = 1;
|
||||
|
||||
byte[] pubkey = ctx.pubkeyCreate(privkey);
|
||||
byte[] msgHash = Ufsecp.sha256("hello".getBytes());
|
||||
byte[] sig = ctx.ecdsaSign(msgHash, privkey);
|
||||
boolean valid = ctx.ecdsaVerify(msgHash, sig, pubkey);
|
||||
}
|
||||
```
|
||||
|
||||
## ECDSA Recovery
|
||||
|
||||
```java
|
||||
RecoverableSignature rs = ctx.ecdsaSignRecoverable(msgHash, privkey);
|
||||
byte[] recovered = ctx.ecdsaRecover(msgHash, rs.getSignature(), rs.getRecid());
|
||||
```
|
||||
|
||||
## Schnorr (BIP-340)
|
||||
|
||||
```java
|
||||
byte[] xonlyPub = ctx.pubkeyXonly(privkey);
|
||||
byte[] schnorrSig = ctx.schnorrSign(msgHash, privkey, auxRand);
|
||||
boolean ok = ctx.schnorrVerify(msgHash, schnorrSig, xonlyPub);
|
||||
```
|
||||
|
||||
## BIP-32 HD Derivation
|
||||
|
||||
```java
|
||||
byte[] master = ctx.bip32Master(seed);
|
||||
byte[] child = ctx.bip32DerivePath(master, "m/44'/0'/0'/0/0");
|
||||
byte[] childPriv = ctx.bip32Privkey(child);
|
||||
byte[] childPub = ctx.bip32Pubkey(child);
|
||||
```
|
||||
|
||||
## Taproot (BIP-341)
|
||||
|
||||
```java
|
||||
TaprootOutputKeyResult tok = ctx.taprootOutputKey(xonlyPub, null);
|
||||
byte[] tweakedPriv = ctx.taprootTweakSeckey(privkey, null);
|
||||
boolean tapValid = ctx.taprootVerify(tok.getOutputKey(), tok.getParity(), xonlyPub, null);
|
||||
```
|
||||
|
||||
## Architecture Note
|
||||
|
||||
The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@ -280,7 +280,301 @@ JNIEXPORT jint JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeVersion(JNIEnv *en
|
||||
return (jint)ufsecp_version();
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeAbiVersion(JNIEnv *env, jclass clz) {
|
||||
(void)env; (void)clz;
|
||||
return (jint)ufsecp_abi_version();
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeVersionString(JNIEnv *env, jclass clz) {
|
||||
(void)clz;
|
||||
return (*env)->NewStringUTF(env, ufsecp_version_string());
|
||||
}
|
||||
|
||||
/* ── Context extras ──────────────────────────────────────────────────── */
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeClone(JNIEnv *env, jclass clz, jlong ptr) {
|
||||
(void)clz;
|
||||
ufsecp_ctx *clone = NULL;
|
||||
int rc = ufsecp_ctx_clone((const ufsecp_ctx*)(uintptr_t)ptr, &clone);
|
||||
if (throw_on_err(env, rc, "ctx_clone")) return 0;
|
||||
return (jlong)(uintptr_t)clone;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeLastError(JNIEnv *env, jclass clz, jlong ptr) {
|
||||
(void)env; (void)clz;
|
||||
return (jint)ufsecp_last_error((const ufsecp_ctx*)(uintptr_t)ptr);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeLastErrorMsg(JNIEnv *env, jclass clz, jlong ptr) {
|
||||
(void)clz;
|
||||
return (*env)->NewStringUTF(env, ufsecp_last_error_msg((const ufsecp_ctx*)(uintptr_t)ptr));
|
||||
}
|
||||
|
||||
/* ── Key ops extras ──────────────────────────────────────────────────── */
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeSeckeyNegate(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray privkey) {
|
||||
(void)clz;
|
||||
jbyte *pk = pin(env, privkey);
|
||||
uint8_t buf[32];
|
||||
memcpy(buf, pk, 32);
|
||||
unpin(env, privkey, pk);
|
||||
int rc = ufsecp_seckey_negate((ufsecp_ctx*)(uintptr_t)ctx, buf);
|
||||
if (throw_on_err(env, rc, "seckey_negate")) return NULL;
|
||||
return mk(env, buf, 32);
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeSeckeyTweakAdd(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray privkey, jbyteArray tweak) {
|
||||
(void)clz;
|
||||
jbyte *pk = pin(env, privkey);
|
||||
jbyte *tw = pin(env, tweak);
|
||||
uint8_t buf[32];
|
||||
memcpy(buf, pk, 32);
|
||||
int rc = ufsecp_seckey_tweak_add((ufsecp_ctx*)(uintptr_t)ctx, buf, (const uint8_t*)tw);
|
||||
unpin(env, tweak, tw);
|
||||
unpin(env, privkey, pk);
|
||||
if (throw_on_err(env, rc, "seckey_tweak_add")) return NULL;
|
||||
return mk(env, buf, 32);
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeSeckeyTweakMul(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray privkey, jbyteArray tweak) {
|
||||
(void)clz;
|
||||
jbyte *pk = pin(env, privkey);
|
||||
jbyte *tw = pin(env, tweak);
|
||||
uint8_t buf[32];
|
||||
memcpy(buf, pk, 32);
|
||||
int rc = ufsecp_seckey_tweak_mul((ufsecp_ctx*)(uintptr_t)ctx, buf, (const uint8_t*)tw);
|
||||
unpin(env, tweak, tw);
|
||||
unpin(env, privkey, pk);
|
||||
if (throw_on_err(env, rc, "seckey_tweak_mul")) return NULL;
|
||||
return mk(env, buf, 32);
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativePubkeyParse(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray pubkey) {
|
||||
(void)clz;
|
||||
jbyte *pk = pin(env, pubkey);
|
||||
jsize len = (*env)->GetArrayLength(env, pubkey);
|
||||
uint8_t out[33];
|
||||
int rc = ufsecp_pubkey_parse((ufsecp_ctx*)(uintptr_t)ctx, (const uint8_t*)pk, (size_t)len, out);
|
||||
unpin(env, pubkey, pk);
|
||||
if (throw_on_err(env, rc, "pubkey_parse")) return NULL;
|
||||
return mk(env, out, 33);
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativePubkeyXonly(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray privkey) {
|
||||
(void)clz;
|
||||
jbyte *pk = pin(env, privkey);
|
||||
uint8_t out[32];
|
||||
int rc = ufsecp_pubkey_xonly((ufsecp_ctx*)(uintptr_t)ctx, (const uint8_t*)pk, out);
|
||||
unpin(env, privkey, pk);
|
||||
if (throw_on_err(env, rc, "pubkey_xonly")) return NULL;
|
||||
return mk(env, out, 32);
|
||||
}
|
||||
|
||||
/* ── ECDSA extras ────────────────────────────────────────────────────── */
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeEcdsaSigToDer(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray sig) {
|
||||
(void)clz;
|
||||
jbyte *s = pin(env, sig);
|
||||
uint8_t der[72];
|
||||
size_t dlen = 72;
|
||||
int rc = ufsecp_ecdsa_sig_to_der((ufsecp_ctx*)(uintptr_t)ctx, (const uint8_t*)s, der, &dlen);
|
||||
unpin(env, sig, s);
|
||||
if (throw_on_err(env, rc, "ecdsa_sig_to_der")) return NULL;
|
||||
return mk(env, der, (int)dlen);
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeEcdsaSigFromDer(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray der) {
|
||||
(void)clz;
|
||||
jbyte *d = pin(env, der);
|
||||
jsize dlen = (*env)->GetArrayLength(env, der);
|
||||
uint8_t sig[64];
|
||||
int rc = ufsecp_ecdsa_sig_from_der((ufsecp_ctx*)(uintptr_t)ctx, (const uint8_t*)d, (size_t)dlen, sig);
|
||||
unpin(env, der, d);
|
||||
if (throw_on_err(env, rc, "ecdsa_sig_from_der")) return NULL;
|
||||
return mk(env, sig, 64);
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeEcdsaSignRecoverable(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray msgHash, jbyteArray privkey) {
|
||||
(void)clz;
|
||||
jbyte *msg = pin(env, msgHash);
|
||||
jbyte *pk = pin(env, privkey);
|
||||
uint8_t sig[64];
|
||||
int recid = 0;
|
||||
int rc = ufsecp_ecdsa_sign_recoverable((ufsecp_ctx*)(uintptr_t)ctx,
|
||||
(const uint8_t*)msg, (const uint8_t*)pk, sig, &recid);
|
||||
unpin(env, privkey, pk);
|
||||
unpin(env, msgHash, msg);
|
||||
if (throw_on_err(env, rc, "ecdsa_sign_recoverable")) return NULL;
|
||||
|
||||
jbyteArray sigArr = mk(env, sig, 64);
|
||||
jclass cls = (*env)->FindClass(env, "com/ultrafast/ufsecp/RecoverableSignature");
|
||||
jmethodID ctor = (*env)->GetMethodID(env, cls, "<init>", "([BI)V");
|
||||
return (*env)->NewObject(env, cls, ctor, sigArr, (jint)recid);
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeEcdsaRecover(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray msgHash, jbyteArray sig, jint recid) {
|
||||
(void)clz;
|
||||
jbyte *msg = pin(env, msgHash);
|
||||
jbyte *s = pin(env, sig);
|
||||
uint8_t pub[33];
|
||||
int rc = ufsecp_ecdsa_recover((ufsecp_ctx*)(uintptr_t)ctx,
|
||||
(const uint8_t*)msg, (const uint8_t*)s, (int)recid, pub);
|
||||
unpin(env, sig, s);
|
||||
unpin(env, msgHash, msg);
|
||||
if (throw_on_err(env, rc, "ecdsa_recover")) return NULL;
|
||||
return mk(env, pub, 33);
|
||||
}
|
||||
|
||||
/* ── ECDH extras ─────────────────────────────────────────────────────── */
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeEcdhXonly(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray privkey, jbyteArray pubkey) {
|
||||
(void)clz;
|
||||
jbyte *pk = pin(env, privkey);
|
||||
jbyte *pub = pin(env, pubkey);
|
||||
uint8_t out[32];
|
||||
int rc = ufsecp_ecdh_xonly((ufsecp_ctx*)(uintptr_t)ctx, (const uint8_t*)pk, (const uint8_t*)pub, out);
|
||||
unpin(env, pubkey, pub);
|
||||
unpin(env, privkey, pk);
|
||||
if (throw_on_err(env, rc, "ecdh_xonly")) return NULL;
|
||||
return mk(env, out, 32);
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeEcdhRaw(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray privkey, jbyteArray pubkey) {
|
||||
(void)clz;
|
||||
jbyte *pk = pin(env, privkey);
|
||||
jbyte *pub = pin(env, pubkey);
|
||||
uint8_t out[32];
|
||||
int rc = ufsecp_ecdh_raw((ufsecp_ctx*)(uintptr_t)ctx, (const uint8_t*)pk, (const uint8_t*)pub, out);
|
||||
unpin(env, pubkey, pub);
|
||||
unpin(env, privkey, pk);
|
||||
if (throw_on_err(env, rc, "ecdh_raw")) return NULL;
|
||||
return mk(env, out, 32);
|
||||
}
|
||||
|
||||
/* ── Hash extras ─────────────────────────────────────────────────────── */
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeTaggedHash(
|
||||
JNIEnv *env, jclass clz, jbyteArray tag, jbyteArray data) {
|
||||
(void)clz;
|
||||
const char *t = (const char*)(*env)->GetByteArrayElements(env, tag, NULL);
|
||||
jsize tlen = (*env)->GetArrayLength(env, tag);
|
||||
/* null-terminate the tag */
|
||||
char tbuf[256];
|
||||
if (tlen >= (jsize)sizeof(tbuf)) tlen = (jsize)sizeof(tbuf) - 1;
|
||||
memcpy(tbuf, t, tlen);
|
||||
tbuf[tlen] = '\0';
|
||||
(*env)->ReleaseByteArrayElements(env, tag, (jbyte*)t, JNI_ABORT);
|
||||
|
||||
jbyte *d = pin(env, data);
|
||||
jsize dlen = (*env)->GetArrayLength(env, data);
|
||||
uint8_t out[32];
|
||||
int rc = ufsecp_tagged_hash(tbuf, (const uint8_t*)d, (size_t)dlen, out);
|
||||
unpin(env, data, d);
|
||||
if (throw_on_err(env, rc, "tagged_hash")) return NULL;
|
||||
return mk(env, out, 32);
|
||||
}
|
||||
|
||||
/* ── WIF extras ──────────────────────────────────────────────────────── */
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeWifDecode(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jstring wif) {
|
||||
(void)clz;
|
||||
const char *w = (*env)->GetStringUTFChars(env, wif, NULL);
|
||||
uint8_t privkey[32];
|
||||
int comp = 0, net = 0;
|
||||
int rc = ufsecp_wif_decode((ufsecp_ctx*)(uintptr_t)ctx, w, privkey, &comp, &net);
|
||||
(*env)->ReleaseStringUTFChars(env, wif, w);
|
||||
if (throw_on_err(env, rc, "wif_decode")) return NULL;
|
||||
|
||||
jbyteArray keyArr = mk(env, privkey, 32);
|
||||
jclass cls = (*env)->FindClass(env, "com/ultrafast/ufsecp/WifDecoded");
|
||||
jmethodID ctor = (*env)->GetMethodID(env, cls, "<init>", "([BZI)V");
|
||||
return (*env)->NewObject(env, cls, ctor, keyArr, (jboolean)(comp == 1), (jint)net);
|
||||
}
|
||||
|
||||
/* ── BIP-32 extras ───────────────────────────────────────────────────── */
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeBip32Privkey(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray key) {
|
||||
(void)clz;
|
||||
jbyte *k = pin(env, key);
|
||||
uint8_t priv[32];
|
||||
int rc = ufsecp_bip32_privkey((ufsecp_ctx*)(uintptr_t)ctx, (const uint8_t*)k, priv);
|
||||
unpin(env, key, k);
|
||||
if (throw_on_err(env, rc, "bip32_privkey")) return NULL;
|
||||
return mk(env, priv, 32);
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeBip32Pubkey(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray key) {
|
||||
(void)clz;
|
||||
jbyte *k = pin(env, key);
|
||||
uint8_t pub[33];
|
||||
int rc = ufsecp_bip32_pubkey((ufsecp_ctx*)(uintptr_t)ctx, (const uint8_t*)k, pub);
|
||||
unpin(env, key, k);
|
||||
if (throw_on_err(env, rc, "bip32_pubkey")) return NULL;
|
||||
return mk(env, pub, 33);
|
||||
}
|
||||
|
||||
/* ── Taproot ─────────────────────────────────────────────────────────── */
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeTaprootOutputKey(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray internalX, jbyteArray merkleRoot) {
|
||||
(void)clz;
|
||||
jbyte *ix = pin(env, internalX);
|
||||
jbyte *mr = merkleRoot ? pin(env, merkleRoot) : NULL;
|
||||
uint8_t outx[32];
|
||||
int parity = 0;
|
||||
int rc = ufsecp_taproot_output_key((ufsecp_ctx*)(uintptr_t)ctx,
|
||||
(const uint8_t*)ix, mr ? (const uint8_t*)mr : NULL, outx, &parity);
|
||||
if (mr) unpin(env, merkleRoot, mr);
|
||||
unpin(env, internalX, ix);
|
||||
if (throw_on_err(env, rc, "taproot_output_key")) return NULL;
|
||||
|
||||
jbyteArray outArr = mk(env, outx, 32);
|
||||
jclass cls = (*env)->FindClass(env, "com/ultrafast/ufsecp/TaprootOutputKeyResult");
|
||||
jmethodID ctor = (*env)->GetMethodID(env, cls, "<init>", "([BI)V");
|
||||
return (*env)->NewObject(env, cls, ctor, outArr, (jint)parity);
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeTaprootTweakSeckey(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray privkey, jbyteArray merkleRoot) {
|
||||
(void)clz;
|
||||
jbyte *pk = pin(env, privkey);
|
||||
jbyte *mr = merkleRoot ? pin(env, merkleRoot) : NULL;
|
||||
uint8_t out[32];
|
||||
int rc = ufsecp_taproot_tweak_seckey((ufsecp_ctx*)(uintptr_t)ctx,
|
||||
(const uint8_t*)pk, mr ? (const uint8_t*)mr : NULL, out);
|
||||
if (mr) unpin(env, merkleRoot, mr);
|
||||
unpin(env, privkey, pk);
|
||||
if (throw_on_err(env, rc, "taproot_tweak_seckey")) return NULL;
|
||||
return mk(env, out, 32);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_com_ultrafast_ufsecp_Ufsecp_nativeTaprootVerify(
|
||||
JNIEnv *env, jclass clz, jlong ctx, jbyteArray outputX, jint parity,
|
||||
jbyteArray internalX, jbyteArray merkleRoot) {
|
||||
(void)clz;
|
||||
jbyte *ox = pin(env, outputX);
|
||||
jbyte *ix = pin(env, internalX);
|
||||
jbyte *mr = merkleRoot ? pin(env, merkleRoot) : NULL;
|
||||
size_t mrLen = merkleRoot ? (size_t)(*env)->GetArrayLength(env, merkleRoot) : 0;
|
||||
int rc = ufsecp_taproot_verify((ufsecp_ctx*)(uintptr_t)ctx,
|
||||
(const uint8_t*)ox, (int)parity, (const uint8_t*)ix,
|
||||
mr ? (const uint8_t*)mr : NULL, mrLen);
|
||||
if (mr) unpin(env, merkleRoot, mr);
|
||||
unpin(env, internalX, ix);
|
||||
unpin(env, outputX, ox);
|
||||
return rc == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
package com.ultrafast.ufsecp;
|
||||
|
||||
/** ECDSA recoverable signature: 64-byte compact sig + recovery id (0-3). */
|
||||
public final class RecoverableSignature {
|
||||
private final byte[] signature;
|
||||
private final int recid;
|
||||
|
||||
public RecoverableSignature(byte[] signature, int recid) {
|
||||
this.signature = signature;
|
||||
this.recid = recid;
|
||||
}
|
||||
|
||||
public byte[] getSignature() { return signature; }
|
||||
public int getRecid() { return recid; }
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.ultrafast.ufsecp;
|
||||
|
||||
/** Taproot output key: 32-byte x-only public key + parity flag. */
|
||||
public final class TaprootOutputKeyResult {
|
||||
private final byte[] outputKey;
|
||||
private final int parity;
|
||||
|
||||
public TaprootOutputKeyResult(byte[] outputKey, int parity) {
|
||||
this.outputKey = outputKey;
|
||||
this.parity = parity;
|
||||
}
|
||||
|
||||
public byte[] getOutputKey() { return outputKey; }
|
||||
public int getParity() { return parity; }
|
||||
}
|
||||
@ -50,8 +50,26 @@ public final class Ufsecp implements AutoCloseable {
|
||||
// ── Version ──────────────────────────────────────────────────────
|
||||
|
||||
public static int version() { return nativeVersion(); }
|
||||
public static int abiVersion() { return nativeAbiVersion(); }
|
||||
public static String versionString(){ return nativeVersionString(); }
|
||||
|
||||
// ── Context extras ───────────────────────────────────────────────
|
||||
|
||||
public Ufsecp clone_() {
|
||||
alive();
|
||||
return new Ufsecp(nativeClone(ptr));
|
||||
}
|
||||
|
||||
public int lastError() {
|
||||
alive();
|
||||
return nativeLastError(ptr);
|
||||
}
|
||||
|
||||
public String lastErrorMsg() {
|
||||
alive();
|
||||
return nativeLastErrorMsg(ptr);
|
||||
}
|
||||
|
||||
// ── Key operations ───────────────────────────────────────────────
|
||||
|
||||
public byte[] pubkeyCreate(byte[] privkey) {
|
||||
@ -64,11 +82,36 @@ public final class Ufsecp implements AutoCloseable {
|
||||
return nativePubkeyCreateUncompressed(ptr, privkey);
|
||||
}
|
||||
|
||||
public byte[] pubkeyParse(byte[] pubkey) {
|
||||
alive();
|
||||
return nativePubkeyParse(ptr, pubkey);
|
||||
}
|
||||
|
||||
public byte[] pubkeyXonly(byte[] privkey) {
|
||||
alive();
|
||||
return nativePubkeyXonly(ptr, privkey);
|
||||
}
|
||||
|
||||
public boolean seckeyVerify(byte[] privkey) {
|
||||
alive();
|
||||
return nativeSeckeyVerify(ptr, privkey);
|
||||
}
|
||||
|
||||
public byte[] seckeyNegate(byte[] privkey) {
|
||||
alive();
|
||||
return nativeSeckeyNegate(ptr, privkey);
|
||||
}
|
||||
|
||||
public byte[] seckeyTweakAdd(byte[] privkey, byte[] tweak) {
|
||||
alive();
|
||||
return nativeSeckeyTweakAdd(ptr, privkey, tweak);
|
||||
}
|
||||
|
||||
public byte[] seckeyTweakMul(byte[] privkey, byte[] tweak) {
|
||||
alive();
|
||||
return nativeSeckeyTweakMul(ptr, privkey, tweak);
|
||||
}
|
||||
|
||||
// ── ECDSA ────────────────────────────────────────────────────────
|
||||
|
||||
public byte[] ecdsaSign(byte[] msgHash, byte[] privkey) {
|
||||
@ -81,6 +124,26 @@ public final class Ufsecp implements AutoCloseable {
|
||||
return nativeEcdsaVerify(ptr, msgHash, sig, pubkey);
|
||||
}
|
||||
|
||||
public byte[] ecdsaSigToDer(byte[] sig) {
|
||||
alive();
|
||||
return nativeEcdsaSigToDer(ptr, sig);
|
||||
}
|
||||
|
||||
public byte[] ecdsaSigFromDer(byte[] der) {
|
||||
alive();
|
||||
return nativeEcdsaSigFromDer(ptr, der);
|
||||
}
|
||||
|
||||
public RecoverableSignature ecdsaSignRecoverable(byte[] msgHash, byte[] privkey) {
|
||||
alive();
|
||||
return nativeEcdsaSignRecoverable(ptr, msgHash, privkey);
|
||||
}
|
||||
|
||||
public byte[] ecdsaRecover(byte[] msgHash, byte[] sig, int recid) {
|
||||
alive();
|
||||
return nativeEcdsaRecover(ptr, msgHash, sig, recid);
|
||||
}
|
||||
|
||||
// ── Schnorr ──────────────────────────────────────────────────────
|
||||
|
||||
public byte[] schnorrSign(byte[] msg, byte[] privkey, byte[] auxRand) {
|
||||
@ -100,10 +163,21 @@ public final class Ufsecp implements AutoCloseable {
|
||||
return nativeEcdh(ptr, privkey, pubkey);
|
||||
}
|
||||
|
||||
public byte[] ecdhXonly(byte[] privkey, byte[] pubkey) {
|
||||
alive();
|
||||
return nativeEcdhXonly(ptr, privkey, pubkey);
|
||||
}
|
||||
|
||||
public byte[] ecdhRaw(byte[] privkey, byte[] pubkey) {
|
||||
alive();
|
||||
return nativeEcdhRaw(ptr, privkey, pubkey);
|
||||
}
|
||||
|
||||
// ── Hashing ──────────────────────────────────────────────────────
|
||||
|
||||
public static byte[] sha256(byte[] data) { return nativeSha256(data); }
|
||||
public static byte[] hash160(byte[] data) { return nativeHash160(data); }
|
||||
public static byte[] taggedHash(byte[] tag, byte[] data) { return nativeTaggedHash(tag, data); }
|
||||
|
||||
// ── Addresses ────────────────────────────────────────────────────
|
||||
|
||||
@ -129,6 +203,11 @@ public final class Ufsecp implements AutoCloseable {
|
||||
return nativeWifEncode(ptr, privkey, compressed, network);
|
||||
}
|
||||
|
||||
public WifDecoded wifDecode(String wif) {
|
||||
alive();
|
||||
return nativeWifDecode(ptr, wif);
|
||||
}
|
||||
|
||||
// ── BIP-32 ───────────────────────────────────────────────────────
|
||||
|
||||
public byte[] bip32Master(byte[] seed) {
|
||||
@ -146,36 +225,86 @@ public final class Ufsecp implements AutoCloseable {
|
||||
return nativeBip32DerivePath(ptr, master, path);
|
||||
}
|
||||
|
||||
public byte[] bip32Privkey(byte[] key) {
|
||||
alive();
|
||||
return nativeBip32Privkey(ptr, key);
|
||||
}
|
||||
|
||||
public byte[] bip32Pubkey(byte[] key) {
|
||||
alive();
|
||||
return nativeBip32Pubkey(ptr, key);
|
||||
}
|
||||
|
||||
// ── Taproot ──────────────────────────────────────────────────────
|
||||
|
||||
public TaprootOutputKeyResult taprootOutputKey(byte[] internalX, byte[] merkleRoot) {
|
||||
alive();
|
||||
return nativeTaprootOutputKey(ptr, internalX, merkleRoot);
|
||||
}
|
||||
|
||||
public byte[] taprootTweakSeckey(byte[] privkey, byte[] merkleRoot) {
|
||||
alive();
|
||||
return nativeTaprootTweakSeckey(ptr, privkey, merkleRoot);
|
||||
}
|
||||
|
||||
public boolean taprootVerify(byte[] outputX, int parity, byte[] internalX, byte[] merkleRoot) {
|
||||
alive();
|
||||
return nativeTaprootVerify(ptr, outputX, parity, internalX, merkleRoot);
|
||||
}
|
||||
|
||||
// ── Native declarations ──────────────────────────────────────────
|
||||
|
||||
private static native long nativeCreate();
|
||||
private static native void nativeDestroy(long ptr);
|
||||
private static native long nativeClone(long ptr);
|
||||
private static native int nativeLastError(long ptr);
|
||||
private static native String nativeLastErrorMsg(long ptr);
|
||||
|
||||
private static native int nativeVersion();
|
||||
private static native int nativeAbiVersion();
|
||||
private static native String nativeVersionString();
|
||||
|
||||
private static native byte[] nativePubkeyCreate(long ctx, byte[] privkey);
|
||||
private static native byte[] nativePubkeyCreateUncompressed(long ctx, byte[] privkey);
|
||||
private static native byte[] nativePubkeyParse(long ctx, byte[] pubkey);
|
||||
private static native byte[] nativePubkeyXonly(long ctx, byte[] privkey);
|
||||
private static native boolean nativeSeckeyVerify(long ctx, byte[] privkey);
|
||||
private static native byte[] nativeSeckeyNegate(long ctx, byte[] privkey);
|
||||
private static native byte[] nativeSeckeyTweakAdd(long ctx, byte[] privkey, byte[] tweak);
|
||||
private static native byte[] nativeSeckeyTweakMul(long ctx, byte[] privkey, byte[] tweak);
|
||||
|
||||
private static native byte[] nativeEcdsaSign(long ctx, byte[] msgHash, byte[] privkey);
|
||||
private static native boolean nativeEcdsaVerify(long ctx, byte[] msgHash, byte[] sig, byte[] pubkey);
|
||||
private static native byte[] nativeEcdsaSigToDer(long ctx, byte[] sig);
|
||||
private static native byte[] nativeEcdsaSigFromDer(long ctx, byte[] der);
|
||||
private static native RecoverableSignature nativeEcdsaSignRecoverable(long ctx, byte[] msgHash, byte[] privkey);
|
||||
private static native byte[] nativeEcdsaRecover(long ctx, byte[] msgHash, byte[] sig, int recid);
|
||||
|
||||
private static native byte[] nativeSchnorrSign(long ctx, byte[] msg, byte[] privkey, byte[] auxRand);
|
||||
private static native boolean nativeSchnorrVerify(long ctx, byte[] msg, byte[] sig, byte[] pubkeyX);
|
||||
|
||||
private static native byte[] nativeEcdh(long ctx, byte[] privkey, byte[] pubkey);
|
||||
private static native byte[] nativeEcdhXonly(long ctx, byte[] privkey, byte[] pubkey);
|
||||
private static native byte[] nativeEcdhRaw(long ctx, byte[] privkey, byte[] pubkey);
|
||||
|
||||
private static native byte[] nativeSha256(byte[] data);
|
||||
private static native byte[] nativeHash160(byte[] data);
|
||||
private static native byte[] nativeTaggedHash(byte[] tag, byte[] data);
|
||||
|
||||
private static native String nativeAddrP2pkh(long ctx, byte[] pubkey, int network);
|
||||
private static native String nativeAddrP2wpkh(long ctx, byte[] pubkey, int network);
|
||||
private static native String nativeAddrP2tr(long ctx, byte[] xonly, int network);
|
||||
|
||||
private static native String nativeWifEncode(long ctx, byte[] privkey, boolean compressed, int network);
|
||||
private static native WifDecoded nativeWifDecode(long ctx, String wif);
|
||||
|
||||
private static native byte[] nativeBip32Master(long ctx, byte[] seed);
|
||||
private static native byte[] nativeBip32Derive(long ctx, byte[] parent, int index);
|
||||
private static native byte[] nativeBip32DerivePath(long ctx, byte[] master, String path);
|
||||
private static native byte[] nativeBip32Privkey(long ctx, byte[] key);
|
||||
private static native byte[] nativeBip32Pubkey(long ctx, byte[] key);
|
||||
|
||||
private static native TaprootOutputKeyResult nativeTaprootOutputKey(long ctx, byte[] internalX, byte[] merkleRoot);
|
||||
private static native byte[] nativeTaprootTweakSeckey(long ctx, byte[] privkey, byte[] merkleRoot);
|
||||
private static native boolean nativeTaprootVerify(long ctx, byte[] outputX, int parity, byte[] internalX, byte[] merkleRoot);
|
||||
}
|
||||
|
||||
18
bindings/java/src/com/ultrafast/ufsecp/WifDecoded.java
Normal file
18
bindings/java/src/com/ultrafast/ufsecp/WifDecoded.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.ultrafast.ufsecp;
|
||||
|
||||
/** Decoded WIF private key: raw 32-byte key + compression flag + network id. */
|
||||
public final class WifDecoded {
|
||||
private final byte[] privkey;
|
||||
private final boolean compressed;
|
||||
private final int network;
|
||||
|
||||
public WifDecoded(byte[] privkey, boolean compressed, int network) {
|
||||
this.privkey = privkey;
|
||||
this.compressed = compressed;
|
||||
this.network = network;
|
||||
}
|
||||
|
||||
public byte[] getPrivkey() { return privkey; }
|
||||
public boolean isCompressed() { return compressed; }
|
||||
public int getNetwork() { return network; }
|
||||
}
|
||||
@ -12,7 +12,6 @@ High-performance Node.js native addon for secp256k1 elliptic curve cryptography,
|
||||
- **Addresses** — P2PKH, P2WPKH, P2TR
|
||||
- **WIF** — encode/decode
|
||||
- **Hashing** — SHA-256 (hardware-accelerated), HASH160, tagged hash
|
||||
- **Constant-time** — all secret-key operations use CT layer automatically
|
||||
|
||||
## Install
|
||||
|
||||
@ -111,9 +110,9 @@ const { outputKeyX, parity } = secp.taprootOutputKey(xOnlyPub);
|
||||
const tweakedPriv = secp.taprootTweakPrivkey(privkey);
|
||||
```
|
||||
|
||||
## Performance
|
||||
## Architecture Note
|
||||
|
||||
Built on hand-optimized C/C++ with platform-specific acceleration (AVX2, SHA-NI, BMI2 on x86; NEON on ARM). All secret-key operations use the constant-time layer — no opt-in required.
|
||||
Built on hand-optimized C/C++ with platform-specific acceleration (AVX2, SHA-NI, BMI2 on x86; NEON on ARM). The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
| Operation | x86-64 | ARM64 | RISC-V |
|
||||
|-----------|--------|-------|--------|
|
||||
|
||||
62
bindings/php/README.md
Normal file
62
bindings/php/README.md
Normal file
@ -0,0 +1,62 @@
|
||||
# Ufsecp — PHP
|
||||
|
||||
PHP FFI binding for [UltrafastSecp256k1](https://github.com/shrec/UltrafastSecp256k1) — high-performance secp256k1 elliptic curve cryptography.
|
||||
|
||||
This is the **reference binding** with 100% API coverage.
|
||||
|
||||
## Features
|
||||
|
||||
- **ECDSA** — sign, verify, recover, DER serialization (RFC 6979)
|
||||
- **Schnorr** — BIP-340 sign/verify
|
||||
- **ECDH** — compressed, x-only, raw shared secret
|
||||
- **BIP-32** — HD key derivation (master/derive/path/privkey/pubkey)
|
||||
- **Taproot** — output key tweaking, verification (BIP-341)
|
||||
- **Addresses** — P2PKH, P2WPKH, P2TR
|
||||
- **WIF** — encode/decode
|
||||
- **Hashing** — SHA-256 (hardware-accelerated), HASH160, tagged hash
|
||||
- **Key tweaking** — negate, add, multiply
|
||||
- **Context** — create, destroy, clone, last_error, ctx_size
|
||||
|
||||
## Requirements
|
||||
|
||||
- PHP 7.4+ with FFI extension enabled
|
||||
- `libufsecp.so` / `ufsecp.dll` / `libufsecp.dylib`
|
||||
|
||||
## Quick Start
|
||||
|
||||
```php
|
||||
use Ultrafast\Ufsecp;
|
||||
|
||||
$ctx = new Ufsecp();
|
||||
|
||||
$privkey = str_repeat("\x00", 31) . "\x01";
|
||||
$pubkey = $ctx->pubkeyCreate($privkey);
|
||||
$msgHash = Ufsecp::sha256("hello");
|
||||
$sig = $ctx->ecdsaSign($msgHash, $privkey);
|
||||
$valid = $ctx->ecdsaVerify($msgHash, $sig, $pubkey);
|
||||
|
||||
$ctx->destroy();
|
||||
```
|
||||
|
||||
## ECDSA Recovery
|
||||
|
||||
```php
|
||||
[$sig, $recid] = $ctx->ecdsaSignRecoverable($msgHash, $privkey);
|
||||
$recovered = $ctx->ecdsaRecover($msgHash, $sig, $recid);
|
||||
```
|
||||
|
||||
## Taproot (BIP-341)
|
||||
|
||||
```php
|
||||
[$outputKey, $parity] = $ctx->taprootOutputKey($xonlyPub);
|
||||
$tweaked = $ctx->taprootTweakSeckey($privkey);
|
||||
$valid = $ctx->taprootVerify($outputKey, $parity, $xonlyPub);
|
||||
```
|
||||
|
||||
## Architecture Note
|
||||
|
||||
The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
68
bindings/python/README.md
Normal file
68
bindings/python/README.md
Normal file
@ -0,0 +1,68 @@
|
||||
# ufsecp — Python
|
||||
|
||||
Python ctypes binding for [UltrafastSecp256k1](https://github.com/shrec/UltrafastSecp256k1) — high-performance secp256k1 elliptic curve cryptography.
|
||||
|
||||
## Features
|
||||
|
||||
- **ECDSA** — sign, verify, recover, DER serialization (RFC 6979)
|
||||
- **Schnorr** — BIP-340 sign/verify
|
||||
- **ECDH** — compressed, x-only, raw shared secret
|
||||
- **BIP-32** — HD key derivation (master/derive/path/privkey/pubkey)
|
||||
- **Taproot** — output key tweaking, verification (BIP-341)
|
||||
- **Addresses** — P2PKH, P2WPKH, P2TR
|
||||
- **WIF** — encode/decode
|
||||
- **Hashing** — SHA-256 (hardware-accelerated), HASH160, tagged hash
|
||||
- **Key tweaking** — negate, add, multiply
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
pip install ufsecp
|
||||
```
|
||||
|
||||
Requires the native `libufsecp.so` / `ufsecp.dll` / `libufsecp.dylib` alongside the package or set `UFSECP_LIB` env var.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
from ufsecp import Ufsecp
|
||||
|
||||
with Ufsecp() as ctx:
|
||||
privkey = bytes(31) + b'\x01'
|
||||
pubkey = ctx.pubkey_create(privkey)
|
||||
msg_hash = ctx.sha256(b'hello')
|
||||
sig = ctx.ecdsa_sign(msg_hash, privkey)
|
||||
valid = ctx.ecdsa_verify(msg_hash, sig, pubkey)
|
||||
```
|
||||
|
||||
## ECDSA Recovery
|
||||
|
||||
```python
|
||||
rs = ctx.ecdsa_sign_recoverable(msg_hash, privkey)
|
||||
recovered = ctx.ecdsa_recover(msg_hash, rs.signature, rs.recovery_id)
|
||||
```
|
||||
|
||||
## BIP-32 HD Derivation
|
||||
|
||||
```python
|
||||
master = ctx.bip32_master(seed)
|
||||
child = ctx.bip32_derive_path(master, "m/44'/0'/0'/0/0")
|
||||
child_priv = ctx.bip32_privkey(child)
|
||||
child_pub = ctx.bip32_pubkey(child)
|
||||
```
|
||||
|
||||
## Taproot (BIP-341)
|
||||
|
||||
```python
|
||||
tok = ctx.taproot_output_key(xonly_pub)
|
||||
tweaked = ctx.taproot_tweak_seckey(privkey)
|
||||
valid = ctx.taproot_verify(tok.output_key_x, tok.parity, xonly_pub)
|
||||
```
|
||||
|
||||
## Architecture Note
|
||||
|
||||
The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@ -182,6 +182,25 @@ class Ufsecp:
|
||||
self._lib.ufsecp_version_string.restype = c_char_p
|
||||
return self._lib.ufsecp_version_string().decode()
|
||||
|
||||
# -- Context extras ---------------------------------------------------
|
||||
|
||||
def clone(self) -> 'Ufsecp':
|
||||
"""Clone this context (deep copy)."""
|
||||
clone_ptr = c_void_p()
|
||||
self._throw(self._lib.ufsecp_ctx_clone(self._ctx, byref(clone_ptr)), "ctx_clone")
|
||||
obj = object.__new__(Ufsecp)
|
||||
obj._lib = self._lib
|
||||
obj._ctx = clone_ptr
|
||||
return obj
|
||||
|
||||
def last_error(self) -> int:
|
||||
"""Return last error code."""
|
||||
return self._lib.ufsecp_last_error(self._ctx)
|
||||
|
||||
def last_error_msg(self) -> str:
|
||||
"""Return last error message."""
|
||||
return self._lib.ufsecp_last_error_msg(self._ctx).decode()
|
||||
|
||||
# -- Key operations ---------------------------------------------------
|
||||
|
||||
def pubkey_create(self, privkey: bytes) -> bytes:
|
||||
@ -437,6 +456,14 @@ class Ufsecp:
|
||||
L.ufsecp_ctx_destroy.argtypes = [vp]
|
||||
L.ufsecp_ctx_destroy.restype = None
|
||||
|
||||
L.ufsecp_ctx_clone.argtypes = [vp, pvp]
|
||||
L.ufsecp_ctx_clone.restype = c_int
|
||||
|
||||
L.ufsecp_last_error.argtypes = [vp]
|
||||
L.ufsecp_last_error.restype = c_int
|
||||
L.ufsecp_last_error_msg.argtypes = [vp]
|
||||
L.ufsecp_last_error_msg.restype = c_char_p
|
||||
|
||||
L.ufsecp_version.restype = c_uint32
|
||||
L.ufsecp_abi_version.restype = c_uint32
|
||||
|
||||
|
||||
@ -14,7 +14,6 @@ Uses native C/C++ through JSI (Android NDK + iOS) for maximum performance — no
|
||||
- **Addresses** — P2PKH, P2WPKH, P2TR
|
||||
- **WIF** — encode/decode
|
||||
- **Hashing** — SHA-256, HASH160, tagged hash
|
||||
- **Constant-time** — all secret-key operations use CT layer automatically
|
||||
|
||||
## Install
|
||||
|
||||
@ -85,6 +84,10 @@ const childPriv = secp.bip32GetPrivkey(child);
|
||||
| iOS | iOS 13+, CocoaPods |
|
||||
| React Native | >= 0.71.0 |
|
||||
|
||||
## Architecture Note
|
||||
|
||||
The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
67
bindings/react-native/lib/ufsecp.js
vendored
67
bindings/react-native/lib/ufsecp.js
vendored
@ -57,6 +57,7 @@ class UfsecpContext {
|
||||
// ── Version ──────────────────────────────────────────────────────
|
||||
|
||||
static version() { return NativeUfsecp.version(); }
|
||||
static abiVersion() { return NativeUfsecp.abiVersion(); }
|
||||
static versionString() { return NativeUfsecp.versionString(); }
|
||||
|
||||
// ── Key ops ──────────────────────────────────────────────────────
|
||||
@ -77,6 +78,31 @@ class UfsecpContext {
|
||||
return NativeUfsecp.seckeyVerify(this._h, privkeyHex);
|
||||
}
|
||||
|
||||
async seckeyNegate(privkeyHex) {
|
||||
this._alive();
|
||||
return NativeUfsecp.seckeyNegate(this._h, privkeyHex);
|
||||
}
|
||||
|
||||
async seckeyTweakAdd(privkeyHex, tweakHex) {
|
||||
this._alive();
|
||||
return NativeUfsecp.seckeyTweakAdd(this._h, privkeyHex, tweakHex);
|
||||
}
|
||||
|
||||
async seckeyTweakMul(privkeyHex, tweakHex) {
|
||||
this._alive();
|
||||
return NativeUfsecp.seckeyTweakMul(this._h, privkeyHex, tweakHex);
|
||||
}
|
||||
|
||||
async pubkeyParse(pubkeyHex) {
|
||||
this._alive();
|
||||
return NativeUfsecp.pubkeyParse(this._h, pubkeyHex);
|
||||
}
|
||||
|
||||
async pubkeyXonly(privkeyHex) {
|
||||
this._alive();
|
||||
return NativeUfsecp.pubkeyXonly(this._h, privkeyHex);
|
||||
}
|
||||
|
||||
// ── ECDSA ────────────────────────────────────────────────────────
|
||||
|
||||
async ecdsaSign(msgHashHex, privkeyHex) {
|
||||
@ -99,6 +125,16 @@ class UfsecpContext {
|
||||
return NativeUfsecp.ecdsaRecover(this._h, msgHashHex, sigHex, recid);
|
||||
}
|
||||
|
||||
async ecdsaSigToDer(sigHex) {
|
||||
this._alive();
|
||||
return NativeUfsecp.ecdsaSigToDer(this._h, sigHex);
|
||||
}
|
||||
|
||||
async ecdsaSigFromDer(derHex) {
|
||||
this._alive();
|
||||
return NativeUfsecp.ecdsaSigFromDer(this._h, derHex);
|
||||
}
|
||||
|
||||
// ── Schnorr ──────────────────────────────────────────────────────
|
||||
|
||||
async schnorrSign(msgHex, privkeyHex, auxRandHex) {
|
||||
@ -118,10 +154,21 @@ class UfsecpContext {
|
||||
return NativeUfsecp.ecdh(this._h, privkeyHex, pubkeyHex);
|
||||
}
|
||||
|
||||
async ecdhXonly(privkeyHex, pubkeyHex) {
|
||||
this._alive();
|
||||
return NativeUfsecp.ecdhXonly(this._h, privkeyHex, pubkeyHex);
|
||||
}
|
||||
|
||||
async ecdhRaw(privkeyHex, pubkeyHex) {
|
||||
this._alive();
|
||||
return NativeUfsecp.ecdhRaw(this._h, privkeyHex, pubkeyHex);
|
||||
}
|
||||
|
||||
// ── Hashing ──────────────────────────────────────────────────────
|
||||
|
||||
static sha256(dataHex) { return NativeUfsecp.sha256(dataHex); }
|
||||
static hash160(dataHex) { return NativeUfsecp.hash160(dataHex); }
|
||||
static taggedHash(tag, dataHex) { return NativeUfsecp.taggedHash(tag, dataHex); }
|
||||
|
||||
// ── Addresses ────────────────────────────────────────────────────
|
||||
|
||||
@ -136,6 +183,11 @@ class UfsecpContext {
|
||||
return NativeUfsecp.wifEncode(this._h, privkeyHex, compressed, network);
|
||||
}
|
||||
|
||||
async wifDecode(wifStr) {
|
||||
this._alive();
|
||||
return NativeUfsecp.wifDecode(this._h, wifStr);
|
||||
}
|
||||
|
||||
// ── BIP-32 ───────────────────────────────────────────────────────
|
||||
|
||||
async bip32Master(seedHex) {
|
||||
@ -153,6 +205,16 @@ class UfsecpContext {
|
||||
return NativeUfsecp.bip32DerivePath(this._h, masterHex, path);
|
||||
}
|
||||
|
||||
async bip32Privkey(keyHex) {
|
||||
this._alive();
|
||||
return NativeUfsecp.bip32Privkey(this._h, keyHex);
|
||||
}
|
||||
|
||||
async bip32Pubkey(keyHex) {
|
||||
this._alive();
|
||||
return NativeUfsecp.bip32Pubkey(this._h, keyHex);
|
||||
}
|
||||
|
||||
// ── Taproot ──────────────────────────────────────────────────────
|
||||
|
||||
async taprootOutputKey(internalXHex, merkleRootHex = null) {
|
||||
@ -165,6 +227,11 @@ class UfsecpContext {
|
||||
return NativeUfsecp.taprootTweakSeckey(this._h, privkeyHex, merkleRootHex);
|
||||
}
|
||||
|
||||
async taprootVerify(outputXHex, parity, internalXHex, merkleRootHex = null) {
|
||||
this._alive();
|
||||
return NativeUfsecp.taprootVerify(this._h, outputXHex, parity, internalXHex, merkleRootHex);
|
||||
}
|
||||
|
||||
// ── Internal ─────────────────────────────────────────────────────
|
||||
|
||||
_alive() {
|
||||
|
||||
62
bindings/ruby/README.md
Normal file
62
bindings/ruby/README.md
Normal file
@ -0,0 +1,62 @@
|
||||
# ufsecp — Ruby
|
||||
|
||||
Ruby FFI binding for [UltrafastSecp256k1](https://github.com/shrec/UltrafastSecp256k1) — high-performance secp256k1 elliptic curve cryptography.
|
||||
|
||||
## Features
|
||||
|
||||
- **ECDSA** — sign, verify, recover, DER serialization (RFC 6979)
|
||||
- **Schnorr** — BIP-340 sign/verify
|
||||
- **ECDH** — compressed, x-only, raw shared secret
|
||||
- **BIP-32** — HD key derivation (master/derive/path/privkey/pubkey)
|
||||
- **Taproot** — output key tweaking, verification (BIP-341)
|
||||
- **Addresses** — P2PKH, P2WPKH, P2TR
|
||||
- **WIF** — encode/decode
|
||||
- **Hashing** — SHA-256 (hardware-accelerated), HASH160, tagged hash
|
||||
- **Key tweaking** — negate, add, multiply
|
||||
|
||||
## Install
|
||||
|
||||
```ruby
|
||||
gem 'ufsecp'
|
||||
```
|
||||
|
||||
Requires `libufsecp.so` / `ufsecp.dll` / `libufsecp.dylib` on the library path.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```ruby
|
||||
require 'ufsecp'
|
||||
|
||||
ctx = Ufsecp::Context.new
|
||||
|
||||
privkey = "\x00" * 31 + "\x01"
|
||||
pubkey = ctx.pubkey_create(privkey)
|
||||
msg_hash = Ufsecp.sha256("hello")
|
||||
sig = ctx.ecdsa_sign(msg_hash, privkey)
|
||||
valid = ctx.ecdsa_verify(msg_hash, sig, pubkey)
|
||||
|
||||
ctx.destroy
|
||||
```
|
||||
|
||||
## ECDSA Recovery
|
||||
|
||||
```ruby
|
||||
sig, recid = ctx.ecdsa_sign_recoverable(msg_hash, privkey)
|
||||
recovered = ctx.ecdsa_recover(msg_hash, sig, recid)
|
||||
```
|
||||
|
||||
## Taproot (BIP-341)
|
||||
|
||||
```ruby
|
||||
output_key, parity = ctx.taproot_output_key(xonly_pub)
|
||||
tweaked = ctx.taproot_tweak_seckey(privkey)
|
||||
valid = ctx.taproot_verify(output_key, parity, xonly_pub)
|
||||
```
|
||||
|
||||
## Architecture Note
|
||||
|
||||
The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
53
bindings/rust/README.md
Normal file
53
bindings/rust/README.md
Normal file
@ -0,0 +1,53 @@
|
||||
# ufsecp — Rust
|
||||
|
||||
Safe Rust wrapper for [UltrafastSecp256k1](https://github.com/shrec/UltrafastSecp256k1) — high-performance secp256k1 elliptic curve cryptography.
|
||||
|
||||
Wraps the `ufsecp-sys` FFI crate with a safe, ergonomic API.
|
||||
|
||||
## Features
|
||||
|
||||
- **ECDSA** — sign, verify, recover, DER serialization (RFC 6979)
|
||||
- **Schnorr** — BIP-340 sign/verify
|
||||
- **ECDH** — compressed, x-only, raw shared secret
|
||||
- **BIP-32** — HD key derivation (master/derive/path/privkey/pubkey)
|
||||
- **Taproot** — output key tweaking, verification (BIP-341)
|
||||
- **Addresses** — P2PKH, P2WPKH, P2TR
|
||||
- **WIF** — encode/decode
|
||||
- **Hashing** — SHA-256 (hardware-accelerated), HASH160, tagged hash
|
||||
- **Key tweaking** — negate, add, multiply
|
||||
|
||||
## Quick Start
|
||||
|
||||
```rust
|
||||
use ufsecp::Context;
|
||||
|
||||
let ctx = Context::new()?;
|
||||
let privkey = [0u8; 31].iter().chain(&[1u8]).copied().collect::<Vec<_>>();
|
||||
let pubkey = ctx.pubkey_create(&privkey)?;
|
||||
let msg_hash = Context::sha256(b"hello")?;
|
||||
let sig = ctx.ecdsa_sign(&msg_hash, &privkey)?;
|
||||
let valid = ctx.ecdsa_verify(&msg_hash, &sig, &pubkey)?;
|
||||
```
|
||||
|
||||
## ECDSA Recovery
|
||||
|
||||
```rust
|
||||
let (sig, recid) = ctx.ecdsa_sign_recoverable(&msg_hash, &privkey)?;
|
||||
let recovered = ctx.ecdsa_recover(&msg_hash, &sig, recid)?;
|
||||
```
|
||||
|
||||
## Taproot (BIP-341)
|
||||
|
||||
```rust
|
||||
let (output_key, parity) = ctx.taproot_output_key(&xonly_pub, None)?;
|
||||
let tweaked = ctx.taproot_tweak_seckey(&privkey, None)?;
|
||||
let valid = ctx.taproot_verify(&output_key, parity, &xonly_pub, None)?;
|
||||
```
|
||||
|
||||
## Architecture Note
|
||||
|
||||
The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@ -127,6 +127,20 @@ impl Context {
|
||||
Ok(Context { ptr })
|
||||
}
|
||||
|
||||
/// Return the last error code stored in this context.
|
||||
pub fn last_error(&self) -> i32 {
|
||||
unsafe { ufsecp_sys::ufsecp_last_error(self.ptr) }
|
||||
}
|
||||
|
||||
/// Return the last error message stored in this context.
|
||||
pub fn last_error_msg(&self) -> &str {
|
||||
unsafe {
|
||||
let p = ufsecp_sys::ufsecp_last_error_msg(self.ptr);
|
||||
if p.is_null() { return ""; }
|
||||
CStr::from_ptr(p).to_str().unwrap_or("")
|
||||
}
|
||||
}
|
||||
|
||||
// ── Version ────────────────────────────────────────────────────────
|
||||
|
||||
pub fn version() -> u32 { unsafe { ufsecp_sys::ufsecp_version() } }
|
||||
|
||||
59
bindings/swift/README.md
Normal file
59
bindings/swift/README.md
Normal file
@ -0,0 +1,59 @@
|
||||
# Ufsecp — Swift
|
||||
|
||||
Swift binding for [UltrafastSecp256k1](https://github.com/shrec/UltrafastSecp256k1) — high-performance secp256k1 elliptic curve cryptography via C interop.
|
||||
|
||||
## Features
|
||||
|
||||
- **ECDSA** — sign, verify, recover, DER serialization (RFC 6979)
|
||||
- **Schnorr** — BIP-340 sign/verify
|
||||
- **ECDH** — compressed, x-only, raw shared secret
|
||||
- **BIP-32** — HD key derivation (master/derive/path/privkey/pubkey)
|
||||
- **Taproot** — output key tweaking, verification (BIP-341)
|
||||
- **Addresses** — P2PKH, P2WPKH, P2TR
|
||||
- **WIF** — encode/decode
|
||||
- **Hashing** — SHA-256 (hardware-accelerated), HASH160, tagged hash
|
||||
- **Key tweaking** — negate, add, multiply
|
||||
|
||||
## Quick Start
|
||||
|
||||
```swift
|
||||
let ctx = try UfsecpContext()
|
||||
defer { ctx.destroy() }
|
||||
|
||||
let privkey = Data(repeating: 0, count: 31) + Data([0x01])
|
||||
let pubkey = try ctx.pubkeyCreate(privkey: privkey)
|
||||
let msgHash = try UfsecpContext.sha256(Data("hello".utf8))
|
||||
let sig = try ctx.ecdsaSign(msgHash: msgHash, privkey: privkey)
|
||||
let valid = try ctx.ecdsaVerify(msgHash: msgHash, sig: sig, pubkey: pubkey)
|
||||
```
|
||||
|
||||
## ECDSA Recovery
|
||||
|
||||
```swift
|
||||
let rs = try ctx.ecdsaSignRecoverable(msgHash: msgHash, privkey: privkey)
|
||||
let recovered = try ctx.ecdsaRecover(msgHash: msgHash, sig: rs.signature, recid: rs.recoveryId)
|
||||
```
|
||||
|
||||
## Schnorr (BIP-340)
|
||||
|
||||
```swift
|
||||
let xonly = try ctx.pubkeyXonly(privkey: privkey)
|
||||
let schnorrSig = try ctx.schnorrSign(msg: msgHash, privkey: privkey, auxRand: auxRand)
|
||||
let ok = try ctx.schnorrVerify(msg: msgHash, sig: schnorrSig, pubkeyX: xonly)
|
||||
```
|
||||
|
||||
## Taproot (BIP-341)
|
||||
|
||||
```swift
|
||||
let tok = try ctx.taprootOutputKey(internalX: xonly, merkleRoot: nil)
|
||||
let tweaked = try ctx.taprootTweakSeckey(privkey: privkey, merkleRoot: nil)
|
||||
let tapValid = try ctx.taprootVerify(outputX: tok.outputKeyX, parity: tok.parity, internalX: xonly, merkleRoot: nil)
|
||||
```
|
||||
|
||||
## Architecture Note
|
||||
|
||||
The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@ -240,6 +240,84 @@ public final class UfsecpContext {
|
||||
return Data(out)
|
||||
}
|
||||
|
||||
public func ecdhXonly(privkey: Data, pubkey: Data) throws -> Data {
|
||||
try chk(privkey, 32, "privkey"); try chk(pubkey, 33, "pubkey"); try alive()
|
||||
var out = [UInt8](repeating: 0, count: 32)
|
||||
try privkey.withUnsafeBytes { pk in
|
||||
try pubkey.withUnsafeBytes { pub in
|
||||
try throwRC(ufsecp_ecdh_xonly(ctx!,
|
||||
pk.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||
pub.baseAddress!.assumingMemoryBound(to: UInt8.self), &out), "ecdh_xonly")
|
||||
}
|
||||
}
|
||||
return Data(out)
|
||||
}
|
||||
|
||||
public func ecdhRaw(privkey: Data, pubkey: Data) throws -> Data {
|
||||
try chk(privkey, 32, "privkey"); try chk(pubkey, 33, "pubkey"); try alive()
|
||||
var out = [UInt8](repeating: 0, count: 32)
|
||||
try privkey.withUnsafeBytes { pk in
|
||||
try pubkey.withUnsafeBytes { pub in
|
||||
try throwRC(ufsecp_ecdh_raw(ctx!,
|
||||
pk.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||
pub.baseAddress!.assumingMemoryBound(to: UInt8.self), &out), "ecdh_raw")
|
||||
}
|
||||
}
|
||||
return Data(out)
|
||||
}
|
||||
|
||||
// MARK: ECDSA DER
|
||||
|
||||
public func ecdsaSigToDer(sig: Data) throws -> Data {
|
||||
try chk(sig, 64, "sig"); try alive()
|
||||
var der = [UInt8](repeating: 0, count: 72)
|
||||
var dlen: Int = 72
|
||||
try sig.withUnsafeBytes { s in
|
||||
try throwRC(ufsecp_ecdsa_sig_to_der(ctx!,
|
||||
s.baseAddress!.assumingMemoryBound(to: UInt8.self), &der, &dlen), "ecdsa_sig_to_der")
|
||||
}
|
||||
return Data(der.prefix(dlen))
|
||||
}
|
||||
|
||||
public func ecdsaSigFromDer(der: Data) throws -> Data {
|
||||
try alive()
|
||||
var sig = [UInt8](repeating: 0, count: 64)
|
||||
try der.withUnsafeBytes { d in
|
||||
try throwRC(ufsecp_ecdsa_sig_from_der(ctx!,
|
||||
d.baseAddress!.assumingMemoryBound(to: UInt8.self), der.count, &sig), "ecdsa_sig_from_der")
|
||||
}
|
||||
return Data(sig)
|
||||
}
|
||||
|
||||
// MARK: ECDSA Recovery
|
||||
|
||||
public func ecdsaSignRecoverable(msgHash: Data, privkey: Data) throws -> RecoverableSignature {
|
||||
try chk(msgHash, 32, "msgHash"); try chk(privkey, 32, "privkey"); try alive()
|
||||
var sig = [UInt8](repeating: 0, count: 64)
|
||||
var recid: Int32 = 0
|
||||
try msgHash.withUnsafeBytes { msg in
|
||||
try privkey.withUnsafeBytes { pk in
|
||||
try throwRC(ufsecp_ecdsa_sign_recoverable(ctx!,
|
||||
msg.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||
pk.baseAddress!.assumingMemoryBound(to: UInt8.self), &sig, &recid), "ecdsa_sign_recoverable")
|
||||
}
|
||||
}
|
||||
return RecoverableSignature(signature: Data(sig), recoveryId: recid)
|
||||
}
|
||||
|
||||
public func ecdsaRecover(msgHash: Data, sig: Data, recid: Int32) throws -> Data {
|
||||
try chk(msgHash, 32, "msgHash"); try chk(sig, 64, "sig"); try alive()
|
||||
var pub = [UInt8](repeating: 0, count: 33)
|
||||
try msgHash.withUnsafeBytes { msg in
|
||||
try sig.withUnsafeBytes { s in
|
||||
try throwRC(ufsecp_ecdsa_recover(ctx!,
|
||||
msg.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||
s.baseAddress!.assumingMemoryBound(to: UInt8.self), recid, &pub), "ecdsa_recover")
|
||||
}
|
||||
}
|
||||
return Data(pub)
|
||||
}
|
||||
|
||||
// MARK: Hashing
|
||||
|
||||
public static func sha256(_ data: Data) throws -> Data {
|
||||
@ -258,6 +336,189 @@ public final class UfsecpContext {
|
||||
return Data(out)
|
||||
}
|
||||
|
||||
public static func taggedHash(tag: String, data: Data) throws -> Data {
|
||||
var out = [UInt8](repeating: 0, count: 32)
|
||||
try data.withUnsafeBytes { d in
|
||||
try throwRC(ufsecp_tagged_hash(tag,
|
||||
d.baseAddress!.assumingMemoryBound(to: UInt8.self), data.count, &out), "tagged_hash")
|
||||
}
|
||||
return Data(out)
|
||||
}
|
||||
|
||||
// MARK: Addresses
|
||||
|
||||
public func addrP2pkh(pubkey: Data, network: Network = .mainnet) throws -> String {
|
||||
try chk(pubkey, 33, "pubkey"); try alive()
|
||||
var buf = [CChar](repeating: 0, count: 64)
|
||||
try pubkey.withUnsafeBytes { pk in
|
||||
try throwRC(ufsecp_addr_p2pkh(ctx!,
|
||||
pk.baseAddress!.assumingMemoryBound(to: UInt8.self), &buf, 64, network.rawValue), "addr_p2pkh")
|
||||
}
|
||||
return String(cString: buf)
|
||||
}
|
||||
|
||||
public func addrP2wpkh(pubkey: Data, network: Network = .mainnet) throws -> String {
|
||||
try chk(pubkey, 33, "pubkey"); try alive()
|
||||
var buf = [CChar](repeating: 0, count: 128)
|
||||
try pubkey.withUnsafeBytes { pk in
|
||||
try throwRC(ufsecp_addr_p2wpkh(ctx!,
|
||||
pk.baseAddress!.assumingMemoryBound(to: UInt8.self), &buf, 128, network.rawValue), "addr_p2wpkh")
|
||||
}
|
||||
return String(cString: buf)
|
||||
}
|
||||
|
||||
public func addrP2tr(xonly: Data, network: Network = .mainnet) throws -> String {
|
||||
try chk(xonly, 32, "xonly"); try alive()
|
||||
var buf = [CChar](repeating: 0, count: 128)
|
||||
try xonly.withUnsafeBytes { x in
|
||||
try throwRC(ufsecp_addr_p2tr(ctx!,
|
||||
x.baseAddress!.assumingMemoryBound(to: UInt8.self), &buf, 128, network.rawValue), "addr_p2tr")
|
||||
}
|
||||
return String(cString: buf)
|
||||
}
|
||||
|
||||
// MARK: WIF
|
||||
|
||||
public func wifEncode(privkey: Data, compressed: Bool = true, network: Network = .mainnet) throws -> String {
|
||||
try chk(privkey, 32, "privkey"); try alive()
|
||||
var buf = [CChar](repeating: 0, count: 64)
|
||||
try privkey.withUnsafeBytes { pk in
|
||||
try throwRC(ufsecp_wif_encode(ctx!,
|
||||
pk.baseAddress!.assumingMemoryBound(to: UInt8.self), compressed ? 1 : 0,
|
||||
network.rawValue, &buf, 64), "wif_encode")
|
||||
}
|
||||
return String(cString: buf)
|
||||
}
|
||||
|
||||
public func wifDecode(wif: String) throws -> WifDecoded {
|
||||
try alive()
|
||||
var privkey = [UInt8](repeating: 0, count: 32)
|
||||
var compressed: Int32 = 0
|
||||
var net: Int32 = 0
|
||||
try throwRC(ufsecp_wif_decode(ctx!, wif, &privkey, &compressed, &net), "wif_decode")
|
||||
return WifDecoded(privkey: Data(privkey), compressed: compressed == 1,
|
||||
network: Network(rawValue: net) ?? .mainnet)
|
||||
}
|
||||
|
||||
// MARK: BIP-32
|
||||
|
||||
public func bip32Master(seed: Data) throws -> Data {
|
||||
try alive()
|
||||
var out = [UInt8](repeating: 0, count: 64)
|
||||
try seed.withUnsafeBytes { s in
|
||||
try throwRC(ufsecp_bip32_master(ctx!,
|
||||
s.baseAddress!.assumingMemoryBound(to: UInt8.self), seed.count, &out), "bip32_master")
|
||||
}
|
||||
return Data(out)
|
||||
}
|
||||
|
||||
public func bip32Derive(parent: Data, index: UInt32) throws -> Data {
|
||||
try chk(parent, 64, "parent"); try alive()
|
||||
var out = [UInt8](repeating: 0, count: 64)
|
||||
try parent.withUnsafeBytes { p in
|
||||
try throwRC(ufsecp_bip32_derive(ctx!,
|
||||
p.baseAddress!.assumingMemoryBound(to: UInt8.self), index, &out), "bip32_derive")
|
||||
}
|
||||
return Data(out)
|
||||
}
|
||||
|
||||
public func bip32DerivePath(master: Data, path: String) throws -> Data {
|
||||
try chk(master, 64, "master"); try alive()
|
||||
var out = [UInt8](repeating: 0, count: 64)
|
||||
try master.withUnsafeBytes { m in
|
||||
try throwRC(ufsecp_bip32_derive_path(ctx!,
|
||||
m.baseAddress!.assumingMemoryBound(to: UInt8.self), path, &out), "bip32_derive_path")
|
||||
}
|
||||
return Data(out)
|
||||
}
|
||||
|
||||
public func bip32Privkey(key: Data) throws -> Data {
|
||||
try chk(key, 64, "key"); try alive()
|
||||
var out = [UInt8](repeating: 0, count: 32)
|
||||
try key.withUnsafeBytes { k in
|
||||
try throwRC(ufsecp_bip32_privkey(ctx!,
|
||||
k.baseAddress!.assumingMemoryBound(to: UInt8.self), &out), "bip32_privkey")
|
||||
}
|
||||
return Data(out)
|
||||
}
|
||||
|
||||
public func bip32Pubkey(key: Data) throws -> Data {
|
||||
try chk(key, 64, "key"); try alive()
|
||||
var out = [UInt8](repeating: 0, count: 33)
|
||||
try key.withUnsafeBytes { k in
|
||||
try throwRC(ufsecp_bip32_pubkey(ctx!,
|
||||
k.baseAddress!.assumingMemoryBound(to: UInt8.self), &out), "bip32_pubkey")
|
||||
}
|
||||
return Data(out)
|
||||
}
|
||||
|
||||
// MARK: Taproot
|
||||
|
||||
public func taprootOutputKey(internalX: Data, merkleRoot: Data?) throws -> TaprootOutputKeyResult {
|
||||
try chk(internalX, 32, "internalX"); try alive()
|
||||
var outx = [UInt8](repeating: 0, count: 32)
|
||||
var parity: Int32 = 0
|
||||
if let mr = merkleRoot {
|
||||
try internalX.withUnsafeBytes { ix in
|
||||
try mr.withUnsafeBytes { m in
|
||||
try throwRC(ufsecp_taproot_output_key(ctx!,
|
||||
ix.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||
m.baseAddress!.assumingMemoryBound(to: UInt8.self), &outx, &parity), "taproot_output_key")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try internalX.withUnsafeBytes { ix in
|
||||
try throwRC(ufsecp_taproot_output_key(ctx!,
|
||||
ix.baseAddress!.assumingMemoryBound(to: UInt8.self), nil, &outx, &parity), "taproot_output_key")
|
||||
}
|
||||
}
|
||||
return TaprootOutputKeyResult(outputKeyX: Data(outx), parity: parity)
|
||||
}
|
||||
|
||||
public func taprootTweakSeckey(privkey: Data, merkleRoot: Data?) throws -> Data {
|
||||
try chk(privkey, 32, "privkey"); try alive()
|
||||
var out = [UInt8](repeating: 0, count: 32)
|
||||
if let mr = merkleRoot {
|
||||
try privkey.withUnsafeBytes { pk in
|
||||
try mr.withUnsafeBytes { m in
|
||||
try throwRC(ufsecp_taproot_tweak_seckey(ctx!,
|
||||
pk.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||
m.baseAddress!.assumingMemoryBound(to: UInt8.self), &out), "taproot_tweak_seckey")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try privkey.withUnsafeBytes { pk in
|
||||
try throwRC(ufsecp_taproot_tweak_seckey(ctx!,
|
||||
pk.baseAddress!.assumingMemoryBound(to: UInt8.self), nil, &out), "taproot_tweak_seckey")
|
||||
}
|
||||
}
|
||||
return Data(out)
|
||||
}
|
||||
|
||||
public func taprootVerify(outputX: Data, parity: Int32, internalX: Data, merkleRoot: Data?) throws -> Bool {
|
||||
try chk(outputX, 32, "outputX"); try chk(internalX, 32, "internalX"); try alive()
|
||||
if let mr = merkleRoot {
|
||||
return outputX.withUnsafeBytes { ox in
|
||||
internalX.withUnsafeBytes { ix in
|
||||
mr.withUnsafeBytes { m in
|
||||
ufsecp_taproot_verify(ctx!,
|
||||
ox.baseAddress!.assumingMemoryBound(to: UInt8.self), parity,
|
||||
ix.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||
m.baseAddress!.assumingMemoryBound(to: UInt8.self), mr.count) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return outputX.withUnsafeBytes { ox in
|
||||
internalX.withUnsafeBytes { ix in
|
||||
ufsecp_taproot_verify(ctx!,
|
||||
ox.baseAddress!.assumingMemoryBound(to: UInt8.self), parity,
|
||||
ix.baseAddress!.assumingMemoryBound(to: UInt8.self), nil, 0) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
private func alive() throws {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace secp256k1::fast {
|
||||
|
||||
@ -16,6 +18,29 @@ enum class SelftestMode : uint8_t {
|
||||
stress = 2 // ci + extended iterations, large random sweeps (~10-60 min)
|
||||
};
|
||||
|
||||
// -- Structured selftest result (for bindings / programmatic use) --
|
||||
struct SelftestCaseResult {
|
||||
std::string name; // e.g. "scalar_mul_KAT_10_vectors"
|
||||
bool passed; // true = PASS
|
||||
std::string detail; // empty on pass; failure description on fail
|
||||
};
|
||||
|
||||
struct SelftestReport {
|
||||
bool all_passed; // true if every case passed
|
||||
int total; // number of test cases run
|
||||
int passed; // number that passed
|
||||
std::string mode; // "smoke", "ci", or "stress"
|
||||
uint64_t seed; // PRNG seed used
|
||||
std::string platform; // e.g. "x86_64 clang-17"
|
||||
std::vector<SelftestCaseResult> cases; // per-test results
|
||||
|
||||
// Render as human-readable multi-line text
|
||||
std::string to_text() const;
|
||||
|
||||
// Render as JSON string
|
||||
std::string to_json() const;
|
||||
};
|
||||
|
||||
// Run comprehensive self-tests on the library
|
||||
// Returns true if all tests pass, false otherwise
|
||||
// Set verbose=true to see detailed test output
|
||||
@ -26,4 +51,10 @@ bool Selftest(bool verbose);
|
||||
// Prints repro bundle: commit, compiler, platform, seed, mode.
|
||||
bool Selftest(bool verbose, SelftestMode mode, uint64_t seed = 0);
|
||||
|
||||
// Run self-tests and return a structured report (no stdout output).
|
||||
// Suitable for bindings (Python, Rust, Node.js, etc.) that need
|
||||
// a programmatic result rather than console output.
|
||||
SelftestReport selftest_report(SelftestMode mode = SelftestMode::smoke,
|
||||
uint64_t seed = 0);
|
||||
|
||||
} // namespace secp256k1::fast
|
||||
|
||||
@ -1214,6 +1214,48 @@ static bool test_point_advanced(bool verbose) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Thread-local report collector for selftest_report()
|
||||
// When non-null, tally() appends case results into this report.
|
||||
// ---------------------------------------------------------------
|
||||
static thread_local SelftestReport* s_active_report = nullptr;
|
||||
|
||||
static inline void tally(int& total, int& passed,
|
||||
const char* name, bool ok) {
|
||||
total++;
|
||||
if (ok) passed++;
|
||||
if (s_active_report) {
|
||||
s_active_report->cases.push_back({name, ok, ok ? "" : "FAIL"});
|
||||
}
|
||||
}
|
||||
|
||||
// Platform string (compile-time)
|
||||
static const char* get_platform_string() {
|
||||
#if defined(_WIN64)
|
||||
return "Windows x64";
|
||||
#elif defined(_WIN32)
|
||||
return "Windows x86";
|
||||
#elif defined(__APPLE__) && defined(__aarch64__)
|
||||
return "macOS ARM64";
|
||||
#elif defined(__APPLE__)
|
||||
return "macOS x64";
|
||||
#elif defined(__linux__) && defined(__riscv)
|
||||
return "Linux RISC-V";
|
||||
#elif defined(__linux__) && defined(__aarch64__)
|
||||
return "Linux ARM64";
|
||||
#elif defined(__linux__)
|
||||
return "Linux x64";
|
||||
#elif defined(SECP256K1_PLATFORM_ESP32) || defined(ESP_PLATFORM)
|
||||
return "ESP32";
|
||||
#elif defined(SECP256K1_PLATFORM_STM32)
|
||||
return "STM32";
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
return "WASM";
|
||||
#else
|
||||
return "unknown";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Main self-test function (legacy API -- delegates to ci mode)
|
||||
bool Selftest(bool verbose) {
|
||||
return Selftest(verbose, SelftestMode::ci, 0);
|
||||
@ -1271,10 +1313,12 @@ bool Selftest(bool verbose, SelftestMode mode, uint64_t seed) {
|
||||
SELFTEST_PRINT("\nScalar Multiplication Tests:\n");
|
||||
}
|
||||
|
||||
for (const auto& vec : TEST_VECTORS) {
|
||||
total++;
|
||||
if (test_scalar_mul(vec, verbose)) {
|
||||
passed++;
|
||||
{
|
||||
int vi = 0;
|
||||
for (const auto& vec : TEST_VECTORS) {
|
||||
char vname[48];
|
||||
std::snprintf(vname, sizeof(vname), "scalar_mul_vector_%d", ++vi);
|
||||
tally(total, passed, vname, test_scalar_mul(vec, verbose));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1282,65 +1326,46 @@ bool Selftest(bool verbose, SelftestMode mode, uint64_t seed) {
|
||||
if (verbose) {
|
||||
SELFTEST_PRINT("\nPoint Addition Test:\n");
|
||||
}
|
||||
total++;
|
||||
if (test_addition(verbose)) {
|
||||
passed++;
|
||||
}
|
||||
tally(total, passed, "point_addition", test_addition(verbose));
|
||||
|
||||
// Test point subtraction
|
||||
if (verbose) {
|
||||
SELFTEST_PRINT("\nPoint Subtraction Test:\n");
|
||||
}
|
||||
total++;
|
||||
if (test_subtraction(verbose)) {
|
||||
passed++;
|
||||
}
|
||||
tally(total, passed, "point_subtraction", test_subtraction(verbose));
|
||||
|
||||
// Field arithmetic
|
||||
total++;
|
||||
if (test_field_arithmetic(verbose)) passed++;
|
||||
tally(total, passed, "field_arithmetic", test_field_arithmetic(verbose));
|
||||
|
||||
// Scalar arithmetic (basic identities)
|
||||
total++;
|
||||
if (test_scalar_arithmetic(verbose)) passed++;
|
||||
tally(total, passed, "scalar_arithmetic", test_scalar_arithmetic(verbose));
|
||||
|
||||
// Point group identities
|
||||
total++;
|
||||
if (test_point_identities(verbose)) passed++;
|
||||
tally(total, passed, "point_identities", test_point_identities(verbose));
|
||||
|
||||
// Point serialization
|
||||
total++;
|
||||
if (test_point_serialization(verbose)) passed++;
|
||||
tally(total, passed, "point_serialization", test_point_serialization(verbose));
|
||||
|
||||
// Batch inverse (small)
|
||||
total++;
|
||||
if (test_batch_inverse(verbose)) passed++;
|
||||
tally(total, passed, "batch_inverse", test_batch_inverse(verbose));
|
||||
|
||||
// Constant-expected point ops
|
||||
total++;
|
||||
if (test_addition_constants(verbose)) passed++;
|
||||
total++;
|
||||
if (test_subtraction_constants(verbose)) passed++;
|
||||
total++;
|
||||
if (test_doubling_constants(verbose)) passed++;
|
||||
total++;
|
||||
if (test_negation_constants(verbose)) passed++;
|
||||
tally(total, passed, "addition_constants", test_addition_constants(verbose));
|
||||
tally(total, passed, "subtraction_constants", test_subtraction_constants(verbose));
|
||||
tally(total, passed, "doubling_constants", test_doubling_constants(verbose));
|
||||
tally(total, passed, "negation_constants", test_negation_constants(verbose));
|
||||
|
||||
// Boundary scalar KAT (limb/order edges)
|
||||
total++;
|
||||
if (test_boundary_scalar_vectors(verbose)) passed++;
|
||||
tally(total, passed, "boundary_scalar_vectors", test_boundary_scalar_vectors(verbose));
|
||||
|
||||
// Field limb boundaries
|
||||
total++;
|
||||
if (test_field_limb_boundaries(verbose)) passed++;
|
||||
tally(total, passed, "field_limb_boundaries", test_field_limb_boundaries(verbose));
|
||||
|
||||
// Extended kG vectors (4G-9G, 15G, 255G)
|
||||
total++;
|
||||
if (test_extended_kg_vectors(verbose)) passed++;
|
||||
tally(total, passed, "extended_kg_vectors", test_extended_kg_vectors(verbose));
|
||||
|
||||
// Point advanced (comm/assoc/mixed/dist/edge)
|
||||
total++;
|
||||
if (test_point_advanced(verbose)) passed++;
|
||||
tally(total, passed, "point_advanced", test_point_advanced(verbose));
|
||||
|
||||
if (is_smoke) {
|
||||
// Smoke mode ends here
|
||||
@ -1363,8 +1388,7 @@ bool Selftest(bool verbose, SelftestMode mode, uint64_t seed) {
|
||||
// ===============================================================
|
||||
|
||||
// External vectors (optional, environment-driven)
|
||||
total++;
|
||||
if (run_external_vectors(verbose)) passed++;
|
||||
tally(total, passed, "external_vectors", run_external_vectors(verbose));
|
||||
|
||||
// Doubling chain vs scalar multiples: for i=1..20, (2^i)G via dbl() equals scalar_mul
|
||||
{
|
||||
@ -1378,7 +1402,7 @@ bool Selftest(bool verbose, SelftestMode mode, uint64_t seed) {
|
||||
if (!points_equal(cur, exp)) { ok = false; break; }
|
||||
}
|
||||
if (verbose) SELFTEST_PRINT(ok ? " PASS\n" : " FAIL\n");
|
||||
total++; if (ok) passed++;
|
||||
tally(total, passed, "doubling_chain_vs_scalar", ok);
|
||||
}
|
||||
|
||||
// Large scalar cross-checks (fast vs affine fallback)
|
||||
@ -1400,7 +1424,7 @@ bool Selftest(bool verbose, SelftestMode mode, uint64_t seed) {
|
||||
if (!points_equal(fast, ref)) { ok = false; break; }
|
||||
}
|
||||
if (verbose) SELFTEST_PRINT(ok ? " PASS\n" : " FAIL\n");
|
||||
total++; if (ok) passed++;
|
||||
tally(total, passed, "large_scalar_cross_checks", ok);
|
||||
}
|
||||
|
||||
// Squared scalar cases: k^2 * G
|
||||
@ -1426,42 +1450,42 @@ bool Selftest(bool verbose, SelftestMode mode, uint64_t seed) {
|
||||
if (!points_equal(fast, ref)) { ok = false; break; }
|
||||
}
|
||||
if (verbose) SELFTEST_PRINT(ok ? " PASS\n" : " FAIL\n");
|
||||
total++; if (ok) passed++;
|
||||
tally(total, passed, "squared_scalar_cases", ok);
|
||||
}
|
||||
|
||||
// Expanded batch inverse (32 elements)
|
||||
total++; if (test_batch_inverse_expanded(verbose)) passed++;
|
||||
tally(total, passed, "batch_inverse_expanded", test_batch_inverse_expanded(verbose));
|
||||
|
||||
// Bilinearity for K*Q with +/-G
|
||||
total++; if (test_bilinearity_K_times_Q(verbose)) passed++;
|
||||
tally(total, passed, "bilinearity_K_times_Q", test_bilinearity_K_times_Q(verbose));
|
||||
|
||||
#if !defined(SECP256K1_PLATFORM_ESP32) && !defined(ESP_PLATFORM) && !defined(IDF_VER) && !defined(SECP256K1_PLATFORM_STM32)
|
||||
// Batch inverse size sweep (21 sizes)
|
||||
total++; if (test_batch_inverse_sweep(verbose)) passed++;
|
||||
tally(total, passed, "batch_inverse_sweep", test_batch_inverse_sweep(verbose));
|
||||
#endif
|
||||
|
||||
#if !defined(SECP256K1_PLATFORM_ESP32) && !defined(ESP_PLATFORM) && !defined(IDF_VER) && !defined(SECP256K1_PLATFORM_STM32)
|
||||
// Fixed-K plan equivalence (GLV-based, not available on embedded)
|
||||
total++; if (test_fixedK_plan(verbose)) passed++;
|
||||
tally(total, passed, "fixedK_plan", test_fixedK_plan(verbose));
|
||||
#endif
|
||||
|
||||
// Sequential increment property
|
||||
total++; if (test_sequential_increment_property(verbose)) passed++;
|
||||
tally(total, passed, "sequential_increment_property", test_sequential_increment_property(verbose));
|
||||
|
||||
// Fast kG vs generic kG (small 1-20 + 20 random)
|
||||
total++; if (test_fast_vs_generic_kG(verbose)) passed++;
|
||||
tally(total, passed, "fast_vs_generic_kG", test_fast_vs_generic_kG(verbose));
|
||||
|
||||
// Repeated addition consistency (k=2..10)
|
||||
total++; if (test_repeated_addition_consistency(verbose)) passed++;
|
||||
tally(total, passed, "repeated_addition_consistency", test_repeated_addition_consistency(verbose));
|
||||
|
||||
// Field stress (normalization + random algebraic laws)
|
||||
total++; if (test_field_stress(verbose)) passed++;
|
||||
tally(total, passed, "field_stress", test_field_stress(verbose));
|
||||
|
||||
// Scalar stress ((n-1)^2=1 + random algebraic laws)
|
||||
total++; if (test_scalar_stress(verbose)) passed++;
|
||||
tally(total, passed, "scalar_stress", test_scalar_stress(verbose));
|
||||
|
||||
// NAF/wNAF encoding validation
|
||||
total++; if (test_naf_wnaf(verbose)) passed++;
|
||||
tally(total, passed, "naf_wnaf", test_naf_wnaf(verbose));
|
||||
|
||||
// ===============================================================
|
||||
// TIER 3: STRESS -- Extended iterations (~10-60 min)
|
||||
@ -1487,7 +1511,7 @@ bool Selftest(bool verbose, SelftestMode mode, uint64_t seed) {
|
||||
}
|
||||
}
|
||||
if (verbose) SELFTEST_PRINT(ok ? " PASS\n" : " FAIL\n");
|
||||
total++; if (ok) passed++;
|
||||
tally(total, passed, "stress_fast_vs_generic_kG_1000", ok);
|
||||
}
|
||||
|
||||
// Stress: extended field algebraic laws (500 random triples)
|
||||
@ -1510,7 +1534,7 @@ bool Selftest(bool verbose, SelftestMode mode, uint64_t seed) {
|
||||
if (!(sq == a * a)) ok = false;
|
||||
}
|
||||
if (verbose) SELFTEST_PRINT(ok ? " PASS\n" : " FAIL\n");
|
||||
total++; if (ok) passed++;
|
||||
tally(total, passed, "stress_field_algebraic_laws_500", ok);
|
||||
}
|
||||
|
||||
// Stress: extended bilinearity K*(Q+/-G) (100 random K,Q pairs)
|
||||
@ -1536,7 +1560,7 @@ bool Selftest(bool verbose, SelftestMode mode, uint64_t seed) {
|
||||
if (!points_equal(Lm, Rm)) { ok = false; break; }
|
||||
}
|
||||
if (verbose) SELFTEST_PRINT(ok ? " PASS\n" : " FAIL\n");
|
||||
total++; if (ok) passed++;
|
||||
tally(total, passed, "stress_bilinearity_100", ok);
|
||||
}
|
||||
|
||||
#if !defined(SECP256K1_PLATFORM_ESP32) && !defined(ESP_PLATFORM) && !defined(IDF_VER) && !defined(SECP256K1_PLATFORM_STM32)
|
||||
@ -1566,7 +1590,7 @@ bool Selftest(bool verbose, SelftestMode mode, uint64_t seed) {
|
||||
if (!ok) break;
|
||||
}
|
||||
if (verbose) SELFTEST_PRINT(ok ? " PASS\n" : " FAIL\n");
|
||||
total++; if (ok) passed++;
|
||||
tally(total, passed, "stress_batch_inverse_8192", ok);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -450,7 +450,7 @@ cmake -S . -B build-android -G Ninja \
|
||||
cmake --build build-android -j
|
||||
```
|
||||
|
||||
The library produces `libsecp256k1-fast-cpu.a` for linking into Android apps via JNI.
|
||||
The library produces `libfastsecp256k1.a` for linking into Android apps via JNI.
|
||||
|
||||
---
|
||||
|
||||
@ -575,7 +575,7 @@ cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ...
|
||||
|
||||
```cmake
|
||||
add_subdirectory(path/to/UltrafastSecp256k1)
|
||||
target_link_libraries(your_target PRIVATE secp256k1-fast-cpu)
|
||||
target_link_libraries(your_target PRIVATE secp256k1::fast)
|
||||
```
|
||||
|
||||
### After Installation
|
||||
@ -591,7 +591,7 @@ cmake --install build --prefix /opt/secp256k1
|
||||
```cmake
|
||||
# In your CMakeLists.txt
|
||||
find_package(secp256k1-fast REQUIRED)
|
||||
target_link_libraries(your_target PRIVATE secp256k1::fast-cpu)
|
||||
target_link_libraries(your_target PRIVATE secp256k1::fastsecp256k1)
|
||||
```
|
||||
|
||||
### pkg-config
|
||||
@ -632,7 +632,7 @@ echo "deb [signed-by=/etc/apt/keyrings/ultrafastsecp256k1.gpg] \
|
||||
| sudo tee /etc/apt/sources.list.d/ultrafastsecp256k1.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install libsecp256k1-fast-dev # headers + static + shared
|
||||
sudo apt install libufsecp-dev # headers + static + shared
|
||||
```
|
||||
|
||||
### Fedora / RHEL (RPM)
|
||||
@ -647,7 +647,7 @@ sudo dnf install ./UltrafastSecp256k1-*.rpm
|
||||
### Arch Linux (AUR)
|
||||
|
||||
```bash
|
||||
yay -S libsecp256k1-fast
|
||||
yay -S libufsecp
|
||||
```
|
||||
|
||||
### Docker (build from source)
|
||||
|
||||
@ -40,7 +40,7 @@ set(SECP256K1_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
FetchContent_MakeAvailable(secp256k1_fast)
|
||||
|
||||
add_executable(my_app main.cpp)
|
||||
target_link_libraries(my_app PRIVATE secp256k1_fast)
|
||||
target_link_libraries(my_app PRIVATE secp256k1::fast)
|
||||
```
|
||||
|
||||
---
|
||||
@ -60,7 +60,7 @@ set(SECP256K1_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
|
||||
add_subdirectory(third_party/secp256k1_fast)
|
||||
|
||||
target_link_libraries(my_app PRIVATE secp256k1_fast)
|
||||
target_link_libraries(my_app PRIVATE secp256k1::fast)
|
||||
```
|
||||
|
||||
---
|
||||
@ -80,7 +80,7 @@ To use from a vcpkg overlay port or after it's published:
|
||||
|
||||
```cmake
|
||||
find_package(secp256k1-fast CONFIG REQUIRED)
|
||||
target_link_libraries(my_app PRIVATE secp256k1-fast::secp256k1-fast)
|
||||
target_link_libraries(my_app PRIVATE secp256k1::fastsecp256k1)
|
||||
```
|
||||
|
||||
---
|
||||
@ -97,7 +97,7 @@ Then in your project:
|
||||
|
||||
```cmake
|
||||
find_package(secp256k1-fast 3.3 CONFIG REQUIRED)
|
||||
target_link_libraries(my_app PRIVATE secp256k1-fast::secp256k1-fast)
|
||||
target_link_libraries(my_app PRIVATE secp256k1::fastsecp256k1)
|
||||
```
|
||||
|
||||
Or via pkg-config:
|
||||
|
||||
@ -87,7 +87,7 @@ int main() {
|
||||
Compile and run:
|
||||
|
||||
```bash
|
||||
g++ -std=c++20 -O3 example.cpp -I build/cpu/include -L build/cpu -lsecp256k1-fast-cpu -o example
|
||||
g++ -std=c++20 -O3 example.cpp -I build/cpu/include -L build/cpu -lfastsecp256k1 -o example
|
||||
./example
|
||||
```
|
||||
|
||||
@ -97,14 +97,14 @@ g++ -std=c++20 -O3 example.cpp -I build/cpu/include -L build/cpu -lsecp256k1-fas
|
||||
|
||||
```cmake
|
||||
add_subdirectory(UltrafastSecp256k1)
|
||||
target_link_libraries(your_target PRIVATE secp256k1-fast-cpu)
|
||||
target_link_libraries(your_target PRIVATE secp256k1::fast)
|
||||
```
|
||||
|
||||
### As Installed Package
|
||||
|
||||
```cmake
|
||||
find_package(secp256k1-fast REQUIRED)
|
||||
target_link_libraries(your_target PRIVATE secp256k1::fast-cpu)
|
||||
target_link_libraries(your_target PRIVATE secp256k1::fastsecp256k1)
|
||||
```
|
||||
|
||||
### pkg-config
|
||||
|
||||
@ -19,8 +19,8 @@ cd build && cpack -G DEB
|
||||
```
|
||||
|
||||
Produces:
|
||||
- `libsecp256k1-fast3_<ver>_<arch>.deb` — shared library
|
||||
- `libsecp256k1-fast-dev_<ver>_<arch>.deb` — headers + static lib + cmake/pkgconfig
|
||||
- `libufsecp3_<ver>_<arch>.deb` — shared library
|
||||
- `libufsecp-dev_<ver>_<arch>.deb` — headers + static lib + cmake/pkgconfig
|
||||
|
||||
## Fedora / RHEL / CentOS (.rpm)
|
||||
|
||||
@ -29,7 +29,7 @@ Produces:
|
||||
sudo dnf install cmake ninja-build gcc-c++ rpm-build
|
||||
|
||||
# Build RPM from spec
|
||||
rpmbuild -ba packaging/rpm/libsecp256k1-fast.spec
|
||||
rpmbuild -ba packaging/rpm/libufsecp.spec
|
||||
# — or use CPack —
|
||||
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
||||
-DSECP256K1_BUILD_SHARED=ON -DSECP256K1_INSTALL=ON
|
||||
@ -65,13 +65,13 @@ sudo ldconfig
|
||||
```
|
||||
|
||||
After install, applications can find the library via:
|
||||
- **pkg-config**: `pkg-config --cflags --libs secp256k1-fast`
|
||||
- **CMake**: `find_package(secp256k1-fast 3 REQUIRED COMPONENTS CPU)`
|
||||
- **pkg-config**: `pkg-config --cflags --libs ufsecp`
|
||||
- **CMake**: `find_package(ufsecp 3 REQUIRED)`
|
||||
|
||||
## Package naming convention
|
||||
|
||||
| Distro | Runtime | Development |
|
||||
|--------|---------|-------------|
|
||||
| Debian/Ubuntu | `libsecp256k1-fast3` | `libsecp256k1-fast-dev` |
|
||||
| Fedora/RHEL | `libsecp256k1-fast` | `libsecp256k1-fast-devel` |
|
||||
| Arch | `libsecp256k1-fast` | (included in main package) |
|
||||
| Debian/Ubuntu | `libufsecp3` | `libufsecp-dev` |
|
||||
| Fedora/RHEL | `libufsecp` | `libufsecp-devel` |
|
||||
| Arch | `libufsecp` | (included in main package) |
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# Maintainer: shrec <shrec@users.noreply.github.com>
|
||||
pkgname=libsecp256k1-fast
|
||||
pkgname=libufsecp
|
||||
pkgver=3.12.1
|
||||
pkgrel=1
|
||||
pkgdesc="High-performance secp256k1 elliptic curve cryptography library"
|
||||
@ -8,7 +8,7 @@ url="https://github.com/shrec/UltrafastSecp256k1"
|
||||
license=('AGPL-3.0-or-later')
|
||||
depends=('gcc-libs')
|
||||
makedepends=('cmake' 'ninja' 'gcc')
|
||||
provides=('libsecp256k1-fast')
|
||||
provides=('libufsecp')
|
||||
source=("$url/archive/v$pkgver/UltrafastSecp256k1-$pkgver.tar.gz")
|
||||
sha256sums=('SKIP')
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
libsecp256k1-fast (3.12.1-1) unstable; urgency=medium
|
||||
libufsecp (3.12.1-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
* Security: bump wheel 0.45.1 -> 0.46.2 (CVE-2026-24049).
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
Source: libsecp256k1-fast
|
||||
Source: libufsecp
|
||||
Section: libs
|
||||
Priority: optional
|
||||
Maintainer: shrec <shrec@users.noreply.github.com>
|
||||
@ -13,7 +13,7 @@ Vcs-Git: https://github.com/shrec/UltrafastSecp256k1.git
|
||||
Vcs-Browser: https://github.com/shrec/UltrafastSecp256k1
|
||||
Rules-Requires-Root: no
|
||||
|
||||
Package: libsecp256k1-fast3
|
||||
Package: libufsecp3
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
@ -26,11 +26,11 @@ Description: High-performance secp256k1 elliptic curve cryptography library
|
||||
.
|
||||
This package contains the shared library.
|
||||
|
||||
Package: libsecp256k1-fast-dev
|
||||
Package: libufsecp-dev
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: libdevel
|
||||
Depends: libsecp256k1-fast3 (= ${binary:Version}), ${misc:Depends}
|
||||
Depends: libufsecp3 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: High-performance secp256k1 library — development files
|
||||
UltrafastSecp256k1 is a high-performance implementation of the secp256k1
|
||||
elliptic curve used by Bitcoin, Ethereum, and other cryptocurrencies.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
%global soversion 3
|
||||
|
||||
Name: libsecp256k1-fast
|
||||
Name: libufsecp
|
||||
Version: 3.12.1
|
||||
Release: 1%{?dist}
|
||||
Summary: High-performance secp256k1 elliptic curve cryptography library
|
||||
@ -8,5 +8,5 @@ Description: High-performance secp256k1 elliptic curve cryptography library
|
||||
URL: @PROJECT_HOMEPAGE_URL@
|
||||
Version: @PROJECT_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -lsecp256k1-fast-cpu
|
||||
Libs: -L${libdir} -lfastsecp256k1
|
||||
Libs.private: -lpthread
|
||||
|
||||
Loading…
Reference in New Issue
Block a user