public inbox for bitcoindev@googlegroups.com
 help / color / mirror / Atom feed
From: Ali Sherief <ali@notatether.com>
To: Bitcoin Development Mailing List <bitcoindev@googlegroups.com>
Subject: [bitcoindev] spscand and libspscan: a watch-only BIP352 scanner
Date: Sat, 14 Nov 2026 00:40:02 -0800 (PST)	[thread overview]
Message-ID: <05fbac98-5a91-4224-ad9b-745441252e26n@googlegroups.com> (raw)


[-- Attachment #1.1: Type: text/plain, Size: 5014 bytes --]

Hi all,

I have published spscand, a watch-only scanner for BIP352 silent payments 
(version 1.1.2), and libspscan, the C library it scans with:

https://github.com/ZenulAbidin/spscand  (MIT)

What it does

spscand holds the scan private key and the spend public key. It finds 
outputs paid to an sp1 address and its labels, tracks spends and reorgs, 
and reports them over a local read-only HTTP API and a webhook. For each 
output it stores the tweak t_k, so a separate signer can derive d = b_spend 
+ t_k. It handles confirmed blocks only.

How scanning stays private

It never queries a server about an address, output or transaction. Also 
there are three block sources:

1. Bitcoin Core (25+) over RPC. Tweaks are computed locally from getblock 
verbosity 3. Nothing leaves the machine.

2. A BlindBit Oracle server. For every block from the wallet birthday to 
the tip it downloads all tweaks, all taproot outputs and the spent-key 
prefixes, and matches locally. Every block is requested the same way 
whether or not it pays the wallet. The server learns the client's IP 
address (unless Tor is used), the birthday and when the client is online or 
rescans. It can also withhold data and so hide a payment.

3. Filters, the strategy in BIP352's appendix. Tweaks still come from 
BlindBit, for every block. The block's BIP158 basic filter comes from P2P 
peers (BIP157). The client computes its candidate k = 0 output keys, tests 
them and its unspent outputs against the filter, and downloads the block 
only on a match. The peer serving that block learns it was of interest, so 
each block is fetched from a randomly chosen peer over a connection of its 
own.

A peer could hide a payment by serving a false filter. Filter headers are 
therefore compared across all reachable peers, and the user is told to 
configure at least two. The P2P transport is v1 (no BIP324 yet).

With a SOCKS5 proxy, names are resolved by the proxy, proxy environment 
variables are ignored, and each process, and in filter mode each P2P 
connection, uses random SOCKS credentials so that Tor with IsolateSOCKSAuth 
gives it circuits of its own.

libspscan

libspscan does the BIP352 receiving side and the BIP158 filter checks. It 
does no I/O, never takes a spend private key, and depends only on the C 
standard library, so it can be used under other wallets or indexers. 
spscand calls it through ctypes.

- It bundles libsecp256k1 v0.8.0, unmodified (the README gives the commit 
and a diff command). Shared secrets, output tweaks and labels come from its 
silentpayments module.
- libsecp256k1 is compiled into the same translation unit so that label 
matching can use its field arithmetic. Only spscan_* symbols are exported.
- A tweak server does not say which transaction a tweak belongs to, so a 
block's tweaks are tried against all of its taproot outputs. Each label 
adds a candidate x(P_k + L) per tweak. These are added in affine 
coordinates with one field inversion per batch of 1024. When labels 
outnumber twice the outputs, output - P_k is looked up among the labels 
instead, as BIP352 suggests.
- The shared secret b_scan * (input_hash * A) is computed in constant time. 
Matching after that branches on public output keys and on P_k, as 
libsecp256k1's own scanner does.
- Lookup tables are keyed with SipHash under a key derived from the scan 
key, so a sender cannot choose output keys that collide.

Measurements, mainnet block 833044 (721 eligible transactions, 2105 taproot 
outputs):

- BlindBit: 135 kB downloaded; 0.05 s CPU, 0.16 s with 1000 labels.
- Filters: 28 kB of tweaks and a 31 kB filter; 0.05 s CPU.
- The filter check tests tweaks * (1 + labels) items. The chance of 
downloading this block for nothing is 0.2% with no labels, 1% with 10 and 
9% with 100. With 1000 labels most blocks match.

Tests

- The BIP352 receiving test vectors, in both the C and the Python code, 
with every result compared between the two.
- Bitcoin Core's BIP158 vectors. libspscan builds the same filters byte for 
byte.
- Mainnet block 833044 checked against a real BlindBit index.
- The filters backend against fake peers, including peers that serve false 
filters.
- An end-to-end run on regtest with Bitcoin Core 27.1: payments from the 
BIP352 reference sender with P2WPKH, P2SH-P2WPKH, P2PKH and P2TR inputs, 
then labels, spending, spend detection, reorgs and rescans. In filter mode 
it checks that only blocks paying or spending the wallet's outputs were 
downloaded, and with a SOCKS proxy that nothing bypassed it.

Corrections and criticism are welcome, on the list or as GitHub issues.

Ali Sherief
bitmixlist.org

-- 
You received this message because you are subscribed to the Google Groups "Bitcoin Development Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bitcoindev+unsubscribe@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/05fbac98-5a91-4224-ad9b-745441252e26n%40googlegroups.com.

[-- Attachment #1.2: Type: text/html, Size: 5486 bytes --]

                 reply	other threads:[~2026-11-14 12:34 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=05fbac98-5a91-4224-ad9b-745441252e26n@googlegroups.com \
    --to=ali@notatether.com \
    --cc=bitcoindev@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox