Bitcoin full node and query server

Jun 6, 2026

View in context in the libbitcoin Slack #general mirror.

Vano Chkheidze09:22
UltrafastSecp256k1 ⇄ libbitcoin — 4-table batch verify: throughput + usage The bridge now supports your full 4-table batching model first-class. At the verify boundary the four tables collapse to two verify kinds: a multisig row is one ECDSA signature, a threshold (tapscript CHECKSIG / CHECKSIGADD) row is one Schnorr signature. The m|n + group bytes are accounting metadata carried in the opaque tail and are never interpreted by the bridge (m-of-n satisfaction stays your job, downstream of the per-row verdict). Throughput (measured) bench_lbtc_4table, measured on this machine — Intel i5-14400F + NVIDIA RTX 5060 Ti, performance governor, best-of-4, 1,000,000 signatures per table, end-to-end (host de-interleave + H2D transfer + kernel + per-row result copy — what a node actually pays, not kernel-only). tablerecord / tailGPU (CUDA)CPU (1 core, reference)ecdsaEcdsaRecord(129) + 33.84 M/s0.034 M/s (~29.5 s / 1M)multisigEcdsaRecord(129) + 63.84 M/s0.034 M/s (~29.3 s / 1M)schnorrSchnorrRecord(128) + 34.90 M/s0.074 M/s (~13.5 s / 1M)thresholdSchnorrRecord(128) + 64.88 M/s0.074 M/s (~13.7 s / 1M) Key result — the m-of-n tail is free. multisig/threshold (6-byte m|n+group+block-fk tail) verify at the same rate as the single ecdsa/schnorr tables (3-byte tail), on both backends. The verify core reads only the leading record and walks stride = record + key_size, so the accounting tail costs nothing — measured, not inferred. Notes:
  • CPU is thesingle-core mandatory reference(the consensus anchor, always available). A node parallelizes it across cores; the figure above is per-core.
  • GPU figures are end-to-end for a saturating 1M batch; smaller batches pay relatively more transfer/launch overhead.
  • Numbers are valid only for this machine; re-measure elsewhere.
Usage
#include "ufsecp_libbitcoin.h"
using namespace ufsecp::lbtc;

Controller ctrl;                       // AUTO: GPU if present, else CPU reference
if (!ctrl.ok()) { /* no usable backend — the only failure is reported here */ }
1. Single ECDSA / Schnorr table — results[]
// rows: n × (EcdsaRecord(129) + block-fk(3)); key_size = 3 (the opaque tail)
std::vector<uint8_t> results(n);
ctrl.verify_ecdsa(rows, n, /*key_size=*/3, results.data());   // results[i] == 1 valid / 0 invalid
ctrl.verify_schnorr(srows, n, /*key_size=*/3, results.data());
2. Multisig / threshold table — typed-span, zero-copy
// Your packed table forwards directly; key_size = sizeof(Row) - record is recovered from the type.
std::span<const MultisigRow>  ms = ...;   // EcdsaRecord  + m|n + group + block-fk (135B)
std::span<const ThresholdRow> th = ...;   // SchnorrRecord + m|n + group + block-fk (134B)
ctrl.verify_multisig (ms, r1.data());     // == ECDSA path,  key_size 6
ctrl.verify_threshold(th, r2.data());     // == Schnorr path, key_size 6
3. Collect — rejected-id list (no results[] array)
// In-place: VALID rows get their key tail zeroed; INVALID rows keep it intact.
std::span<MultisigRow> batch = ...;       // MUTABLE
ctrl.collect_multisig(batch);
for (auto& row : batch)                    // survivors = the rejected set (m|n+group+block-fk intact)
    if (tail_nonzero(row)) rejected.push_back(row);
4. Pure C ABI (no C++) — the 4 tables map onto 2 calls
// multisig record IS the ECDSA record; threshold record IS the Schnorr record.
// m|n+group+block-fk is the opaque key tail (key_size = 6).
ufsecp_lbtc_verify_ecdsa  (ctrl, ms_rows, n, /*key_size=*/6, results); // multisig table
ufsecp_lbtc_verify_schnorr(ctrl, th_rows, n, /*key_size=*/6, results); // threshold table
ufsecp_lbtc_verify_ecdsa  (ctrl, e_rows,  n, /*key_size=*/3, results); // ecdsa table
ufsecp_lbtc_verify_schnorr(ctrl, s_rows,  n, /*key_size=*/3, results); // schnorr table
Canonical packed rows (on-wire source of truth, #pragma pack(1)):
struct MultisigRow  { EcdsaRecord   record; uint8_t mn; uint8_t group[2]; uint8_t block_fk[3]; }; // 135
struct ThresholdRow { SchnorrRecord record; uint8_t mn; uint8_t group[2]; uint8_t block_fk[3]; }; // 134