UltrafastSecp256k1/docs/API_REFERENCE.md
2026-03-25 14:36:36 +00:00

74 KiB

UltrafastSecp256k1 API Reference

Complete API documentation for CPU, CUDA, and WASM implementations.


Table of Contents

  1. CPU API
  2. Address Generation API
  3. Multi-Chain Coins API
  4. Unified Wallet API
  5. BIP-39 Mnemonic Seed Phrases
  6. Message Signing API
  7. Zero-Knowledge Proof API
  8. CUDA API
  9. WASM API
  10. C ABI (ufsecp)
  11. Performance Tips
  12. 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_p2wpkh for 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 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.

// 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 == 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

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

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

  1. Use in-place operations when possible:

    // Slower: creates temporary
    point = point.add(other);
    
    // Faster: no allocation
    point.add_inplace(other);
    
  2. 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);
    }
    
  3. Use from_limbs for binary I/O (not from_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

  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)

#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.