2364 lines
74 KiB
Markdown
2364 lines
74 KiB
Markdown
# UltrafastSecp256k1 API Reference
|
|
|
|
Complete API documentation for CPU, CUDA, and WASM implementations.
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [CPU API](#cpu-api)
|
|
- [FieldElement](#fieldelement)
|
|
- [Scalar](#scalar)
|
|
- [Point](#point)
|
|
- [ECDSA (RFC 6979)](#ecdsa-rfc-6979)
|
|
- [Schnorr (BIP-340)](#schnorr-bip-340)
|
|
- [SHA-256](#sha-256)
|
|
- [Constant-Time Layer](#constant-time-layer)
|
|
- [Utility Functions](#utility-functions)
|
|
2. [Address Generation API](#address-generation-api)
|
|
- [Address Types](#address-types)
|
|
- [Encoding Functions](#encoding-functions)
|
|
- [WIF (Wallet Import Format)](#wif-wallet-import-format)
|
|
- [BIP-352 Silent Payments](#bip-352-silent-payments)
|
|
3. [Multi-Chain Coins API](#multi-chain-coins-api)
|
|
- [Coin Parameters](#coin-parameters)
|
|
- [Coin Address Functions](#coin-address-functions)
|
|
- [Coin Key Generation](#coin-key-generation)
|
|
4. [Unified Wallet API](#unified-wallet-api)
|
|
- [Wallet Key Management](#wallet-key-management)
|
|
- [Wallet Address Generation](#wallet-address-generation)
|
|
- [Wallet Signing](#wallet-signing)
|
|
- [Wallet Recovery](#wallet-recovery)
|
|
5. [BIP-39 Mnemonic Seed Phrases](#bip-39-mnemonic-seed-phrases)
|
|
6. [Message Signing API](#message-signing-api)
|
|
7. [Zero-Knowledge Proof API](#zero-knowledge-proof-api)
|
|
- [Knowledge Proof (Schnorr Sigma)](#knowledge-proof-schnorr-sigma)
|
|
- [DLEQ Proof (Discrete Log Equality)](#dleq-proof-discrete-log-equality)
|
|
- [Bulletproof Range Proof](#bulletproof-range-proof)
|
|
- [Batch Operations](#zk-batch-operations)
|
|
8. [CUDA API](#cuda-api)
|
|
- [Data Structures](#cuda-data-structures)
|
|
- [Field Operations](#cuda-field-operations)
|
|
- [Point Operations](#cuda-point-operations)
|
|
- [Batch Operations](#cuda-batch-operations)
|
|
- [Signature Operations](#cuda-signature-operations)
|
|
9. [WASM API](#wasm-api)
|
|
10. [C ABI (ufsecp)](#c-abi-ufsecp)
|
|
- [Context Lifecycle](#c-abi-context-lifecycle)
|
|
- [Private Key Operations](#c-abi-private-key-operations)
|
|
- [Public Key Operations](#c-abi-public-key-operations)
|
|
- [ECDSA](#c-abi-ecdsa)
|
|
- [Schnorr / BIP-340](#c-abi-schnorr)
|
|
- [ECDH](#c-abi-ecdh)
|
|
- [Hashing](#c-abi-hashing)
|
|
- [Addresses and WIF](#c-abi-addresses)
|
|
- [BIP-32 HD Keys](#c-abi-bip32)
|
|
- [Taproot / BIP-341](#c-abi-taproot)
|
|
- [BIP-39 Mnemonics](#c-abi-bip39)
|
|
- [Batch Verification](#c-abi-batch)
|
|
- [Multi-Scalar Multiplication](#c-abi-msm)
|
|
- [MuSig2 / BIP-327](#c-abi-musig2)
|
|
- [FROST Threshold Signatures](#c-abi-frost)
|
|
- [Adaptor Signatures](#c-abi-adaptor)
|
|
- [Pedersen Commitments](#c-abi-pedersen)
|
|
- [Zero-Knowledge Proofs](#c-abi-zk)
|
|
- [Multi-Coin Wallet](#c-abi-multicoin)
|
|
- [Ethereum](#c-abi-ethereum)
|
|
11. [Performance Tips](#performance-tips)
|
|
12. [Examples](#examples)
|
|
|
|
---
|
|
|
|
## CPU API
|
|
|
|
**Namespace:** `secp256k1::fast`
|
|
|
|
**Headers:**
|
|
```cpp
|
|
#include <secp256k1/field.hpp>
|
|
#include <secp256k1/scalar.hpp>
|
|
#include <secp256k1/point.hpp>
|
|
```
|
|
|
|
---
|
|
|
|
### FieldElement
|
|
|
|
256-bit field element for secp256k1 curve (mod p where p = 2^256 - 2^32 - 977).
|
|
|
|
#### Construction
|
|
|
|
```cpp
|
|
// Zero element
|
|
FieldElement a = FieldElement::zero();
|
|
|
|
// One element
|
|
FieldElement b = FieldElement::one();
|
|
|
|
// From 64-bit integer
|
|
FieldElement c = FieldElement::from_uint64(12345);
|
|
|
|
// From 4 x 64-bit limbs (little-endian, RECOMMENDED for binary I/O)
|
|
std::array<uint64_t, 4> limbs = {0x123, 0x456, 0x789, 0xABC};
|
|
FieldElement d = FieldElement::from_limbs(limbs);
|
|
|
|
// From 32 bytes (big-endian, for hex/test vectors only)
|
|
std::array<uint8_t, 32> bytes = {...};
|
|
FieldElement e = FieldElement::from_bytes(bytes);
|
|
|
|
// From hex string (developer-friendly)
|
|
FieldElement f = FieldElement::from_hex(
|
|
"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
|
|
);
|
|
```
|
|
|
|
#### Arithmetic Operations
|
|
|
|
```cpp
|
|
FieldElement a, b;
|
|
|
|
// Basic arithmetic (immutable, returns new object)
|
|
FieldElement sum = a + b;
|
|
FieldElement diff = a - b;
|
|
FieldElement prod = a * b;
|
|
FieldElement sq = a.square();
|
|
FieldElement inv = a.inverse();
|
|
|
|
// In-place arithmetic (mutable, ~10-15% faster)
|
|
a += b;
|
|
a -= b;
|
|
a *= b;
|
|
a.square_inplace(); // a = a^2
|
|
a.inverse_inplace(); // a = a^-¹
|
|
```
|
|
|
|
#### Serialization
|
|
|
|
```cpp
|
|
FieldElement a;
|
|
|
|
// To bytes (big-endian)
|
|
std::array<uint8_t, 32> bytes = a.to_bytes();
|
|
|
|
// To bytes into existing buffer (no allocation)
|
|
uint8_t buffer[32];
|
|
a.to_bytes_into(buffer);
|
|
|
|
// To hex string
|
|
std::string hex = a.to_hex();
|
|
|
|
// Access raw limbs (little-endian)
|
|
const auto& limbs = a.limbs(); // std::array<uint64_t, 4>
|
|
```
|
|
|
|
#### Comparison
|
|
|
|
```cpp
|
|
FieldElement a, b;
|
|
if (a == b) { ... }
|
|
if (a != b) { ... }
|
|
```
|
|
|
|
---
|
|
|
|
### Scalar
|
|
|
|
256-bit scalar for secp256k1 curve (mod n where n is the group order).
|
|
|
|
#### Construction
|
|
|
|
```cpp
|
|
// Zero
|
|
Scalar a = Scalar::zero();
|
|
|
|
// One
|
|
Scalar b = Scalar::one();
|
|
|
|
// From 64-bit integer
|
|
Scalar c = Scalar::from_uint64(12345);
|
|
|
|
// From limbs (little-endian)
|
|
std::array<uint64_t, 4> limbs = {...};
|
|
Scalar d = Scalar::from_limbs(limbs);
|
|
|
|
// From bytes (big-endian)
|
|
std::array<uint8_t, 32> bytes = {...};
|
|
Scalar e = Scalar::from_bytes(bytes);
|
|
|
|
// From hex string
|
|
Scalar f = Scalar::from_hex(
|
|
"E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33262"
|
|
);
|
|
```
|
|
|
|
#### Arithmetic Operations
|
|
|
|
```cpp
|
|
Scalar a, b;
|
|
|
|
// Basic arithmetic
|
|
Scalar sum = a + b;
|
|
Scalar diff = a - b;
|
|
Scalar prod = a * b;
|
|
|
|
// In-place
|
|
a += b;
|
|
a -= b;
|
|
a *= b;
|
|
```
|
|
|
|
#### Utility Methods
|
|
|
|
```cpp
|
|
Scalar s;
|
|
|
|
// Check if zero
|
|
bool isZero = s.is_zero();
|
|
|
|
// Get specific bit
|
|
uint8_t bit = s.bit(index); // 0 or 1
|
|
|
|
// NAF encoding (Non-Adjacent Form)
|
|
std::vector<int8_t> naf = s.to_naf();
|
|
|
|
// wNAF encoding (width-w NAF)
|
|
std::vector<int8_t> wnaf = s.to_wnaf(4); // width = 4
|
|
```
|
|
|
|
---
|
|
|
|
### Point
|
|
|
|
Elliptic curve point on secp256k1 (internally Jacobian coordinates).
|
|
|
|
#### Construction
|
|
|
|
```cpp
|
|
// Generator point G
|
|
Point G = Point::generator();
|
|
|
|
// Point at infinity (identity)
|
|
Point inf = Point::infinity();
|
|
|
|
// From affine coordinates
|
|
FieldElement x = FieldElement::from_hex("...");
|
|
FieldElement y = FieldElement::from_hex("...");
|
|
Point p = Point::from_affine(x, y);
|
|
|
|
// From hex strings (developer-friendly)
|
|
Point p2 = Point::from_hex(
|
|
"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
|
|
"483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"
|
|
);
|
|
```
|
|
|
|
#### Point Operations
|
|
|
|
```cpp
|
|
Point p, q;
|
|
Scalar k;
|
|
|
|
// Addition and doubling
|
|
Point sum = p.add(q); // p + q
|
|
Point doubled = p.dbl(); // 2p
|
|
|
|
// Scalar multiplication
|
|
Point result = p.scalar_mul(k); // k * p
|
|
|
|
// Negation
|
|
Point neg = p.negate(); // -p
|
|
```
|
|
|
|
#### Optimized Scalar Multiplication
|
|
|
|
```cpp
|
|
// For fixed K x variable Q pattern (same K, different Q points):
|
|
Scalar K = Scalar::from_hex("...");
|
|
KPlan plan = KPlan::from_scalar(K); // Precompute once
|
|
|
|
// Then for each Q:
|
|
Point Q1 = Point::from_hex("...", "...");
|
|
Point Q2 = Point::from_hex("...", "...");
|
|
|
|
Point R1 = Q1.scalar_mul_with_plan(plan); // Fastest!
|
|
Point R2 = Q2.scalar_mul_with_plan(plan);
|
|
```
|
|
|
|
#### In-Place Operations (Fastest)
|
|
|
|
```cpp
|
|
Point p;
|
|
|
|
// Increment/decrement by generator
|
|
p.next_inplace(); // p += G
|
|
p.prev_inplace(); // p -= G
|
|
|
|
// In-place arithmetic
|
|
p.add_inplace(q); // p += q
|
|
p.sub_inplace(q); // p -= q
|
|
p.dbl_inplace(); // p = 2p
|
|
p.negate_inplace(); // p = -p
|
|
|
|
// Mixed addition (when q is affine, z=1)
|
|
FieldElement qx, qy;
|
|
p.add_mixed_inplace(qx, qy); // Branchless, ~12% faster
|
|
```
|
|
|
|
#### Serialization
|
|
|
|
```cpp
|
|
Point p;
|
|
|
|
// Get affine coordinates
|
|
FieldElement x = p.x();
|
|
FieldElement y = p.y();
|
|
|
|
// Compressed format (33 bytes: 0x02/0x03 + x)
|
|
std::array<uint8_t, 33> compressed = p.to_compressed();
|
|
|
|
// Uncompressed format (65 bytes: 0x04 + x + y)
|
|
std::array<uint8_t, 65> uncompressed = p.to_uncompressed();
|
|
|
|
// Split x-coordinate for database lookups
|
|
std::array<uint8_t, 16> first_half = p.x_first_half();
|
|
std::array<uint8_t, 16> second_half = p.x_second_half();
|
|
```
|
|
|
|
#### Properties
|
|
|
|
```cpp
|
|
Point p;
|
|
|
|
// Check if point at infinity
|
|
bool isInf = p.is_infinity();
|
|
|
|
// Direct Jacobian coordinate access
|
|
const FieldElement& X = p.X(); // Jacobian X
|
|
const FieldElement& Y = p.Y(); // Jacobian Y
|
|
const FieldElement& Z = p.z(); // Jacobian Z
|
|
```
|
|
|
|
---
|
|
|
|
### Utility Functions
|
|
|
|
#### Self-Test
|
|
|
|
```cpp
|
|
#include <secp256k1/point.hpp>
|
|
|
|
// Run correctness tests
|
|
bool passed = secp256k1::fast::Selftest(true); // verbose=true
|
|
if (!passed) {
|
|
std::cerr << "Self-test failed!" << std::endl;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### ECDSA (RFC 6979)
|
|
|
|
**Namespace:** `secp256k1`
|
|
|
|
**Header:**
|
|
```cpp
|
|
#include <secp256k1/ecdsa.hpp>
|
|
```
|
|
|
|
#### ECDSASignature
|
|
|
|
```cpp
|
|
struct ECDSASignature {
|
|
fast::Scalar r;
|
|
fast::Scalar s;
|
|
|
|
// DER encoding (variable length, max 72 bytes)
|
|
std::pair<std::array<uint8_t, 72>, std::size_t> to_der() const;
|
|
|
|
// Compact 64-byte encoding: r (32 bytes) || s (32 bytes)
|
|
std::array<uint8_t, 64> to_compact() const;
|
|
|
|
// Decode from compact
|
|
static ECDSASignature from_compact(const std::array<uint8_t, 64>& data);
|
|
|
|
// Normalize to low-S (BIP-62)
|
|
ECDSASignature normalize() const;
|
|
|
|
// Check low-S
|
|
bool is_low_s() const;
|
|
};
|
|
```
|
|
|
|
#### Signing
|
|
|
|
```cpp
|
|
// Sign a 32-byte message hash with RFC 6979 deterministic nonce.
|
|
// Returns normalized (low-S) signature. Returns {0,0} on failure.
|
|
ECDSASignature ecdsa_sign(
|
|
const std::array<uint8_t, 32>& msg_hash,
|
|
const fast::Scalar& private_key
|
|
);
|
|
```
|
|
|
|
#### Verification
|
|
|
|
```cpp
|
|
// Verify ECDSA signature. Accepts both low-S and high-S.
|
|
bool ecdsa_verify(
|
|
const std::array<uint8_t, 32>& msg_hash,
|
|
const fast::Point& public_key,
|
|
const ECDSASignature& sig
|
|
);
|
|
```
|
|
|
|
#### RFC 6979 Nonce
|
|
|
|
```cpp
|
|
// Deterministic nonce generation per RFC 6979.
|
|
fast::Scalar rfc6979_nonce(
|
|
const fast::Scalar& private_key,
|
|
const std::array<uint8_t, 32>& msg_hash
|
|
);
|
|
```
|
|
|
|
#### Example
|
|
|
|
```cpp
|
|
#include <secp256k1/ecdsa.hpp>
|
|
#include <secp256k1/sha256.hpp>
|
|
#include <secp256k1/point.hpp>
|
|
|
|
using namespace secp256k1;
|
|
|
|
auto msg_hash = SHA256::hash("Hello ECDSA", 11);
|
|
fast::Scalar sk = fast::Scalar::from_hex("...");
|
|
fast::Point pk = fast::Point::generator().scalar_mul(sk);
|
|
|
|
// Sign
|
|
auto sig = ecdsa_sign(msg_hash, sk);
|
|
|
|
// Verify
|
|
bool ok = ecdsa_verify(msg_hash, pk, sig);
|
|
|
|
// Compact encoding (64 bytes)
|
|
auto compact = sig.to_compact();
|
|
auto recovered = ECDSASignature::from_compact(compact);
|
|
```
|
|
|
|
---
|
|
|
|
### Schnorr (BIP-340)
|
|
|
|
**Namespace:** `secp256k1`
|
|
|
|
**Header:**
|
|
```cpp
|
|
#include <secp256k1/schnorr.hpp>
|
|
```
|
|
|
|
#### SchnorrSignature
|
|
|
|
```cpp
|
|
struct SchnorrSignature {
|
|
std::array<uint8_t, 32> r; // R.x (nonce point x-coordinate)
|
|
fast::Scalar s; // scalar s
|
|
|
|
// 64-byte encoding: r (32) || s (32)
|
|
std::array<uint8_t, 64> to_bytes() const;
|
|
static SchnorrSignature from_bytes(const std::array<uint8_t, 64>& data);
|
|
};
|
|
```
|
|
|
|
#### Signing
|
|
|
|
```cpp
|
|
// BIP-340 Schnorr sign.
|
|
// aux_rand: 32 bytes of auxiliary randomness (use zeros for deterministic).
|
|
SchnorrSignature schnorr_sign(
|
|
const fast::Scalar& private_key,
|
|
const std::array<uint8_t, 32>& msg,
|
|
const std::array<uint8_t, 32>& aux_rand
|
|
);
|
|
```
|
|
|
|
#### Verification
|
|
|
|
```cpp
|
|
// BIP-340 Schnorr verify with x-only public key.
|
|
bool schnorr_verify(
|
|
const std::array<uint8_t, 32>& pubkey_x,
|
|
const std::array<uint8_t, 32>& msg,
|
|
const SchnorrSignature& sig
|
|
);
|
|
```
|
|
|
|
#### Utilities
|
|
|
|
```cpp
|
|
// X-only public key (BIP-340: negate if Y is odd)
|
|
std::array<uint8_t, 32> schnorr_pubkey(const fast::Scalar& private_key);
|
|
|
|
// Tagged hash: H_tag(msg) = SHA256(SHA256(tag) || SHA256(tag) || msg)
|
|
std::array<uint8_t, 32> tagged_hash(
|
|
const char* tag, const void* data, std::size_t len
|
|
);
|
|
```
|
|
|
|
#### Example
|
|
|
|
```cpp
|
|
#include <secp256k1/schnorr.hpp>
|
|
|
|
using namespace secp256k1;
|
|
|
|
fast::Scalar sk = fast::Scalar::from_hex("...");
|
|
auto pk_x = schnorr_pubkey(sk);
|
|
|
|
std::array<uint8_t, 32> msg = { /* message hash */ };
|
|
std::array<uint8_t, 32> aux = {}; // zeros for deterministic
|
|
|
|
auto sig = schnorr_sign(sk, msg, aux);
|
|
bool ok = schnorr_verify(pk_x, msg, sig);
|
|
```
|
|
|
|
---
|
|
|
|
### SHA-256
|
|
|
|
**Namespace:** `secp256k1`
|
|
|
|
**Header:**
|
|
```cpp
|
|
#include <secp256k1/sha256.hpp>
|
|
```
|
|
|
|
#### One-shot Hashing
|
|
|
|
```cpp
|
|
// SHA-256
|
|
SHA256::digest_type SHA256::hash(const void* data, std::size_t len);
|
|
|
|
// Double-SHA256: SHA256(SHA256(data))
|
|
SHA256::digest_type SHA256::hash256(const void* data, std::size_t len);
|
|
```
|
|
|
|
#### Streaming API
|
|
|
|
```cpp
|
|
secp256k1::SHA256 ctx;
|
|
ctx.update("part1", 5);
|
|
ctx.update("part2", 5);
|
|
auto digest = ctx.finalize();
|
|
|
|
// Reuse
|
|
ctx.reset();
|
|
ctx.update("new data", 8);
|
|
auto digest2 = ctx.finalize();
|
|
```
|
|
|
|
#### Example
|
|
|
|
```cpp
|
|
#include <secp256k1/sha256.hpp>
|
|
|
|
auto hash = secp256k1::SHA256::hash("Hello, world!", 13);
|
|
// hash is std::array<uint8_t, 32>
|
|
|
|
// Double-SHA256 (Bitcoin's hash)
|
|
auto hash256 = secp256k1::SHA256::hash256("tx_data", 7);
|
|
```
|
|
|
|
---
|
|
|
|
### Constant-Time Layer
|
|
|
|
**Namespace:** `secp256k1::fast::ct`
|
|
|
|
**Headers:**
|
|
```cpp
|
|
#include <secp256k1/ct/field.hpp>
|
|
#include <secp256k1/ct/scalar.hpp>
|
|
#include <secp256k1/ct/point.hpp>
|
|
#include <secp256k1/ct/ops.hpp>
|
|
```
|
|
|
|
The CT layer provides side-channel resistant variants of critical operations:
|
|
|
|
```cpp
|
|
namespace secp256k1::fast::ct {
|
|
|
|
// Constant-time field operations
|
|
void field_mul(const FieldElement& a, const FieldElement& b, FieldElement& out);
|
|
void field_inv(const FieldElement& a, FieldElement& out);
|
|
|
|
// Constant-time scalar multiplication (branchless double-and-add)
|
|
void scalar_mul(const Point& base, const Scalar& k, Point& out);
|
|
|
|
// Complete addition formula (handles all edge cases without branching)
|
|
void point_add_complete(const Point& p, const Point& q, Point& out);
|
|
|
|
// Constant-time point doubling
|
|
void point_dbl(const Point& p, Point& out);
|
|
|
|
} // namespace secp256k1::fast::ct
|
|
```
|
|
|
|
> [!] CT operations are ~5-7x slower than the fast variants. Use only for private key operations (signing, ECDH).
|
|
|
|
---
|
|
|
|
## Address Generation API
|
|
|
|
**Namespace:** `secp256k1`
|
|
|
|
**Header:**
|
|
```cpp
|
|
#include <secp256k1/address.hpp>
|
|
```
|
|
|
|
### Address Types
|
|
|
|
All address functions take a `Network` enum (`Mainnet` or `Testnet`) and return a `std::string`.
|
|
|
|
#### P2PKH (Legacy, Base58Check)
|
|
|
|
```cpp
|
|
// "1..." (mainnet) or "m/n..." (testnet)
|
|
std::string address_p2pkh(const fast::Point& pubkey,
|
|
Network net = Network::Mainnet);
|
|
```
|
|
|
|
#### P2WPKH (Native SegWit v0, Bech32)
|
|
|
|
```cpp
|
|
// "bc1q..." (mainnet) or "tb1q..." (testnet)
|
|
std::string address_p2wpkh(const fast::Point& pubkey,
|
|
Network net = Network::Mainnet);
|
|
```
|
|
|
|
#### P2TR (Taproot, SegWit v1, Bech32m)
|
|
|
|
```cpp
|
|
// "bc1p..." (mainnet) or "tb1p..." (testnet)
|
|
std::string address_p2tr(const fast::Point& internal_key,
|
|
Network net = Network::Mainnet);
|
|
|
|
// From raw 32-byte x-only output key
|
|
std::string address_p2tr_raw(const std::array<uint8_t, 32>& output_key_x,
|
|
Network net = Network::Mainnet);
|
|
```
|
|
|
|
#### P2SH-P2WPKH (Nested/Wrapped SegWit)
|
|
|
|
```cpp
|
|
// "3..." (mainnet) -- wraps P2WPKH inside P2SH for backward compatibility (BIP-49)
|
|
std::string address_p2sh_p2wpkh(const fast::Point& pubkey,
|
|
Network net = Network::Mainnet);
|
|
```
|
|
|
|
#### P2SH (Pay-to-Script-Hash)
|
|
|
|
```cpp
|
|
// "3..." (mainnet) -- from raw 20-byte script hash
|
|
std::string address_p2sh(const std::array<uint8_t, 20>& script_hash,
|
|
Network net = Network::Mainnet);
|
|
```
|
|
|
|
#### P2WSH (Witness Script Hash)
|
|
|
|
```cpp
|
|
// "bc1q..." 32-byte program (SegWit v0)
|
|
std::string address_p2wsh(const std::array<uint8_t, 32>& witness_script_hash,
|
|
Network net = Network::Mainnet);
|
|
```
|
|
|
|
#### CashAddr (Bitcoin Cash BIP-0185)
|
|
|
|
```cpp
|
|
// Encode hash160 as CashAddr (type: 0=P2PKH, 1=P2SH)
|
|
std::string cashaddr_encode(const std::array<uint8_t, 20>& hash,
|
|
const std::string& prefix,
|
|
uint8_t type = 0);
|
|
|
|
// CashAddr P2PKH from public key: "bitcoincash:q..."
|
|
std::string address_cashaddr(const fast::Point& pubkey,
|
|
const std::string& prefix = "bitcoincash");
|
|
```
|
|
|
|
### Encoding Functions
|
|
|
|
#### Base58Check
|
|
|
|
```cpp
|
|
std::string base58check_encode(const uint8_t* data, size_t len);
|
|
std::pair<std::vector<uint8_t>, bool> base58check_decode(const std::string& encoded);
|
|
```
|
|
|
|
#### Bech32 / Bech32m (BIP-173 / BIP-350)
|
|
|
|
```cpp
|
|
std::string bech32_encode(const std::string& hrp,
|
|
uint8_t witness_version,
|
|
const uint8_t* witness_program,
|
|
size_t prog_len);
|
|
|
|
struct Bech32DecodeResult {
|
|
std::string hrp;
|
|
int witness_version; // -1 if invalid
|
|
std::vector<uint8_t> witness_program;
|
|
bool valid;
|
|
};
|
|
Bech32DecodeResult bech32_decode(const std::string& addr);
|
|
```
|
|
|
|
#### HASH160
|
|
|
|
```cpp
|
|
// RIPEMD160(SHA256(data))
|
|
std::array<uint8_t, 20> hash160(const uint8_t* data, size_t len);
|
|
```
|
|
|
|
### WIF (Wallet Import Format)
|
|
|
|
```cpp
|
|
std::string wif_encode(const fast::Scalar& private_key,
|
|
bool compressed = true,
|
|
Network net = Network::Mainnet);
|
|
|
|
struct WIFDecodeResult {
|
|
fast::Scalar key;
|
|
bool compressed;
|
|
Network network;
|
|
bool valid;
|
|
};
|
|
WIFDecodeResult wif_decode(const std::string& wif);
|
|
```
|
|
|
|
### BIP-352 Silent Payments
|
|
|
|
```cpp
|
|
struct SilentPaymentAddress {
|
|
fast::Point scan_pubkey; // B_scan
|
|
fast::Point spend_pubkey; // B_spend
|
|
std::string encode(Network net = Network::Mainnet) const; // sp1q... or tsp1q...
|
|
};
|
|
|
|
// Generate silent payment address from scan/spend private keys
|
|
SilentPaymentAddress silent_payment_address(const fast::Scalar& scan_privkey,
|
|
const fast::Scalar& spend_privkey);
|
|
|
|
// Sender: compute unique output key for recipient
|
|
std::pair<fast::Point, fast::Scalar>
|
|
silent_payment_create_output(const std::vector<fast::Scalar>& input_privkeys,
|
|
const SilentPaymentAddress& recipient,
|
|
uint32_t k = 0);
|
|
|
|
// Receiver: scan transaction to detect addressed outputs
|
|
std::vector<std::pair<uint32_t, fast::Scalar>>
|
|
silent_payment_scan(const fast::Scalar& scan_privkey,
|
|
const fast::Scalar& spend_privkey,
|
|
const std::vector<fast::Point>& input_pubkeys,
|
|
const std::vector<std::array<uint8_t, 32>>& output_pubkeys);
|
|
```
|
|
|
|
---
|
|
|
|
## Multi-Chain Coins API
|
|
|
|
**Namespace:** `secp256k1::coins`
|
|
|
|
**Headers:**
|
|
```cpp
|
|
#include <secp256k1/coins/coin_params.hpp>
|
|
#include <secp256k1/coins/coin_address.hpp>
|
|
```
|
|
|
|
### Coin Parameters
|
|
|
|
28 coins predefined as `constexpr CoinParams` objects:
|
|
|
|
| Coin | Ticker | Encoding | BIP-44 | Chain ID |
|
|
|------|--------|----------|--------|----------|
|
|
| Bitcoin | BTC | Bech32 | 0 | -- |
|
|
| Litecoin | LTC | Bech32 | 2 | -- |
|
|
| Dogecoin | DOGE | Base58Check | 3 | -- |
|
|
| Dash | DASH | Base58Check | 5 | -- |
|
|
| Ethereum | ETH | EIP-55 | 60 | 1 |
|
|
| Bitcoin Cash | BCH | CashAddr | 145 | -- |
|
|
| Bitcoin SV | BSV | Base58Check | 236 | -- |
|
|
| Zcash | ZEC | Base58Check | 133 | -- |
|
|
| DigiByte | DGB | Bech32 | 20 | -- |
|
|
| Namecoin | NMC | Base58Check | 7 | -- |
|
|
| Peercoin | PPC | Base58Check | 6 | -- |
|
|
| Vertcoin | VTC | Bech32 | 28 | -- |
|
|
| Viacoin | VIA | Bech32 | 14 | -- |
|
|
| Groestlcoin | GRS | Bech32 | 17 | -- |
|
|
| Syscoin | SYS | Bech32 | 57 | -- |
|
|
| BNB Smart Chain | BNB | EIP-55 | 60 | 56 |
|
|
| Polygon | POL | EIP-55 | 60 | 137 |
|
|
| Avalanche | AVAX | EIP-55 | 60 | 43114 |
|
|
| Fantom | FTM | EIP-55 | 60 | 250 |
|
|
| Arbitrum | ARB | EIP-55 | 60 | 42161 |
|
|
| Optimism | OP | EIP-55 | 60 | 10 |
|
|
| Ravencoin | RVN | Base58Check | 175 | -- |
|
|
| Flux | FLUX | Base58Check | 19167 | -- |
|
|
| Qtum | QTUM | Base58Check | 2301 | -- |
|
|
| Horizen | ZEN | Base58Check | 121 | -- |
|
|
| Bitcoin Gold | BTG | Base58Check | 156 | -- |
|
|
| Komodo | KMD | Base58Check | 141 | -- |
|
|
| Tron | TRX | TRON_BASE58 | 195 | -- |
|
|
|
|
#### Lookup Functions
|
|
|
|
```cpp
|
|
const CoinParams* find_by_coin_type(uint32_t coin_type); // Returns nullptr if not found
|
|
const CoinParams* find_by_ticker(const char* ticker); // Case-sensitive
|
|
```
|
|
|
|
#### Iteration
|
|
|
|
```cpp
|
|
inline constexpr const CoinParams* ALL_COINS[]; // Array of all 28 coins
|
|
inline constexpr size_t ALL_COINS_COUNT = 28;
|
|
```
|
|
|
|
### Coin Address Functions
|
|
|
|
```cpp
|
|
// Default/preferred format for each coin (Bech32 for BTC, EIP-55 for ETH, etc.)
|
|
std::string coin_address(const fast::Point& pubkey, const CoinParams& coin,
|
|
bool testnet = false);
|
|
|
|
// Explicit format selection
|
|
std::string coin_address_p2pkh(const fast::Point& pubkey, const CoinParams& coin,
|
|
bool testnet = false);
|
|
std::string coin_address_p2wpkh(const fast::Point& pubkey, const CoinParams& coin,
|
|
bool testnet = false);
|
|
std::string coin_address_p2tr(const fast::Point& internal_key, const CoinParams& coin,
|
|
bool testnet = false);
|
|
std::string coin_address_p2sh_p2wpkh(const fast::Point& pubkey, const CoinParams& coin,
|
|
bool testnet = false);
|
|
std::string coin_address_p2sh(const std::array<uint8_t, 20>& script_hash,
|
|
const CoinParams& coin, bool testnet = false);
|
|
std::string coin_address_cashaddr(const fast::Point& pubkey, const CoinParams& coin,
|
|
bool testnet = false);
|
|
|
|
// WIF encoding with coin-specific prefix
|
|
std::string coin_wif_encode(const fast::Scalar& private_key, const CoinParams& coin,
|
|
bool compressed = true, bool testnet = false);
|
|
```
|
|
|
|
> Functions return empty string if the coin does not support the requested format (e.g., `coin_address_p2wpkh` for Dogecoin).
|
|
|
|
### Coin Key Generation
|
|
|
|
```cpp
|
|
struct CoinKeyPair {
|
|
fast::Scalar private_key;
|
|
fast::Point public_key;
|
|
std::string address; // Default format for coin
|
|
std::string wif; // WIF-encoded key (empty for EVM coins)
|
|
};
|
|
|
|
CoinKeyPair coin_derive(const fast::Scalar& private_key, const CoinParams& coin,
|
|
bool testnet = false, const CurveContext* ctx = nullptr);
|
|
```
|
|
|
|
---
|
|
|
|
## Unified Wallet API
|
|
|
|
**Namespace:** `secp256k1::coins::wallet`
|
|
|
|
**Header:**
|
|
```cpp
|
|
#include <secp256k1/coins/wallet.hpp>
|
|
```
|
|
|
|
Chain-agnostic facade over all address, signing, and recovery operations. Same API regardless of underlying chain.
|
|
|
|
### Wallet Key Management
|
|
|
|
```cpp
|
|
struct WalletKey {
|
|
fast::Scalar priv; // 32-byte private scalar
|
|
fast::Point pub; // Public key
|
|
};
|
|
|
|
// Create from raw 32-byte key. Returns (key, success).
|
|
std::pair<WalletKey, bool> from_private_key(const uint8_t* priv32);
|
|
|
|
// Export in chain-appropriate format:
|
|
// Bitcoin-family: WIF (Base58Check)
|
|
// EVM-family: 0x-prefixed hex
|
|
// Tron: raw hex
|
|
std::string export_private_key(const CoinParams& coin, const WalletKey& key,
|
|
bool testnet = false);
|
|
|
|
std::string export_public_key_hex(const CoinParams& coin, const WalletKey& key);
|
|
```
|
|
|
|
### Wallet Address Generation
|
|
|
|
```cpp
|
|
// Default address format for coin
|
|
std::string get_address(const CoinParams& coin, const WalletKey& key,
|
|
bool testnet = false);
|
|
|
|
// Explicit format selection
|
|
std::string get_address_p2pkh(const CoinParams& coin, const WalletKey& key,
|
|
bool testnet = false);
|
|
std::string get_address_p2wpkh(const CoinParams& coin, const WalletKey& key,
|
|
bool testnet = false);
|
|
std::string get_address_p2sh_p2wpkh(const CoinParams& coin, const WalletKey& key,
|
|
bool testnet = false);
|
|
std::string get_address_p2tr(const CoinParams& coin, const WalletKey& key,
|
|
bool testnet = false);
|
|
std::string get_address_cashaddr(const CoinParams& coin, const WalletKey& key,
|
|
bool testnet = false);
|
|
```
|
|
|
|
### Wallet Signing
|
|
|
|
```cpp
|
|
struct MessageSignature {
|
|
std::array<uint8_t, 32> r;
|
|
std::array<uint8_t, 32> s;
|
|
int recid; // Recovery ID (0-3)
|
|
uint64_t v; // EIP-155 v (EVM) or 27+recid (Bitcoin)
|
|
std::array<uint8_t, 65> to_rsv() const; // [r:32][s:32][v:1]
|
|
};
|
|
|
|
// Chain-aware message signing:
|
|
// Bitcoin: "\x18Bitcoin Signed Message:\n" prefix -> dSHA256
|
|
// Ethereum: "\x19Ethereum Signed Message:\n" prefix -> Keccak-256
|
|
MessageSignature sign_message(const CoinParams& coin, const WalletKey& key,
|
|
const uint8_t* msg, size_t msg_len);
|
|
|
|
// Sign raw 32-byte hash (no prefix, no hashing)
|
|
MessageSignature sign_hash(const CoinParams& coin, const WalletKey& key,
|
|
const uint8_t* hash32);
|
|
|
|
// Verify signed message against public key
|
|
bool verify_message(const CoinParams& coin, const fast::Point& pubkey,
|
|
const uint8_t* msg, size_t msg_len,
|
|
const MessageSignature& sig);
|
|
```
|
|
|
|
### Wallet Recovery
|
|
|
|
```cpp
|
|
// Recover public key from message + signature
|
|
std::pair<fast::Point, bool>
|
|
recover_signer(const CoinParams& coin,
|
|
const uint8_t* msg, size_t msg_len,
|
|
const MessageSignature& sig);
|
|
|
|
// Recover address string from message + signature
|
|
std::pair<std::string, bool>
|
|
recover_address(const CoinParams& coin,
|
|
const uint8_t* msg, size_t msg_len,
|
|
const MessageSignature& sig);
|
|
```
|
|
|
|
---
|
|
|
|
## BIP-39 Mnemonic Seed Phrases
|
|
|
|
**Namespace:** `secp256k1`
|
|
|
|
**Header:**
|
|
```cpp
|
|
#include <secp256k1/bip39.hpp>
|
|
```
|
|
|
|
BIP-39 mnemonic code for generating deterministic keys. Converts entropy to
|
|
human-readable word sequences and derives 512-bit seeds compatible with BIP-32.
|
|
|
|
### Mnemonic Generation
|
|
|
|
```cpp
|
|
// Generate mnemonic from OS CSPRNG entropy (12 words = 16 bytes)
|
|
auto [mnemonic, ok] = secp256k1::bip39_generate(16);
|
|
|
|
// Generate from explicit entropy (24 words = 32 bytes)
|
|
uint8_t entropy[32] = { /* ... */ };
|
|
auto [mnemonic24, ok2] = secp256k1::bip39_generate(32, entropy);
|
|
```
|
|
|
|
```cpp
|
|
std::pair<std::string, bool>
|
|
bip39_generate(std::size_t entropy_bytes,
|
|
const std::uint8_t* entropy_in = nullptr);
|
|
```
|
|
|
|
**Parameters:**
|
|
| Parameter | Description |
|
|
|-----------|-------------|
|
|
| `entropy_bytes` | 16 (12 words), 20 (15), 24 (18), 28 (21), or 32 (24 words) |
|
|
| `entropy_in` | Optional explicit entropy; `nullptr` = use OS CSPRNG |
|
|
|
|
**Returns:** `{mnemonic_string, success}`
|
|
|
|
### Mnemonic Validation
|
|
|
|
```cpp
|
|
bool valid = secp256k1::bip39_validate("abandon abandon ... about");
|
|
```
|
|
|
|
```cpp
|
|
bool bip39_validate(const std::string& mnemonic);
|
|
```
|
|
|
|
Validates word count (12/15/18/21/24), word membership in BIP-39 English wordlist,
|
|
and SHA-256 checksum.
|
|
|
|
### Seed Derivation
|
|
|
|
```cpp
|
|
auto [seed, ok] = secp256k1::bip39_mnemonic_to_seed(mnemonic, "my passphrase");
|
|
// seed is std::array<uint8_t, 64> -- pass to bip32_master_key()
|
|
```
|
|
|
|
```cpp
|
|
std::pair<std::array<std::uint8_t, 64>, bool>
|
|
bip39_mnemonic_to_seed(const std::string& mnemonic,
|
|
const std::string& passphrase = "");
|
|
```
|
|
|
|
Uses PBKDF2-HMAC-SHA512 with 2048 iterations. Salt = `"mnemonic" + passphrase`.
|
|
|
|
### Mnemonic to Entropy
|
|
|
|
```cpp
|
|
auto [ent, ok] = secp256k1::bip39_mnemonic_to_entropy("abandon abandon ... about");
|
|
// ent.data = raw entropy bytes, ent.length = byte count (16-32)
|
|
```
|
|
|
|
```cpp
|
|
struct Bip39Entropy {
|
|
std::array<std::uint8_t, 32> data{};
|
|
std::size_t length = 0;
|
|
};
|
|
|
|
std::pair<Bip39Entropy, bool>
|
|
bip39_mnemonic_to_entropy(const std::string& mnemonic);
|
|
```
|
|
|
|
### PBKDF2-HMAC-SHA512
|
|
|
|
```cpp
|
|
void pbkdf2_hmac_sha512(const std::uint8_t* password, std::size_t password_len,
|
|
const std::uint8_t* salt, std::size_t salt_len,
|
|
std::uint32_t iterations,
|
|
std::uint8_t* output, std::size_t output_len);
|
|
```
|
|
|
|
Exposed for direct use and testing. Used internally by `bip39_mnemonic_to_seed()`.
|
|
|
|
### Wordlist Access
|
|
|
|
```cpp
|
|
const char* const* words = secp256k1::bip39_wordlist_english();
|
|
// words[0] == "abandon", words[2047] == "zoo"
|
|
```
|
|
|
|
Returns pointer to the 2048-word sorted English BIP-39 wordlist.
|
|
|
|
---
|
|
|
|
## Message Signing API
|
|
|
|
**Namespace:** `secp256k1::coins`
|
|
|
|
**Header:**
|
|
```cpp
|
|
#include <secp256k1/coins/message_signing.hpp>
|
|
```
|
|
|
|
Low-level Bitcoin message signing (BIP-137 / Electrum format).
|
|
|
|
#### Bitcoin Message Hash
|
|
|
|
```cpp
|
|
// SHA256(SHA256("\x18Bitcoin Signed Message:\n" + varint(msg_len) + msg))
|
|
std::array<uint8_t, 32> bitcoin_message_hash(const uint8_t* msg, size_t msg_len);
|
|
```
|
|
|
|
#### Sign / Verify / Recover
|
|
|
|
```cpp
|
|
RecoverableSignature bitcoin_sign_message(const uint8_t* msg, size_t msg_len,
|
|
const fast::Scalar& private_key);
|
|
|
|
bool bitcoin_verify_message(const uint8_t* msg, size_t msg_len,
|
|
const fast::Point& pubkey,
|
|
const ECDSASignature& sig);
|
|
|
|
std::pair<fast::Point, bool>
|
|
bitcoin_recover_message(const uint8_t* msg, size_t msg_len,
|
|
const ECDSASignature& sig, int recid);
|
|
```
|
|
|
|
#### Base64 Encoding
|
|
|
|
```cpp
|
|
// 65-byte format: 1-byte header (recid + compression flag, range 27-34) + r + s
|
|
std::string bitcoin_sig_to_base64(const RecoverableSignature& rsig,
|
|
bool compressed = true);
|
|
|
|
struct BitcoinSigDecodeResult {
|
|
ECDSASignature sig;
|
|
int recid;
|
|
bool compressed;
|
|
bool valid;
|
|
};
|
|
BitcoinSigDecodeResult bitcoin_sig_from_base64(const std::string& base64);
|
|
```
|
|
|
|
---
|
|
|
|
## Zero-Knowledge Proof API
|
|
|
|
**Namespace:** `secp256k1::zk`
|
|
|
|
**Header:**
|
|
```cpp
|
|
#include <secp256k1/zk.hpp>
|
|
```
|
|
|
|
---
|
|
|
|
### Knowledge Proof (Schnorr Sigma)
|
|
|
|
Non-interactive proof of knowledge of discrete log via Fiat-Shamir transform.
|
|
|
|
#### KnowledgeProof
|
|
|
|
```cpp
|
|
struct KnowledgeProof {
|
|
std::array<uint8_t, 32> rx; // R.x (nonce point x-coordinate)
|
|
fast::Scalar s; // response scalar
|
|
|
|
std::array<uint8_t, 64> serialize() const;
|
|
static bool deserialize(const uint8_t* data64, KnowledgeProof& out);
|
|
};
|
|
```
|
|
|
|
#### knowledge_prove
|
|
|
|
```cpp
|
|
// Prove: "I know x such that pubkey = x*G"
|
|
// Uses CT layer (constant-time). Nonce hedged with aux_rand.
|
|
KnowledgeProof knowledge_prove(
|
|
const fast::Scalar& secret,
|
|
const fast::Point& pubkey,
|
|
const std::array<uint8_t, 32>& msg,
|
|
const std::array<uint8_t, 32>& aux_rand);
|
|
```
|
|
|
|
#### knowledge_verify
|
|
|
|
```cpp
|
|
// Verify: s*G == R + e*P (FAST path)
|
|
bool knowledge_verify(
|
|
const KnowledgeProof& proof,
|
|
const fast::Point& pubkey,
|
|
const std::array<uint8_t, 32>& msg);
|
|
```
|
|
|
|
#### knowledge_prove_base / knowledge_verify_base
|
|
|
|
Same as above but with an arbitrary base point (not just G):
|
|
|
|
```cpp
|
|
KnowledgeProof knowledge_prove_base(
|
|
const fast::Scalar& secret,
|
|
const fast::Point& point, // point = secret * base
|
|
const fast::Point& base,
|
|
const std::array<uint8_t, 32>& msg,
|
|
const std::array<uint8_t, 32>& aux_rand);
|
|
|
|
bool knowledge_verify_base(
|
|
const KnowledgeProof& proof,
|
|
const fast::Point& point,
|
|
const fast::Point& base,
|
|
const std::array<uint8_t, 32>& msg);
|
|
```
|
|
|
|
---
|
|
|
|
### DLEQ Proof (Discrete Log Equality)
|
|
|
|
Proves log_G(P) == log_H(Q) without revealing the secret.
|
|
|
|
#### DLEQProof
|
|
|
|
```cpp
|
|
struct DLEQProof {
|
|
fast::Scalar e; // challenge
|
|
fast::Scalar s; // response
|
|
|
|
std::array<uint8_t, 64> serialize() const;
|
|
static bool deserialize(const uint8_t* data64, DLEQProof& out);
|
|
};
|
|
```
|
|
|
|
#### dleq_prove
|
|
|
|
```cpp
|
|
// Prove: P = secret*G AND Q = secret*H (same secret)
|
|
// Uses CT layer. Nonce hedged with aux_rand.
|
|
DLEQProof dleq_prove(
|
|
const fast::Scalar& secret,
|
|
const fast::Point& G,
|
|
const fast::Point& H,
|
|
const fast::Point& P,
|
|
const fast::Point& Q,
|
|
const std::array<uint8_t, 32>& aux_rand);
|
|
```
|
|
|
|
#### dleq_verify
|
|
|
|
```cpp
|
|
// Verify: s*G == R1 + e*P AND s*H == R2 + e*Q (FAST path)
|
|
bool dleq_verify(
|
|
const DLEQProof& proof,
|
|
const fast::Point& G,
|
|
const fast::Point& H,
|
|
const fast::Point& P,
|
|
const fast::Point& Q);
|
|
```
|
|
|
|
---
|
|
|
|
### Bulletproof Range Proof
|
|
|
|
Proves committed value is in [0, 2^64) without revealing the value. Based on
|
|
Bulletproofs (Bunz et al., 2018). Logarithmic proof size.
|
|
|
|
#### RangeProof
|
|
|
|
```cpp
|
|
static constexpr size_t RANGE_PROOF_BITS = 64;
|
|
static constexpr size_t RANGE_PROOF_LOG2 = 6;
|
|
|
|
struct RangeProof {
|
|
fast::Point A, S; // vector commitments
|
|
fast::Point T1, T2; // polynomial commitments
|
|
fast::Scalar tau_x, mu, t_hat; // scalar responses
|
|
std::array<fast::Point, 6> L; // inner product argument (log2(64) rounds)
|
|
std::array<fast::Point, 6> R;
|
|
fast::Scalar a, b; // final inner product scalars
|
|
};
|
|
```
|
|
|
|
#### range_prove
|
|
|
|
```cpp
|
|
// Generate Bulletproof range proof for Pedersen commitment C = value*H + blinding*G
|
|
// value must be in [0, 2^64). Uses CT layer.
|
|
RangeProof range_prove(
|
|
uint64_t value,
|
|
const fast::Scalar& blinding,
|
|
const PedersenCommitment& commitment,
|
|
const std::array<uint8_t, 32>& aux_rand);
|
|
```
|
|
|
|
#### range_verify
|
|
|
|
```cpp
|
|
// Verify range proof (MSM-optimized, FAST path)
|
|
// Returns true if committed value is in [0, 2^64)
|
|
bool range_verify(
|
|
const PedersenCommitment& commitment,
|
|
const RangeProof& proof);
|
|
```
|
|
|
|
---
|
|
|
|
### ZK Batch Operations
|
|
|
|
#### batch_range_verify
|
|
|
|
```cpp
|
|
// Batch-verify multiple range proofs (more efficient than individual verification)
|
|
// Returns true only if ALL proofs are valid.
|
|
bool batch_range_verify(
|
|
const PedersenCommitment* commitments,
|
|
const RangeProof* proofs,
|
|
size_t count);
|
|
```
|
|
|
|
#### batch_commit
|
|
|
|
```cpp
|
|
// Batch-create Pedersen commitments
|
|
void batch_commit(
|
|
const fast::Scalar* values,
|
|
const fast::Scalar* blindings,
|
|
PedersenCommitment* commitments_out,
|
|
size_t count);
|
|
```
|
|
|
|
#### GeneratorVectors
|
|
|
|
```cpp
|
|
struct GeneratorVectors {
|
|
std::array<fast::Point, 64> G; // nothing-up-my-sleeve generators
|
|
std::array<fast::Point, 64> H;
|
|
};
|
|
|
|
// Retrieve cached generator vectors (computed once on first call)
|
|
const GeneratorVectors& get_generator_vectors();
|
|
```
|
|
|
|
#### ZK Performance Summary
|
|
|
|
| Operation | Time | Throughput | Layer |
|
|
|-----------|------|------------|-------|
|
|
| Pedersen Commit | 33.0 us | 30.3K op/s | FAST |
|
|
| Knowledge Prove | 20.3 us | 49.3K op/s | CT |
|
|
| Knowledge Verify | 21.8 us | 46.0K op/s | FAST |
|
|
| DLEQ Prove | 40.0 us | 25.0K op/s | CT |
|
|
| DLEQ Verify | 56.4 us | 17.7K op/s | FAST |
|
|
| Range Prove (64b) | 13,467 us | 74 op/s | CT |
|
|
| Range Verify (64b) | 2,634 us | 380 op/s | FAST |
|
|
|
|
*Measured on i7-14400F, 11 passes, pinned core, median.*
|
|
|
|
---
|
|
|
|
## CUDA API
|
|
|
|
**Namespace:** `secp256k1::cuda`
|
|
|
|
**Header:**
|
|
```cpp
|
|
#include <secp256k1.cuh>
|
|
```
|
|
|
|
---
|
|
|
|
### CUDA Data Structures
|
|
|
|
```cpp
|
|
// Field element (4 x 64-bit limbs, little-endian)
|
|
struct FieldElement {
|
|
uint64_t limbs[4];
|
|
};
|
|
|
|
// Scalar (4 x 64-bit limbs)
|
|
struct Scalar {
|
|
uint64_t limbs[4];
|
|
};
|
|
|
|
// Jacobian point (X, Y, Z)
|
|
struct JacobianPoint {
|
|
FieldElement x;
|
|
FieldElement y;
|
|
FieldElement z;
|
|
bool infinity;
|
|
};
|
|
|
|
// Affine point (x, y)
|
|
struct AffinePoint {
|
|
FieldElement x;
|
|
FieldElement y;
|
|
};
|
|
|
|
// 32-bit view for optimized operations (zero-cost conversion)
|
|
struct MidFieldElement {
|
|
uint32_t limbs[8];
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
### CUDA Field Operations
|
|
|
|
All functions are `__device__` and can only be called from GPU kernels.
|
|
|
|
```cpp
|
|
// Initialization
|
|
__device__ void field_set_zero(FieldElement* r);
|
|
__device__ void field_set_one(FieldElement* r);
|
|
|
|
// Comparison
|
|
__device__ bool field_is_zero(const FieldElement* a);
|
|
__device__ bool field_eq(const FieldElement* a, const FieldElement* b);
|
|
|
|
// Arithmetic
|
|
__device__ void field_add(const FieldElement* a, const FieldElement* b, FieldElement* r);
|
|
__device__ void field_sub(const FieldElement* a, const FieldElement* b, FieldElement* r);
|
|
__device__ void field_mul(const FieldElement* a, const FieldElement* b, FieldElement* r);
|
|
__device__ void field_sqr(const FieldElement* a, FieldElement* r);
|
|
__device__ void field_inv(const FieldElement* a, FieldElement* r);
|
|
__device__ void field_neg(const FieldElement* a, FieldElement* r);
|
|
|
|
// Domain conversion (Montgomery mode only)
|
|
__device__ void field_to_mont(const FieldElement* a, FieldElement* r);
|
|
__device__ void field_from_mont(const FieldElement* a, FieldElement* r);
|
|
```
|
|
|
|
---
|
|
|
|
### CUDA Point Operations
|
|
|
|
```cpp
|
|
// Initialization
|
|
__device__ void jacobian_set_infinity(JacobianPoint* p);
|
|
__device__ void jacobian_set_generator(JacobianPoint* p);
|
|
__device__ bool jacobian_is_infinity(const JacobianPoint* p);
|
|
|
|
// Point arithmetic
|
|
__device__ void jacobian_double(const JacobianPoint* p, JacobianPoint* r);
|
|
__device__ void jacobian_add(const JacobianPoint* p, const JacobianPoint* q, JacobianPoint* r);
|
|
__device__ void jacobian_add_mixed(const JacobianPoint* p, const AffinePoint* q, JacobianPoint* r);
|
|
|
|
// Scalar multiplication
|
|
__device__ void scalar_mul(const JacobianPoint* p, const Scalar* k, JacobianPoint* r);
|
|
__device__ void scalar_mul_generator(const Scalar* k, JacobianPoint* r);
|
|
|
|
// Conversion
|
|
__device__ void jacobian_to_affine(const JacobianPoint* p, AffinePoint* r);
|
|
```
|
|
|
|
---
|
|
|
|
### CUDA Batch Operations
|
|
|
|
```cpp
|
|
#include <batch_inversion.cuh>
|
|
|
|
// Batch field inversion (Montgomery's trick)
|
|
// Inverts n field elements using only 1 modular inversion + 3(n-1) multiplications
|
|
__device__ void batch_invert(FieldElement* elements, int n, FieldElement* scratch);
|
|
```
|
|
|
|
---
|
|
|
|
### CUDA Hash Operations
|
|
|
|
```cpp
|
|
#include <hash160.cuh>
|
|
|
|
// Compute HASH160 = RIPEMD160(SHA256(pubkey))
|
|
__device__ void hash160_compressed(const uint8_t pubkey[33], uint8_t hash[20]);
|
|
__device__ void hash160_uncompressed(const uint8_t pubkey[65], uint8_t hash[20]);
|
|
```
|
|
|
|
---
|
|
|
|
### CUDA Signature Operations
|
|
|
|
> **World-first:** No other open-source GPU library provides secp256k1 ECDSA + Schnorr sign/verify.
|
|
|
|
#### Data Structures
|
|
|
|
```cpp
|
|
#include <ecdsa.cuh>
|
|
#include <schnorr.cuh>
|
|
#include <recovery.cuh>
|
|
|
|
// ECDSA signature (r, s as Scalars)
|
|
struct ECDSASignatureGPU {
|
|
Scalar r;
|
|
Scalar s;
|
|
};
|
|
|
|
// Schnorr BIP-340 signature (32-byte R x-coordinate + Scalar s)
|
|
struct SchnorrSignatureGPU {
|
|
uint8_t r[32]; // x-coordinate of R point
|
|
Scalar s;
|
|
};
|
|
|
|
// Recoverable ECDSA signature
|
|
struct RecoverableSignatureGPU {
|
|
ECDSASignatureGPU sig;
|
|
int recid; // Recovery ID (0-3)
|
|
};
|
|
```
|
|
|
|
#### Device Functions
|
|
|
|
```cpp
|
|
// ECDSA Sign (RFC 6979 deterministic nonces, low-S normalization)
|
|
// Returns true on success
|
|
__device__ bool ecdsa_sign(
|
|
const uint8_t msg_hash[32], // 32-byte message hash
|
|
const Scalar* privkey, // Private key
|
|
ECDSASignatureGPU* sig // Output signature
|
|
);
|
|
|
|
// ECDSA Verify (Shamir's trick + GLV endomorphism)
|
|
// Returns true if signature is valid
|
|
__device__ bool ecdsa_verify(
|
|
const uint8_t msg_hash[32], // 32-byte message hash
|
|
const JacobianPoint* pubkey, // Public key (Jacobian)
|
|
const ECDSASignatureGPU* sig // Signature to verify
|
|
);
|
|
|
|
// ECDSA Sign with Recovery ID
|
|
__device__ bool ecdsa_sign_recoverable(
|
|
const uint8_t msg_hash[32],
|
|
const Scalar* privkey,
|
|
RecoverableSignatureGPU* sig // Output: signature + recid
|
|
);
|
|
|
|
// ECDSA Recover public key from signature
|
|
__device__ bool ecdsa_recover(
|
|
const uint8_t msg_hash[32],
|
|
const RecoverableSignatureGPU* sig,
|
|
JacobianPoint* pubkey // Output: recovered public key
|
|
);
|
|
|
|
// Schnorr Sign (BIP-340, tagged hash midstates for performance)
|
|
__device__ bool schnorr_sign(
|
|
const Scalar* privkey,
|
|
const uint8_t msg[32],
|
|
const uint8_t aux_rand[32], // Auxiliary randomness
|
|
SchnorrSignatureGPU* sig // Output signature
|
|
);
|
|
|
|
// Schnorr Verify (BIP-340, x-only pubkey)
|
|
__device__ bool schnorr_verify(
|
|
const uint8_t pubkey_x[32], // X-only public key (32 bytes)
|
|
const uint8_t msg[32],
|
|
const SchnorrSignatureGPU* sig
|
|
);
|
|
```
|
|
|
|
#### Batch Kernel Wrappers
|
|
|
|
Host-callable kernel wrappers for batch processing:
|
|
|
|
```cpp
|
|
// Launch batch ECDSA sign (128 threads/block, 2 blocks/SM)
|
|
void ecdsa_sign_batch_kernel<<<blocks, 128>>>(
|
|
const uint8_t* msg_hashes, // N x 32 bytes
|
|
const Scalar* privkeys, // N scalars
|
|
ECDSASignatureGPU* sigs, // N output signatures
|
|
int count
|
|
);
|
|
|
|
// Launch batch ECDSA verify
|
|
void ecdsa_verify_batch_kernel<<<blocks, 128>>>(
|
|
const uint8_t* msg_hashes,
|
|
const JacobianPoint* pubkeys,
|
|
const ECDSASignatureGPU* sigs,
|
|
bool* results, // N output booleans
|
|
int count
|
|
);
|
|
|
|
// Launch batch Schnorr sign
|
|
void schnorr_sign_batch_kernel<<<blocks, 128>>>(
|
|
const Scalar* privkeys,
|
|
const uint8_t* msgs,
|
|
const uint8_t* aux_rands,
|
|
SchnorrSignatureGPU* sigs,
|
|
int count
|
|
);
|
|
|
|
// Launch batch Schnorr verify
|
|
void schnorr_verify_batch_kernel<<<blocks, 128>>>(
|
|
const uint8_t* pubkey_xs,
|
|
const uint8_t* msgs,
|
|
const SchnorrSignatureGPU* sigs,
|
|
bool* results,
|
|
int count
|
|
);
|
|
```
|
|
|
|
#### Performance
|
|
|
|
| Operation | Time/Op | Throughput |
|
|
|-----------|---------|------------|
|
|
| ECDSA Sign | 204.8 ns | 4.88 M/s |
|
|
| ECDSA Verify | 410.1 ns | 2.44 M/s |
|
|
| ECDSA Sign + Recid | 311.5 ns | 3.21 M/s |
|
|
| Schnorr Sign | 273.4 ns | 3.66 M/s |
|
|
| Schnorr Verify | 354.6 ns | 2.82 M/s |
|
|
|
|
*RTX 5060 Ti, kernel-only timing, batch 16K*
|
|
|
|
---
|
|
|
|
### CUDA CT (Constant-Time) Operations
|
|
|
|
**Namespace:** `secp256k1::cuda::ct`
|
|
|
|
**Header:**
|
|
```cpp
|
|
#include <ct/ct_sign.cuh> // CT ECDSA/Schnorr sign
|
|
#include <ct/ct_zk.cuh> // CT ZK knowledge/DLEQ prove
|
|
```
|
|
|
|
All CT functions use constant-time scalar multiplication and arithmetic to prevent
|
|
side-channel leakage. Proving operations are CT; verification uses the fast path
|
|
(public data only -- see `zk.cuh`).
|
|
|
|
#### CT Signing
|
|
|
|
```cpp
|
|
// CT ECDSA sign (constant-time k*G, k^-1, scalar ops)
|
|
__device__ bool ct_ecdsa_sign(
|
|
const uint8_t msg_hash[32],
|
|
const Scalar* privkey,
|
|
ECDSASignatureGPU* sig
|
|
);
|
|
|
|
// CT Schnorr sign (constant-time nonce generation + signing)
|
|
__device__ bool ct_schnorr_sign(
|
|
const Scalar* privkey,
|
|
const uint8_t msg[32],
|
|
const uint8_t aux_rand[32],
|
|
SchnorrSignatureGPU* sig
|
|
);
|
|
|
|
// CT Schnorr keypair (x-only pubkey extraction)
|
|
__device__ void ct_schnorr_keypair_create(
|
|
const Scalar* privkey,
|
|
SchnorrKeypairGPU* keypair
|
|
);
|
|
```
|
|
|
|
#### CT Zero-Knowledge Proofs
|
|
|
|
##### Data Structures
|
|
|
|
```cpp
|
|
#include <zk.cuh> // KnowledgeProofGPU, DLEQProofGPU
|
|
#include <ct/ct_zk.cuh> // CT proving functions
|
|
|
|
// Knowledge proof (Schnorr sigma protocol)
|
|
struct KnowledgeProofGPU {
|
|
uint8_t rx[32]; // R point x-coordinate
|
|
Scalar s; // Response scalar
|
|
};
|
|
|
|
// DLEQ proof (discrete log equality)
|
|
struct DLEQProofGPU {
|
|
Scalar e; // Challenge
|
|
Scalar s; // Response
|
|
};
|
|
```
|
|
|
|
##### Device Functions
|
|
|
|
```cpp
|
|
// CT Knowledge Proof: proves knowledge of secret s such that P = s * B
|
|
// Uses CT scalar_mul for k*B (k is secret), CT scalar arithmetic
|
|
__device__ bool ct_knowledge_prove_device(
|
|
const Scalar* secret,
|
|
const JacobianPoint* pubkey, // P = secret * base
|
|
const JacobianPoint* base, // Arbitrary base point B
|
|
const uint8_t msg[32],
|
|
const uint8_t aux[32], // Auxiliary randomness (XOR hedging)
|
|
KnowledgeProofGPU* proof
|
|
);
|
|
|
|
// Convenience: prove for generator G
|
|
__device__ bool ct_knowledge_prove_generator_device(
|
|
const Scalar* secret,
|
|
const JacobianPoint* pubkey, // P = secret * G
|
|
const uint8_t msg[32],
|
|
const uint8_t aux[32],
|
|
KnowledgeProofGPU* proof
|
|
);
|
|
|
|
// CT DLEQ Proof: proves log_G(P) == log_H(Q) without revealing the log
|
|
// Two CT scalar_mul operations (k*G, k*H), 6-point challenge hash
|
|
__device__ bool ct_dleq_prove_device(
|
|
const Scalar* secret,
|
|
const JacobianPoint* G, // First base
|
|
const JacobianPoint* H, // Second base
|
|
const JacobianPoint* P, // P = secret * G
|
|
const JacobianPoint* Q, // Q = secret * H
|
|
const uint8_t aux[32],
|
|
DLEQProofGPU* proof
|
|
);
|
|
```
|
|
|
|
##### Batch Kernels
|
|
|
|
```cpp
|
|
// Batch CT knowledge prove (one thread per proof)
|
|
__global__ void ct_knowledge_prove_batch_kernel(
|
|
const Scalar* secrets,
|
|
const JacobianPoint* pubkeys,
|
|
const JacobianPoint* bases,
|
|
const uint8_t* messages, // N * 32 bytes
|
|
const uint8_t* aux_rands, // N * 32 bytes
|
|
KnowledgeProofGPU* proofs,
|
|
bool* results,
|
|
uint32_t count
|
|
);
|
|
|
|
// Batch CT knowledge prove with generator G
|
|
__global__ void ct_knowledge_prove_generator_batch_kernel(
|
|
const Scalar* secrets,
|
|
const JacobianPoint* pubkeys,
|
|
const uint8_t* messages,
|
|
const uint8_t* aux_rands,
|
|
KnowledgeProofGPU* proofs,
|
|
bool* results,
|
|
uint32_t count
|
|
);
|
|
|
|
// Batch CT DLEQ prove
|
|
__global__ void ct_dleq_prove_batch_kernel(
|
|
const Scalar* secrets,
|
|
const JacobianPoint* G_pts,
|
|
const JacobianPoint* H_pts,
|
|
const JacobianPoint* P_pts,
|
|
const JacobianPoint* Q_pts,
|
|
const uint8_t* aux_rands,
|
|
DLEQProofGPU* proofs,
|
|
bool* results,
|
|
uint32_t count
|
|
);
|
|
```
|
|
|
|
> **Design note:** Verification functions (`knowledge_verify_device`, `dleq_verify_device`)
|
|
> live in `zk.cuh` and use the fast (variable-time) path -- verification inputs are public.
|
|
|
|
---
|
|
|
|
### OpenCL ZK Operations
|
|
|
|
**Kernel file:** `opencl/kernels/secp256k1_zk.cl`
|
|
|
|
OpenCL ZK operations use the fast-path scalar multiplication (wNAF-5). OpenCL has no
|
|
separate CT layer -- all operations are uniform across work-items by design.
|
|
|
|
```c
|
|
// Device functions (called from kernels)
|
|
void zk_knowledge_prove_impl(...);
|
|
void zk_knowledge_verify_impl(...);
|
|
void zk_dleq_prove_impl(...);
|
|
void zk_dleq_verify_impl(...);
|
|
|
|
// Batch kernels
|
|
__kernel void zk_knowledge_prove_batch(...);
|
|
__kernel void zk_knowledge_verify_batch(...);
|
|
__kernel void zk_dleq_prove_batch(...);
|
|
__kernel void zk_dleq_verify_batch(...);
|
|
```
|
|
|
|
---
|
|
|
|
### Metal ZK Operations
|
|
|
|
**Shader file:** `metal/shaders/secp256k1_zk.h`
|
|
**Kernel file:** `metal/shaders/secp256k1_kernels.metal` (Kernels 19-22)
|
|
|
|
Metal ZK uses branchless `affine_select` in scalar multiplication (semi-CT).
|
|
8x32 limb representation.
|
|
|
|
```metal
|
|
// Device functions
|
|
bool zk_knowledge_prove(...);
|
|
bool zk_knowledge_verify(...);
|
|
bool zk_dleq_prove(...);
|
|
bool zk_dleq_verify(...);
|
|
|
|
// Kernels 19-22
|
|
kernel void zk_knowledge_prove_batch(...) [[kernel]];
|
|
kernel void zk_knowledge_verify_batch(...) [[kernel]];
|
|
kernel void zk_dleq_prove_batch(...) [[kernel]];
|
|
kernel void zk_dleq_verify_batch(...) [[kernel]];
|
|
```
|
|
|
|
---
|
|
|
|
## WASM API
|
|
|
|
**Module:** `@ultrafastsecp256k1/wasm`
|
|
|
|
**Usage:**
|
|
```javascript
|
|
import { Secp256k1 } from './secp256k1.mjs';
|
|
const lib = await Secp256k1.create();
|
|
```
|
|
|
|
### Functions
|
|
|
|
| Function | Parameters | Returns | Description |
|
|
|----------|-----------|---------|-------------|
|
|
| `selftest()` | -- | `boolean` | Run built-in self-test |
|
|
| `version()` | -- | `string` | Library version (`"3.0.0"`) |
|
|
| `pubkeyCreate(seckey)` | `Uint8Array(32)` | `{x, y}` | Public key from private key |
|
|
| `pointMul(px, py, scalar)` | `Uint8Array(32)` x 3 | `{x, y}` | Scalar x Point |
|
|
| `pointAdd(px, py, qx, qy)` | `Uint8Array(32)` x 4 | `{x, y}` | Point addition |
|
|
| `ecdsaSign(msgHash, seckey)` | `Uint8Array(32)` x 2 | `Uint8Array(64)` | ECDSA sign (r‖s) |
|
|
| `ecdsaVerify(msgHash, pubX, pubY, sig)` | `Uint8Array(32)` x 3 + `Uint8Array(64)` | `boolean` | ECDSA verify |
|
|
| `schnorrSign(seckey, msg, aux?)` | `Uint8Array(32)` x 2-3 | `Uint8Array(64)` | Schnorr BIP-340 sign |
|
|
| `schnorrVerify(pubkeyX, msg, sig)` | `Uint8Array(32)` x 2 + `Uint8Array(64)` | `boolean` | Schnorr verify |
|
|
| `schnorrPubkey(seckey)` | `Uint8Array(32)` | `Uint8Array(32)` | X-only public key |
|
|
| `sha256(data)` | `Uint8Array` | `Uint8Array(32)` | SHA-256 hash |
|
|
|
|
### C API
|
|
|
|
For direct C/C++ or custom WASM bindings, see [secp256k1_wasm.h](../wasm/secp256k1_wasm.h).
|
|
|
|
### Example
|
|
|
|
```javascript
|
|
const lib = await Secp256k1.create();
|
|
console.log('v' + lib.version(), lib.selftest() ? 'OK' : 'X');
|
|
|
|
// ECDSA workflow
|
|
const privkey = new Uint8Array(32);
|
|
privkey[31] = 1;
|
|
const { x, y } = lib.pubkeyCreate(privkey);
|
|
const msgHash = lib.sha256(new TextEncoder().encode('Hello'));
|
|
const sig = lib.ecdsaSign(msgHash, privkey);
|
|
const valid = lib.ecdsaVerify(msgHash, x, y, sig);
|
|
```
|
|
|
|
See [wasm/README.md](../wasm/README.md) for detailed build and usage instructions.
|
|
|
|
---
|
|
|
|
## C ABI (ufsecp)
|
|
|
|
The stable C ABI is defined in `include/ufsecp/ufsecp.h`. All functions follow these rules:
|
|
|
|
- **Opaque context**: `ufsecp_ctx*` -- one per thread, or externally synchronised
|
|
- **Every function returns `ufsecp_error_t`** (0 = OK)
|
|
- **All I/O is `uint8_t[]`** with fixed sizes -- no internal types leak
|
|
- **Dual-layer CT**: signing/nonce/key-tweak always use the CT layer; verify/point-arith use the fast layer. No opt-in flag.
|
|
- **Caller owns all buffers** -- library never allocates on behalf of caller (except `ctx_create`/`ctx_clone`)
|
|
|
|
### Error Codes
|
|
|
|
| Code | Name | Value |
|
|
|------|------|-------|
|
|
| `UFSECP_OK` | Success | 0 |
|
|
| `UFSECP_ERR_NULL_ARG` | NULL pointer argument | 1 |
|
|
| `UFSECP_ERR_BAD_KEY` | Invalid private key | 2 |
|
|
| `UFSECP_ERR_BAD_PUBKEY` | Invalid public key | 3 |
|
|
| `UFSECP_ERR_BAD_SIG` | Invalid signature | 4 |
|
|
| `UFSECP_ERR_BAD_INPUT` | Invalid input data | 5 |
|
|
| `UFSECP_ERR_VERIFY_FAIL` | Verification failed | 6 |
|
|
| `UFSECP_ERR_ARITH` | Arithmetic error | 7 |
|
|
| `UFSECP_ERR_SELFTEST` | Self-test failure | 8 |
|
|
| `UFSECP_ERR_INTERNAL` | Internal error | 9 |
|
|
| `UFSECP_ERR_BUF_TOO_SMALL` | Output buffer too small | 10 |
|
|
|
|
### Size Constants
|
|
|
|
| Constant | Value | Description |
|
|
|----------|-------|-------------|
|
|
| `UFSECP_PRIVKEY_LEN` | 32 | Private key |
|
|
| `UFSECP_PUBKEY_COMPRESSED_LEN` | 33 | Compressed public key |
|
|
| `UFSECP_PUBKEY_UNCOMPRESSED_LEN` | 65 | Uncompressed public key |
|
|
| `UFSECP_PUBKEY_XONLY_LEN` | 32 | x-only public key (BIP-340) |
|
|
| `UFSECP_SIG_COMPACT_LEN` | 64 | Compact R\|\|S / r\|\|s |
|
|
| `UFSECP_SIG_DER_MAX_LEN` | 72 | Maximum DER-encoded ECDSA sig |
|
|
| `UFSECP_HASH_LEN` | 32 | SHA-256 / Keccak-256 digest |
|
|
| `UFSECP_HASH160_LEN` | 20 | RIPEMD160(SHA256) digest |
|
|
| `UFSECP_SHARED_SECRET_LEN` | 32 | ECDH shared secret |
|
|
| `UFSECP_BIP32_SERIALIZED_LEN` | 78 | BIP-32 extended key |
|
|
|
|
<a id="c-abi-context-lifecycle"></a>
|
|
### Context Lifecycle
|
|
|
|
```c
|
|
// Create context (runs self-test on first call)
|
|
ufsecp_ctx* ctx = NULL;
|
|
ufsecp_error_t err = ufsecp_ctx_create(&ctx);
|
|
|
|
// Clone context (deep copy, for multi-thread use)
|
|
ufsecp_ctx* ctx2 = NULL;
|
|
ufsecp_ctx_clone(ctx, &ctx2);
|
|
|
|
// Error inspection
|
|
ufsecp_error_t last = ufsecp_last_error(ctx);
|
|
const char* msg = ufsecp_last_error_msg(ctx);
|
|
|
|
// FFI layout assertion
|
|
size_t sz = ufsecp_ctx_size();
|
|
|
|
// Destroy (NULL-safe)
|
|
ufsecp_ctx_destroy(ctx);
|
|
```
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_ctx_create` | `(ufsecp_ctx** ctx_out) -> error_t` | Create new context |
|
|
| `ufsecp_ctx_clone` | `(const ctx*, ufsecp_ctx** ctx_out) -> error_t` | Deep copy context |
|
|
| `ufsecp_ctx_destroy` | `(ctx*) -> void` | Free context (NULL-safe) |
|
|
| `ufsecp_last_error` | `(const ctx*) -> error_t` | Last error code |
|
|
| `ufsecp_last_error_msg` | `(const ctx*) -> const char*` | Last error message |
|
|
| `ufsecp_ctx_size` | `(void) -> size_t` | Compiled ctx struct size |
|
|
|
|
<a id="c-abi-private-key-operations"></a>
|
|
### Private Key Operations
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_seckey_verify` | `(ctx, privkey[32]) -> error_t` | Validate private key (non-zero, < n) |
|
|
| `ufsecp_seckey_negate` | `(ctx, privkey[32]) -> error_t` | Negate in-place: key <- -key mod n |
|
|
| `ufsecp_seckey_tweak_add` | `(ctx, privkey[32], tweak[32]) -> error_t` | key <- (key + tweak) mod n |
|
|
| `ufsecp_seckey_tweak_mul` | `(ctx, privkey[32], tweak[32]) -> error_t` | key <- (key * tweak) mod n |
|
|
|
|
<a id="c-abi-public-key-operations"></a>
|
|
### Public Key Operations
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_pubkey_create` | `(ctx, privkey[32], pubkey33_out[33]) -> error_t` | Compressed pubkey from privkey |
|
|
| `ufsecp_pubkey_create_uncompressed` | `(ctx, privkey[32], pubkey65_out[65]) -> error_t` | Uncompressed pubkey from privkey |
|
|
| `ufsecp_pubkey_parse` | `(ctx, input, input_len, pubkey33_out[33]) -> error_t` | Parse 33 or 65 bytes to compressed |
|
|
| `ufsecp_pubkey_xonly` | `(ctx, privkey[32], xonly32_out[32]) -> error_t` | x-only pubkey (BIP-340) |
|
|
| `ufsecp_pubkey_add` | `(ctx, a33[33], b33[33], out33[33]) -> error_t` | Point addition: out = a + b |
|
|
| `ufsecp_pubkey_negate` | `(ctx, pubkey33[33], out33[33]) -> error_t` | Point negation: out = -P |
|
|
| `ufsecp_pubkey_tweak_add` | `(ctx, pubkey33[33], tweak[32], out33[33]) -> error_t` | out = P + tweak*G |
|
|
| `ufsecp_pubkey_tweak_mul` | `(ctx, pubkey33[33], tweak[32], out33[33]) -> error_t` | out = tweak * P |
|
|
| `ufsecp_pubkey_combine` | `(ctx, pubkeys, n, out33[33]) -> error_t` | Sum N compressed pubkeys |
|
|
|
|
<a id="c-abi-ecdsa"></a>
|
|
### ECDSA
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_ecdsa_sign` | `(ctx, msg32[32], privkey[32], sig64_out[64]) -> error_t` | Sign (RFC 6979, low-S) |
|
|
| `ufsecp_ecdsa_sign_verified` | `(ctx, msg32[32], privkey[32], sig64_out[64]) -> error_t` | Sign + verify (fault resistance) |
|
|
| `ufsecp_ecdsa_sign_batch` | `(ctx, n, msgs32[], privkeys32[], sigs64_out[]) -> error_t` | CPU CT batch sign; repeats stable 32/32/64-byte item layout per entry |
|
|
| `ufsecp_ecdsa_verify` | `(ctx, msg32[32], sig64[64], pubkey33[33]) -> error_t` | Verify compact signature |
|
|
| `ufsecp_ecdsa_sig_to_der` | `(ctx, sig64[64], der_out, der_len*) -> error_t` | Compact to DER encoding |
|
|
| `ufsecp_ecdsa_sig_from_der` | `(ctx, der, der_len, sig64_out[64]) -> error_t` | DER to compact encoding |
|
|
| `ufsecp_ecdsa_sign_recoverable` | `(ctx, msg32, privkey, sig64_out, recid_out*) -> error_t` | Sign with recovery id (0-3) |
|
|
| `ufsecp_ecdsa_recover` | `(ctx, msg32, sig64, recid, pubkey33_out[33]) -> error_t` | Recover pubkey from recoverable sig |
|
|
|
|
<a id="c-abi-schnorr"></a>
|
|
### Schnorr / BIP-340
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_schnorr_sign` | `(ctx, msg32, privkey, aux_rand[32], sig64_out) -> error_t` | BIP-340 sign (aux_rand=zeros for deterministic) |
|
|
| `ufsecp_schnorr_sign_verified` | `(ctx, msg32, privkey, aux_rand, sig64_out) -> error_t` | Sign + verify (fault resistance) |
|
|
| `ufsecp_schnorr_sign_batch` | `(ctx, n, msgs32[], privkeys32[], aux_rands32[]?, sigs64_out[]) -> error_t` | CPU CT batch sign; `aux_rands32 == NULL` means all-zero aux for every entry |
|
|
| `ufsecp_schnorr_verify` | `(ctx, msg32, sig64, pubkey_x[32]) -> error_t` | Verify BIP-340 signature |
|
|
|
|
<a id="c-abi-ecdh"></a>
|
|
### ECDH
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_ecdh` | `(ctx, privkey, pubkey33, secret32_out) -> error_t` | SHA256(compressed shared point) |
|
|
| `ufsecp_ecdh_xonly` | `(ctx, privkey, pubkey33, secret32_out) -> error_t` | SHA256(x-coordinate) |
|
|
| `ufsecp_ecdh_raw` | `(ctx, privkey, pubkey33, secret32_out) -> error_t` | Raw x-coordinate (no hash) |
|
|
|
|
<a id="c-abi-hashing"></a>
|
|
### Hashing
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_sha256` | `(data, len, digest32_out) -> error_t` | SHA-256 (HW-accel when available) |
|
|
| `ufsecp_sha512` | `(data, len, digest64_out) -> error_t` | SHA-512 |
|
|
| `ufsecp_hash160` | `(data, len, digest20_out) -> error_t` | RIPEMD160(SHA256) |
|
|
| `ufsecp_tagged_hash` | `(tag, data, len, digest32_out) -> error_t` | BIP-340 tagged hash |
|
|
|
|
<a id="c-abi-addresses"></a>
|
|
### Addresses and WIF
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_addr_p2pkh` | `(ctx, pubkey33, network, addr_out, addr_len*) -> error_t` | P2PKH (Base58) |
|
|
| `ufsecp_addr_p2wpkh` | `(ctx, pubkey33, network, addr_out, addr_len*) -> error_t` | P2WPKH (Bech32, SegWit v0) |
|
|
| `ufsecp_addr_p2tr` | `(ctx, internal_key_x[32], network, addr_out, addr_len*) -> error_t` | P2TR (Bech32m, Taproot) |
|
|
| `ufsecp_wif_encode` | `(ctx, privkey, compressed, network, wif_out, wif_len*) -> error_t` | Private key to WIF |
|
|
| `ufsecp_wif_decode` | `(ctx, wif, privkey32_out, compressed_out*, network_out*) -> error_t` | WIF to private key |
|
|
|
|
Network constants: `UFSECP_NET_MAINNET` (0), `UFSECP_NET_TESTNET` (1).
|
|
|
|
<a id="c-abi-bip32"></a>
|
|
### BIP-32 HD Keys
|
|
|
|
Opaque key type: `ufsecp_bip32_key` (82 bytes, contains 78-byte serialised key + metadata).
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_bip32_master` | `(ctx, seed, seed_len, key_out*) -> error_t` | Master key from seed (16-64 bytes) |
|
|
| `ufsecp_bip32_derive` | `(ctx, parent*, index, child_out*) -> error_t` | Single child derivation (>=0x80000000 = hardened) |
|
|
| `ufsecp_bip32_derive_path` | `(ctx, master*, path_str, key_out*) -> error_t` | Full path, e.g. "m/44'/0'/0'/0/0" |
|
|
| `ufsecp_bip32_privkey` | `(ctx, key*, privkey32_out) -> error_t` | Extract 32-byte private key |
|
|
| `ufsecp_bip32_pubkey` | `(ctx, key*, pubkey33_out) -> error_t` | Extract 33-byte compressed public key |
|
|
|
|
<a id="c-abi-taproot"></a>
|
|
### Taproot / BIP-341
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_taproot_output_key` | `(ctx, internal_x[32], merkle_root, output_x_out[32], parity_out*) -> error_t` | Derive Taproot output key |
|
|
| `ufsecp_taproot_tweak_seckey` | `(ctx, privkey[32], merkle_root, tweaked32_out[32]) -> error_t` | Tweak privkey for key-path spend |
|
|
| `ufsecp_taproot_verify` | `(ctx, output_x, parity, internal_x, merkle_root, len) -> error_t` | Verify Taproot commitment |
|
|
|
|
<a id="c-abi-bip39"></a>
|
|
### BIP-39 Mnemonics
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_bip39_generate` | `(ctx, entropy_bytes, entropy_in, mnemonic_out, mnemonic_len*) -> error_t` | Generate mnemonic (12/15/18/21/24 words) |
|
|
| `ufsecp_bip39_validate` | `(ctx, mnemonic) -> error_t` | Validate mnemonic (checksum + wordlist) |
|
|
| `ufsecp_bip39_to_seed` | `(ctx, mnemonic, passphrase, seed64_out) -> error_t` | Mnemonic to 64-byte seed (PBKDF2) |
|
|
| `ufsecp_bip39_to_entropy` | `(ctx, mnemonic, entropy_out, entropy_len*) -> error_t` | Mnemonic back to raw entropy |
|
|
|
|
Entropy sizes: 16 (12 words), 20 (15), 24 (18), 28 (21), 32 (24 words). Pass `entropy_in=NULL` for random.
|
|
|
|
<a id="c-abi-batch"></a>
|
|
### Batch Verification
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_schnorr_batch_verify` | `(ctx, entries, n) -> error_t` | Verify N Schnorr sigs. Entry: 32 xonly + 32 msg + 64 sig = 128 bytes |
|
|
| `ufsecp_ecdsa_batch_verify` | `(ctx, entries, n) -> error_t` | Verify N ECDSA sigs. Entry: 32 msg + 33 pubkey + 64 sig = 129 bytes |
|
|
| `ufsecp_schnorr_batch_identify_invalid` | `(ctx, entries, n, invalid_out, invalid_count*) -> error_t` | Find indices of invalid Schnorr sigs |
|
|
| `ufsecp_ecdsa_batch_identify_invalid` | `(ctx, entries, n, invalid_out, invalid_count*) -> error_t` | Find indices of invalid ECDSA sigs |
|
|
|
|
<a id="c-abi-msm"></a>
|
|
### Multi-Scalar Multiplication
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_shamir_trick` | `(ctx, a[32], P33[33], b[32], Q33[33], out33[33]) -> error_t` | Compute a*P + b*Q |
|
|
| `ufsecp_multi_scalar_mul` | `(ctx, scalars, points, n, out33[33]) -> error_t` | Compute sum(scalars[i] * points[i]) |
|
|
|
|
Scalars: 32-byte big-endian, contiguous. Points: 33-byte compressed, contiguous.
|
|
|
|
<a id="c-abi-musig2"></a>
|
|
### MuSig2 / BIP-327
|
|
|
|
Size constants: `UFSECP_MUSIG2_PUBNONCE_LEN` (66), `UFSECP_MUSIG2_AGGNONCE_LEN` (66), `UFSECP_MUSIG2_KEYAGG_LEN` (165), `UFSECP_MUSIG2_SESSION_LEN` (165), `UFSECP_MUSIG2_SECNONCE_LEN` (64).
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_musig2_key_agg` | `(ctx, pubkeys, n, keyagg_out, agg_pubkey32_out) -> error_t` | Aggregate x-only pubkeys |
|
|
| `ufsecp_musig2_nonce_gen` | `(ctx, privkey, pubkey32, agg_pubkey32, msg32, extra_in, secnonce_out, pubnonce_out) -> error_t` | Generate nonce pair |
|
|
| `ufsecp_musig2_nonce_agg` | `(ctx, pubnonces, n, aggnonce_out) -> error_t` | Aggregate public nonces |
|
|
| `ufsecp_musig2_start_sign_session` | `(ctx, aggnonce, keyagg, msg32, session_out) -> error_t` | Start signing session |
|
|
| `ufsecp_musig2_partial_sign` | `(ctx, secnonce, privkey, keyagg, session, signer_idx, partial_sig32_out) -> error_t` | Produce partial signature |
|
|
| `ufsecp_musig2_partial_verify` | `(ctx, partial_sig32, pubnonce, pubkey32, keyagg, session, signer_idx) -> error_t` | Verify partial signature |
|
|
| `ufsecp_musig2_partial_sig_agg` | `(ctx, partial_sigs, n, session, sig64_out) -> error_t` | Aggregate partials to final BIP-340 sig |
|
|
|
|
<a id="c-abi-frost"></a>
|
|
### FROST Threshold Signatures
|
|
|
|
Size constants: `UFSECP_FROST_SHARE_LEN` (36), `UFSECP_FROST_KEYPKG_LEN` (141), `UFSECP_FROST_NONCE_LEN` (64), `UFSECP_FROST_NONCE_COMMIT_LEN` (70).
|
|
|
|
Input invariants enforced by the current implementation:
|
|
|
|
- participant IDs are 1-based; `id == 0` is rejected during keygen/finalize and yields a zero Lagrange coefficient
|
|
- signer/commitment/share sender sets must be unique; duplicate signer IDs are treated as invalid and fail closed
|
|
- FROST keygen polynomial coefficients are derived from the pair `(participant_id, coeff_index)` rather than the old arithmetic `id*1000+j` scheme, preventing cross-participant coefficient collisions
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_frost_keygen_begin` | `(ctx, id, threshold, n, seed, commits_out, commits_len*, shares_out, shares_len*) -> error_t` | Key generation phase 1 |
|
|
| `ufsecp_frost_keygen_finalize` | `(ctx, id, all_commits, commits_len, shares, shares_len, t, n, keypkg_out) -> error_t` | Key generation phase 2 |
|
|
| `ufsecp_frost_sign_nonce_gen` | `(ctx, id, nonce_seed, nonce_out, nonce_commit_out) -> error_t` | Generate signing nonce |
|
|
| `ufsecp_frost_sign` | `(ctx, keypkg, nonce, msg32, nonce_commits, n_signers, partial_sig_out[36]) -> error_t` | Produce partial signature |
|
|
| `ufsecp_frost_verify_partial` | `(ctx, partial_sig[36], verification_share33[33], nonce_commits, n_signers, msg32, group_pubkey32) -> error_t` | Verify partial signature |
|
|
| `ufsecp_frost_aggregate` | `(ctx, partial_sigs, n, nonce_commits, n_signers, group_pubkey32, msg32, sig64_out) -> error_t` | Aggregate to final Schnorr sig |
|
|
|
|
<a id="c-abi-adaptor"></a>
|
|
### Adaptor Signatures
|
|
|
|
Size constants: `UFSECP_SCHNORR_ADAPTOR_SIG_LEN` (97), `UFSECP_ECDSA_ADAPTOR_SIG_LEN` (130).
|
|
|
|
Current adaptor invariants:
|
|
|
|
- Schnorr adaptor signatures remain bound to the supplied adaptor point through the challenge computed over `R^ + T`
|
|
- ECDSA adaptor signatures use a multiplicative adapt/extract flow: `adapt` divides out the adaptor secret and `extract` recovers the same secret from `(pre_sig, sig)`
|
|
- ECDSA adaptor verification is adaptor-point bound; verifying the same pre-signature against the wrong adaptor point fails
|
|
|
|
**Schnorr Adaptor:**
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_schnorr_adaptor_sign` | `(ctx, privkey, msg32, adaptor33, aux_rand, pre_sig_out[97]) -> error_t` | Pre-sign |
|
|
| `ufsecp_schnorr_adaptor_verify` | `(ctx, pre_sig[97], pubkey_x, msg32, adaptor33) -> error_t` | Verify pre-signature |
|
|
| `ufsecp_schnorr_adaptor_adapt` | `(ctx, pre_sig[97], secret[32], sig64_out) -> error_t` | Adapt to valid signature |
|
|
| `ufsecp_schnorr_adaptor_extract` | `(ctx, pre_sig[97], sig64, secret32_out) -> error_t` | Extract adaptor secret |
|
|
|
|
**ECDSA Adaptor:**
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_ecdsa_adaptor_sign` | `(ctx, privkey, msg32, adaptor33, pre_sig_out[130]) -> error_t` | Pre-sign |
|
|
| `ufsecp_ecdsa_adaptor_verify` | `(ctx, pre_sig[130], pubkey33, msg32, adaptor33) -> error_t` | Verify pre-signature |
|
|
| `ufsecp_ecdsa_adaptor_adapt` | `(ctx, pre_sig[130], secret[32], sig64_out) -> error_t` | Adapt to valid signature |
|
|
| `ufsecp_ecdsa_adaptor_extract` | `(ctx, pre_sig[130], sig64, secret32_out) -> error_t` | Extract adaptor secret |
|
|
|
|
<a id="c-abi-pedersen"></a>
|
|
### Pedersen Commitments
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_pedersen_commit` | `(ctx, value[32], blinding[32], commitment33_out) -> error_t` | C = value*H + blinding*G |
|
|
| `ufsecp_pedersen_verify` | `(ctx, commitment33, value[32], blinding[32]) -> error_t` | Verify commitment |
|
|
| `ufsecp_pedersen_verify_sum` | `(ctx, pos, n_pos, neg, n_neg) -> error_t` | Verify balance: sum(pos) == sum(neg) |
|
|
| `ufsecp_pedersen_blind_sum` | `(ctx, blinds_in, n_in, blinds_out, n_out, sum32_out) -> error_t` | Compute blinding factor sum |
|
|
| `ufsecp_pedersen_switch_commit` | `(ctx, value, blinding, switch_blind, commitment33_out) -> error_t` | Switch commitment (3-gen) |
|
|
|
|
<a id="c-abi-zk"></a>
|
|
### Zero-Knowledge Proofs
|
|
|
|
Size constants: `UFSECP_ZK_KNOWLEDGE_PROOF_LEN` (64), `UFSECP_ZK_DLEQ_PROOF_LEN` (64), `UFSECP_ZK_RANGE_PROOF_MAX_LEN` (675).
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_zk_knowledge_prove` | `(ctx, secret, pubkey33, msg32, aux_rand, proof_out[64]) -> error_t` | Prove knowledge of discrete log |
|
|
| `ufsecp_zk_knowledge_verify` | `(ctx, proof[64], pubkey33, msg32) -> error_t` | Verify knowledge proof |
|
|
| `ufsecp_zk_dleq_prove` | `(ctx, secret, G33, H33, P33, Q33, aux_rand, proof_out[64]) -> error_t` | Prove DLEQ: logG(P) == logH(Q) |
|
|
| `ufsecp_zk_dleq_verify` | `(ctx, proof[64], G33, H33, P33, Q33) -> error_t` | Verify DLEQ proof |
|
|
| `ufsecp_zk_range_prove` | `(ctx, value, blinding, commitment33, aux_rand, proof_out, proof_len*) -> error_t` | Bulletproof range proof |
|
|
| `ufsecp_zk_range_verify` | `(ctx, commitment33, proof, proof_len) -> error_t` | Verify Bulletproof range proof |
|
|
|
|
<a id="c-abi-multicoin"></a>
|
|
### Multi-Coin Wallet
|
|
|
|
Coin type constants (BIP-44): `UFSECP_COIN_BITCOIN` (0), `UFSECP_COIN_LITECOIN` (2), `UFSECP_COIN_DOGECOIN` (3), `UFSECP_COIN_DASH` (5), `UFSECP_COIN_ETHEREUM` (60), `UFSECP_COIN_BITCOIN_CASH` (145), `UFSECP_COIN_TRON` (195).
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_coin_address` | `(ctx, pubkey33, coin_type, testnet, addr_out, addr_len*) -> error_t` | Default address for any coin |
|
|
| `ufsecp_coin_derive_from_seed` | `(ctx, seed, seed_len, coin_type, account, change, index, testnet, privkey_out, pubkey_out, addr_out, addr_len*) -> error_t` | Full derivation from seed |
|
|
| `ufsecp_coin_wif_encode` | `(ctx, privkey, coin_type, testnet, wif_out, wif_len*) -> error_t` | WIF for any coin |
|
|
| `ufsecp_btc_message_sign` | `(ctx, msg, msg_len, privkey, base64_out, base64_len*) -> error_t` | Bitcoin message sign (BIP-137) |
|
|
| `ufsecp_btc_message_verify` | `(ctx, msg, msg_len, pubkey33, base64_sig) -> error_t` | Bitcoin message verify |
|
|
| `ufsecp_btc_message_hash` | `(msg, msg_len, digest32_out) -> error_t` | Bitcoin message hash |
|
|
|
|
<a id="c-abi-ethereum"></a>
|
|
### Ethereum
|
|
|
|
Available when built with `-DSECP256K1_BUILD_ETHEREUM=ON` (default ON).
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_keccak256` | `(data, len, digest32_out) -> error_t` | Keccak-256 hash |
|
|
| `ufsecp_eth_address` | `(ctx, pubkey33, addr20_out) -> error_t` | 20-byte Ethereum address |
|
|
| `ufsecp_eth_address_checksummed` | `(ctx, pubkey33, addr_out, addr_len*) -> error_t` | EIP-55 checksummed address string |
|
|
| `ufsecp_eth_personal_hash` | `(msg, msg_len, digest32_out) -> error_t` | EIP-191 personal_sign hash |
|
|
| `ufsecp_eth_sign` | `(ctx, msg32, privkey, r_out, s_out, v_out*, chain_id) -> error_t` | ECDSA with recovery (EIP-155 v) |
|
|
| `ufsecp_eth_ecrecover` | `(ctx, msg32, r, s, v, addr20_out) -> error_t` | Recover address from v,r,s |
|
|
|
|
<a id="c-abi-gpu"></a>
|
|
### GPU Operations
|
|
|
|
Backend-neutral GPU acceleration surface. All functions use opaque `ufsecp_gpu_ctx*` (separate from CPU `ufsecp_ctx*`). Include `<ufsecp/ufsecp_gpu.h>`.
|
|
|
|
**GPU Error Codes** (100--106): `UFSECP_ERR_GPU_UNAVAILABLE` (100), `UFSECP_ERR_GPU_DEVICE` (101), `UFSECP_ERR_GPU_LAUNCH` (102), `UFSECP_ERR_GPU_MEMORY` (103), `UFSECP_ERR_GPU_UNSUPPORTED` (104), `UFSECP_ERR_GPU_BACKEND` (105), `UFSECP_ERR_GPU_QUEUE` (106).
|
|
|
|
#### Discovery
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_gpu_backend_count` | `(ids_out*, max) -> uint32_t` | Number of compiled backends; optionally fills array |
|
|
| `ufsecp_gpu_backend_name` | `(bid) -> const char*` | Human-readable backend name ("CUDA", "OpenCL", "Metal") |
|
|
| `ufsecp_gpu_is_available` | `(bid) -> int` | 1 if backend has >= 1 usable device, 0 otherwise |
|
|
| `ufsecp_gpu_device_count` | `(bid) -> uint32_t` | Devices visible to backend `bid` |
|
|
| `ufsecp_gpu_device_info` | `(bid, dev, info_out*) -> error_t` | Fill `ufsecp_gpu_device_info_t` (name, memory, CUs, clock) |
|
|
|
|
#### Context Lifecycle
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_gpu_ctx_create` | `(ctx_out**, bid, dev) -> error_t` | Create GPU context on backend `bid`, device `dev` |
|
|
| `ufsecp_gpu_ctx_destroy` | `(ctx*) -> void` | Destroy context (NULL-safe) |
|
|
| `ufsecp_gpu_last_error` | `(ctx*) -> error_t` | Last error code from `ctx` |
|
|
| `ufsecp_gpu_last_error_msg` | `(ctx*) -> const char*` | Last error message string |
|
|
| `ufsecp_gpu_error_str` | `(code) -> const char*` | Error code to string (CPU + GPU codes) |
|
|
|
|
#### Batch Operations and Extensions
|
|
|
|
| Function | Signature | Description |
|
|
|----------|-----------|-------------|
|
|
| `ufsecp_gpu_generator_mul_batch` | `(ctx, scalars32[], n, pubkeys33_out[]) -> error_t` | k*G for n scalars |
|
|
| `ufsecp_gpu_ecdsa_verify_batch` | `(ctx, msgs32[], pubs33[], sigs64[], n, results_out[]) -> error_t` | ECDSA batch verify |
|
|
| `ufsecp_gpu_schnorr_verify_batch` | `(ctx, msgs32[], pubs_x32[], sigs64[], n, results_out[]) -> error_t` | Schnorr/BIP-340 batch verify |
|
|
| `ufsecp_gpu_ecdh_batch` | `(ctx, privkeys32[], pubs33[], n, secrets32_out[]) -> error_t` | ECDH shared secrets (SECRET-BEARING) |
|
|
| `ufsecp_gpu_hash160_pubkey_batch` | `(ctx, pubs33[], n, hashes20_out[]) -> error_t` | SHA-256 + RIPEMD-160 of pubkeys |
|
|
| `ufsecp_gpu_msm` | `(ctx, scalars32[], points33[], n, result33_out) -> error_t` | Multi-scalar multiplication |
|
|
| `ufsecp_gpu_frost_verify_partial_batch` | `(ctx, z_i32[], D_i33[], E_i33[], Y_i33[], rho_i32[], lambda_ie32[], negate_R[], negate_key[], n, results_out[]) -> error_t` | Batch FROST partial verification |
|
|
| `ufsecp_gpu_ecrecover_batch` | `(ctx, msgs32[], sigs64[], recids[], n, pubkeys33_out[], valid_out[]) -> error_t` | Batch public-key recovery from recoverable ECDSA signatures |
|
|
| `ufsecp_gpu_zk_knowledge_verify_batch` | `(ctx, proofs64[], pubkeys65[], msgs32[], n, results[]) -> error_t` | Batch ZK knowledge proof verification (PUBLIC) |
|
|
| `ufsecp_gpu_zk_dleq_verify_batch` | `(ctx, proofs64[], G65[], H65[], P65[], Q65[], n, results[]) -> error_t` | Batch DLEQ proof verification (PUBLIC) |
|
|
| `ufsecp_gpu_bulletproof_verify_batch` | `(ctx, proofs324[], commits65[], H65, n, results[]) -> error_t` | Batch Bulletproof range proof verification (PUBLIC) |
|
|
| `ufsecp_gpu_bip324_aead_encrypt_batch` | `(ctx, keys32[], nonces12[], plain[], sizes[], max_payload, n, wire_out[]) -> error_t` | Batch BIP-324 ChaCha20-Poly1305 AEAD encrypt (PUBLIC) |
|
|
| `ufsecp_gpu_bip324_aead_decrypt_batch` | `(ctx, keys32[], nonces12[], wire[], sizes[], max_payload, n, plain_out[], valid[]) -> error_t` | Batch BIP-324 ChaCha20-Poly1305 AEAD decrypt (SECRET) |
|
|
|
|
---
|
|
|
|
## Performance Tips
|
|
|
|
### CPU
|
|
|
|
1. **Use in-place operations** when possible:
|
|
```cpp
|
|
// Slower: creates temporary
|
|
point = point.add(other);
|
|
|
|
// Faster: no allocation
|
|
point.add_inplace(other);
|
|
```
|
|
|
|
2. **Use KPlan for fixed-K multiplication**:
|
|
```cpp
|
|
// If K is constant and Q varies, precompute K once
|
|
KPlan plan = KPlan::from_scalar(K);
|
|
for (auto& Q : points) {
|
|
result = Q.scalar_mul_with_plan(plan);
|
|
}
|
|
```
|
|
|
|
3. **Use `from_limbs` for binary I/O** (not `from_bytes`):
|
|
```cpp
|
|
// Database/binary files: use from_limbs (native little-endian)
|
|
FieldElement::from_limbs(limbs);
|
|
|
|
// Hex strings/test vectors: use from_bytes (big-endian)
|
|
FieldElement::from_bytes(bytes);
|
|
```
|
|
|
|
### CUDA
|
|
|
|
1. **Batch operations**: Process thousands of points in parallel
|
|
2. **Avoid divergence**: Use branchless algorithms where possible
|
|
3. **Memory coalescing**: Align data structures to 32/64 bytes
|
|
4. **Use hybrid 32-bit multiplication**: Enabled by default (`SECP256K1_CUDA_USE_HYBRID_MUL=1`)
|
|
|
|
---
|
|
|
|
## Examples
|
|
|
|
### Generate Bitcoin Address (CPU)
|
|
|
|
```cpp
|
|
#include <secp256k1/point.hpp>
|
|
#include <secp256k1/scalar.hpp>
|
|
|
|
using namespace secp256k1::fast;
|
|
|
|
int main() {
|
|
// Private key (256-bit)
|
|
Scalar private_key = Scalar::from_hex(
|
|
"E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33262"
|
|
);
|
|
|
|
// Public key = private_key x G
|
|
Point G = Point::generator();
|
|
Point public_key = G.scalar_mul(private_key);
|
|
|
|
// Get compressed public key (33 bytes)
|
|
auto compressed = public_key.to_compressed();
|
|
|
|
// Print as hex
|
|
for (auto byte : compressed) {
|
|
printf("%02x", byte);
|
|
}
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### Batch Point Generation (CUDA)
|
|
|
|
```cpp
|
|
#include <secp256k1.cuh>
|
|
|
|
using namespace secp256k1::cuda;
|
|
|
|
__global__ void generate_points_kernel(
|
|
const Scalar* private_keys,
|
|
AffinePoint* public_keys,
|
|
int count
|
|
) {
|
|
int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (idx >= count) return;
|
|
|
|
JacobianPoint result;
|
|
scalar_mul_generator(&private_keys[idx], &result);
|
|
jacobian_to_affine(&result, &public_keys[idx]);
|
|
}
|
|
|
|
void generate_points(
|
|
const Scalar* d_private_keys,
|
|
AffinePoint* d_public_keys,
|
|
int count
|
|
) {
|
|
int threads = 256;
|
|
int blocks = (count + threads - 1) / threads;
|
|
generate_points_kernel<<<blocks, threads>>>(
|
|
d_private_keys, d_public_keys, count
|
|
);
|
|
}
|
|
```
|
|
|
|
### Verify Self-Test (CPU)
|
|
|
|
```cpp
|
|
#include <secp256k1/point.hpp>
|
|
#include <iostream>
|
|
|
|
int main() {
|
|
bool ok = secp256k1::fast::Selftest(true);
|
|
if (ok) {
|
|
std::cout << "All tests passed!" << std::endl;
|
|
return 0;
|
|
} else {
|
|
std::cerr << "TESTS FAILED!" << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Build Configuration Macros
|
|
|
|
### CPU
|
|
|
|
| Macro | Default | Description |
|
|
|-------|---------|-------------|
|
|
| `SECP256K1_USE_ASM` | ON | Enable x64/RISC-V assembly |
|
|
| `SECP256K1_RISCV_FAST_REDUCTION` | ON | Fast modular reduction (RISC-V) |
|
|
| `SECP256K1_RISCV_USE_VECTOR` | ON | RVV vector extension |
|
|
|
|
### CUDA
|
|
|
|
| Macro | Default | Description |
|
|
|-------|---------|-------------|
|
|
| `SECP256K1_CUDA_USE_HYBRID_MUL` | 1 | 32-bit hybrid multiplication (~10% faster) |
|
|
| `SECP256K1_CUDA_USE_MONTGOMERY` | 0 | Montgomery domain arithmetic |
|
|
| `SECP256K1_CUDA_LIMBS_32` | 0 | Use 8x32-bit limbs (experimental) |
|
|
|
|
---
|
|
|
|
## Platform Support
|
|
|
|
| Platform | Assembly | SIMD | Status |
|
|
|----------|----------|------|--------|
|
|
| x86-64 Linux/Windows/macOS | BMI2/ADX | AVX2 | [OK] Production |
|
|
| RISC-V 64 | RV64GC | RVV 1.0 | [OK] Production |
|
|
| ARM64 (Android/iOS/macOS) | MUL/UMULH | NEON | [OK] Production |
|
|
| CUDA (sm_75+) | PTX | -- | [OK] Production |
|
|
| ROCm/HIP (AMD) | Portable | -- | [OK] CI |
|
|
| OpenCL 3.0 | PTX | -- | [OK] Production |
|
|
| WebAssembly | Portable | -- | [OK] Production |
|
|
| ESP32-S3 / ESP32 | Portable | -- | [OK] Tested |
|
|
| STM32F103 (Cortex-M3) | UMULL | -- | [OK] Tested |
|
|
|
|
---
|
|
|
|
## Version
|
|
|
|
UltrafastSecp256k1 v3.22.0
|
|
|
|
For more information, see the [README](../README.md) or [GitHub repository](https://github.com/shrec/UltrafastSecp256k1).
|
|
|