Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

blindbit-v1-shim

The problem: BlindBit Oracle is the server that hands a wallet the per-block data it needs to find its own BIP-352 silent payments. Its HTTP API was rewritten this year, and the old one was removed. Every wallet-shaped client that exists today still speaks the old one: the Dana Android APK, spdk's backend-blindbit-v1 and its WASM backend, and BlindBit's own mobile apps. Only blindbit-cli and blindbit-desktop speak v2. So a v2 endpoint is unusable by almost every client ever written against it.

What this is: a translation layer. Point it at a v2 oracle, point an old client at it, and the client works unchanged.

wallet (v1 API)  ->  shim :8020  ->  oracle v2 :8010

One /full-block/{h} fetch per block, cached and single-flighted, serves all nine v1 routes for that block.

Run

go build -o v1shim . && ./v1shim -listen 127.0.0.1:8020 -oracle http://127.0.0.1:8010

Spent coverage: check it, do not assume it

This is the one thing to read before pointing a wallet with real money at it.

The v1 API identifies a spent output by sha256(txid || vout || blockhash)[:8], salted per block. v2 identifies it by the first 8 bytes of the spent output's x-only pubkey, unsalted. They are not interconvertible, so the v1 form has to be rebuilt from outpoints.

A stock /full-block/{h} only returns transactions that have outputs in the index, so it exposes outpoints only for transactions that themselves create silent payments. Everything else is dropped, including the outpoints the handler just read from the database. Measured against a full mainnet index over blocks 965060-965089, that is 13,276 of 16,745 spends, 79.3%. The gap is biased against the case that matters: an ordinary spend of a received silent payment, by a transaction that does not itself create one. Concretely, 7 of the 19 spends of block 965060's indexed outputs are missing from /full-block/965061.

Consequence: a wallet on a stock oracle can show an already-spent output as spendable.

So the shim requests /full-block/{h}?include_input_only=1, and measures at startup whether the oracle honours it, by comparing its reconstructed spent set for one block against the oracle's own count:

$ curl -s localhost:8020/shim/status
{"spent_coverage":"complete","probe_height":965204,"shim_spent":634,"oracle_spent":634}

The same result is logged, as a warning when coverage is partial. Against a patched oracle the reconstruction is exact: 16,745 of 16,745 over the same 30 blocks.

That parameter is not upstream yet. It is branch feat/full-block-include-input-only, and the upstream question about the dropped outpoints is setavenger/blindbit-oracle#58. It is opt-in rather than the default for a concrete reason: an input-only transaction carries an all-zero tweak, and spdk's v2 backend parses that field with PublicKey::from_slice, which rejects the whole response. Making completeness the default would break existing v2 clients.

Routes

v1 route notes
GET /info passthrough, the shape did not change
GET /block-height v2 dropped the route, kept the field on /info
GET /block-hash/{h}
GET /tweaks/{h}?dustLimit= full index, see "Cut-through"
GET /tweak-index/{h}?dustLimit=
GET /filter/new-utxos/{h} rebuilt, see "Filters"
GET /filter/spent/{h} rebuilt, see "Spent coverage"
GET /utxos/{h}
GET /spent-index/{h}
POST /forward-tx 501, this shim does not broadcast
GET /shim/status not v1, the coverage probe result

Where the two APIs genuinely differ

Filters. v2 removed them. The builder is still in its tree but nothing calls it and nothing is stored, so the shim rebuilds both per block: the same btcsuite GCS code, the same DefaultP and DefaultM, the same key derivation from the internal-order block hash. The btcsuite versions in go.mod are pinned to the ones blindbit-oracle itself uses, so the bytes match by construction rather than by reimplementation.

One deliberate divergence: v1's new-utxos filter covered every taproot output in the block, this one covers every taproot output of the indexed transactions. That is a subset, so the filter is smaller with fewer false positives, and it cannot produce a false negative for silent payment scanning, because a payment to the user is by definition in an indexed transaction.

Byte order, twice. v2 serialises an input as a display-order txid followed by a big-endian vout. v1 hashed an internal-order txid followed by a little-endian vout, salted with the internal-order block hash. Both flip. Getting either wrong produces identifiers that look fine and that no client ever matches.

Cut-through. v1 /tweaks served the cut-through list and /tweak-index the full one. v2 exposes no cut-through tweak list over HTTP, so both routes return the full index. That over-delivers tweaks, which costs the client work and never hides a payment from it.

utxos.timestamp is always 0 and utxos.spent is always false. v1 marked both "not used" in its own source, and spdk parses them but drops them when converting to UtxoData.

Tests

The conformance tests run against a live v2 oracle. They are anchored to mainnet block 965085, which carries a 50,000-sat silent payment found blind with the BIP-352 reference implementation.

ORACLE=http://127.0.0.1:8010 go test -v ./...

They assert that the new-utxos filter matches every indexed output in the block, that it rejects an unrelated key, and that the spent filter matches every identifier the shim publishes. A filter that silently matches nothing is the failure mode worth guarding.

Files

  • main.go: flags, startup coverage probe.
  • v2client.go: the v2 client, the per-block view every route is derived from, and the v1 spent-identifier reconstruction.
  • filters.go: GCS filter construction, byte-compatible with v1.
  • v1api.go: the nine v1 routes plus /shim/status.
  • conformance_test.go: the checks above.

MIT. From the operators of https://silentpayments.net. Related: silentpayments-measurements, which measures what each light-client design actually costs and carries a converged protocol pre-draft.

About

Serves the removed BlindBit Oracle v1 HTTP API in front of a v2 oracle, so existing BIP-352 silent payments clients keep working. Measures its own spent-index coverage rather than claiming it.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages