74 KiB
UltrafastSecp256k1 API Reference
Complete API documentation for CPU, CUDA, and WASM implementations.
Table of Contents
- CPU API
- Address Generation API
- Multi-Chain Coins API
- Unified Wallet API
- BIP-39 Mnemonic Seed Phrases
- Message Signing API
- Zero-Knowledge Proof API
- CUDA API
- WASM API
- C ABI (ufsecp)
- Context Lifecycle
- Private Key Operations
- Public Key Operations
- ECDSA
- Schnorr / BIP-340
- ECDH
- Hashing
- Addresses and WIF
- BIP-32 HD Keys
- Taproot / BIP-341
- BIP-39 Mnemonics
- Batch Verification
- Multi-Scalar Multiplication
- MuSig2 / BIP-327
- FROST Threshold Signatures
- Adaptor Signatures
- Pedersen Commitments
- Zero-Knowledge Proofs
- Multi-Coin Wallet
- Ethereum
- Performance Tips
- Examples
CPU API
Namespace: secp256k1::fast
Headers:
#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
// 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
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
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
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
// 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
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
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
// 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
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
// 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)
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
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
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
#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:
#include <secp256k1/ecdsa.hpp>
ECDSASignature
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
// 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
// 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
// Deterministic nonce generation per RFC 6979.
fast::Scalar rfc6979_nonce(
const fast::Scalar& private_key,
const std::array<uint8_t, 32>& msg_hash
);
Example
#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:
#include <secp256k1/schnorr.hpp>
SchnorrSignature
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
// 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
// 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
// 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
#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:
#include <secp256k1/sha256.hpp>
One-shot Hashing
// 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
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
#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:
#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:
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:
#include <secp256k1/address.hpp>
Address Types
All address functions take a Network enum (Mainnet or Testnet) and return a std::string.
P2PKH (Legacy, Base58Check)
// "1..." (mainnet) or "m/n..." (testnet)
std::string address_p2pkh(const fast::Point& pubkey,
Network net = Network::Mainnet);
P2WPKH (Native SegWit v0, Bech32)
// "bc1q..." (mainnet) or "tb1q..." (testnet)
std::string address_p2wpkh(const fast::Point& pubkey,
Network net = Network::Mainnet);
P2TR (Taproot, SegWit v1, Bech32m)
// "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)
// "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)
// "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)
// "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)
// 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
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)
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
// RIPEMD160(SHA256(data))
std::array<uint8_t, 20> hash160(const uint8_t* data, size_t len);
WIF (Wallet Import Format)
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
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:
#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
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
inline constexpr const CoinParams* ALL_COINS[]; // Array of all 28 coins
inline constexpr size_t ALL_COINS_COUNT = 28;
Coin Address Functions
// 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_p2wpkhfor Dogecoin).
Coin Key Generation
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:
#include <secp256k1/coins/wallet.hpp>
Chain-agnostic facade over all address, signing, and recovery operations. Same API regardless of underlying chain.
Wallet Key Management
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
// 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
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
// 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:
#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
// 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);
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
bool valid = secp256k1::bip39_validate("abandon abandon ... about");
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
auto [seed, ok] = secp256k1::bip39_mnemonic_to_seed(mnemonic, "my passphrase");
// seed is std::array<uint8_t, 64> -- pass to bip32_master_key()
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
auto [ent, ok] = secp256k1::bip39_mnemonic_to_entropy("abandon abandon ... about");
// ent.data = raw entropy bytes, ent.length = byte count (16-32)
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
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
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:
#include <secp256k1/coins/message_signing.hpp>
Low-level Bitcoin message signing (BIP-137 / Electrum format).
Bitcoin Message Hash
// 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
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
// 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:
#include <secp256k1/zk.hpp>
Knowledge Proof (Schnorr Sigma)
Non-interactive proof of knowledge of discrete log via Fiat-Shamir transform.
KnowledgeProof
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
// 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
// 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):
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
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
// 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
// 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
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
// 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
// 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
// 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
// Batch-create Pedersen commitments
void batch_commit(
const fast::Scalar* values,
const fast::Scalar* blindings,
PedersenCommitment* commitments_out,
size_t count);
GeneratorVectors
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:
#include <secp256k1.cuh>
CUDA Data Structures
// 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.
// 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
// 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
#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
#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
#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
// 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:
// 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:
#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
// 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
#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
// 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
// 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 inzk.cuhand 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.
// 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.
// 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:
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.
Example
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 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 |
Context Lifecycle
// 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 |
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 |
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 |
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 |
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 |
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) |
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 |
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).
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 |
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 |
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.
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 |
Multi-Scalar Multiplication
| Function | Signature | Description |
|---|---|---|
ufsecp_shamir_trick |
(ctx, a[32], P33[33], b[32], Q33[33], out33[33]) -> error_t |
Compute aP + bQ |
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.
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 |
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 == 0is 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 arithmeticid*1000+jscheme, 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 |
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:
adaptdivides out the adaptor secret andextractrecovers 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 |
Pedersen Commitments
| Function | Signature | Description |
|---|---|---|
ufsecp_pedersen_commit |
(ctx, value[32], blinding[32], commitment33_out) -> error_t |
C = valueH + blindingG |
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) |
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 |
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 |
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 |
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
-
Use in-place operations when possible:
// Slower: creates temporary point = point.add(other); // Faster: no allocation point.add_inplace(other); -
Use KPlan for fixed-K multiplication:
// 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); } -
Use
from_limbsfor binary I/O (notfrom_bytes):// 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
- Batch operations: Process thousands of points in parallel
- Avoid divergence: Use branchless algorithms where possible
- Memory coalescing: Align data structures to 32/64 bytes
- Use hybrid 32-bit multiplication: Enabled by default (
SECP256K1_CUDA_USE_HYBRID_MUL=1)
Examples
Generate Bitcoin Address (CPU)
#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)
#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)
#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 or GitHub repository.