diff --git a/.gitignore b/.gitignore index b9474a2..dba8466 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,10 @@ dist/ .env .env.local *.local +.DS_Store +.specstory +.cursorindexingignore +.cursor +.vscode +.pnpm-store +.qwen diff --git a/slides/public/chainalysis-logo.svg b/slides/public/chainalysis-logo.svg new file mode 100644 index 0000000..32cfa97 --- /dev/null +++ b/slides/public/chainalysis-logo.svg @@ -0,0 +1,22 @@ + + Chainalysis logo + Chainalysis wordmark + + + + + + + + + Chainalysis + + diff --git a/slides/public/demo-2x-fast.mp4 b/slides/public/demo-2x-fast.mp4 new file mode 100644 index 0000000..72ae3ac Binary files /dev/null and b/slides/public/demo-2x-fast.mp4 differ diff --git a/slides/slides.md b/slides/slides.md index 9038a0c..b0a7513 100644 --- a/slides/slides.md +++ b/slides/slides.md @@ -9,72 +9,133 @@ fonts: lineNumbers: false drawings: persist: false -transition: slide +transition: fade +colorSchema: dark mdc: true --- -# STEALTH - -### Bitcoin Wallet Privacy Analyzer - -A privacy audit tool that surfaces vulnerabilities at the UTXO level. +
+ +

STEALTH

+

Bitcoin Wallet Privacy Analyzer

+

A read-only audit engine that surfaces wallet exposure at the UTXO level before funds move.

+
+ No keys + UTXO-level findings + Self-hostable +
+
--- # The Problem - -
- -
- -**Bitcoin privacy is fragile** - -- Chain analysis firms track wallets -- Common heuristics link addresses -- Users rarely know their exposure -- One bad UTXO can taint the rest - -
- -
- -**Today's tools** - -- Complex, require expertise -- No UTXO-level visibility -- Hard to understand risk before spending - -
- +
+
+

Visibility gap

+

Bitcoin privacy leaks are invisible to users

+ +
+

Companies can analyze your privacy better than you can. +

--- -# What Stealth Does +# Why This Happens +
+
+

Privacy is broken by patterns, not hacks

-
+Common wallet patterns that leak privacy: -
+- Multi-input transactions (CIOH / consolidation) +- Combining coins +- Address reuse +- Sending change to same input address +- Dust UTXOs +- Exchange linkage / taint signals +
-**Input** -- Paste wallet descriptor -- Supports `wpkh`, `pkh`, `sh(wpkh)`, `tr`, multisig +--- -**Output** -- Structured findings + warnings -- Type/severity/description + evidence details -- Severity badges mapped from detector output +## Visibility Imbalance + +

Chainalysis users can see wallet-linkage signals that the average user cannot see about themselves.

+ +
+
+
+
+
+ Chainalysis +
+
+
+ user +
+
+
+
+ +--- + +## Privacy Parity + +

With Stealth, users gain visibility closer to institutional-grade analysis.

+ +
+
+
+
+
+ Chainalysis +
+
+
+
+ user + STEALTH +
+
+
+
+
+ +--- + +## What Stealth Does + +
+ +
+ +

Input

+
    +
  • Paste a wallet descriptor
  • +
  • Supports wpkh, pkh, sh(wpkh), tr, multisig
  • +
+ +

Output

+
    +
  • Structured findings plus warnings
  • +
  • Type, severity, description, and evidence
  • +
  • Severity badges mapped directly from detectors
  • +
-
+
```bash -# One click -wpkh([xpub...]/0/*) → Analyze +# one click +wpkh([xpub...]/0/*) -> Analyze ``` -→ Full report with actionable insights +

=> Full report with actionable, spend-aware insights

@@ -82,49 +143,67 @@ wpkh([xpub...]/0/*) → Analyze --- -# Vulnerabilities Detected +## Vulnerabilities Detected +
+ + + + + + + + + + + + + + + + + + + +
Detector TypeMeaning
ADDRESS_REUSERepeated receive address links payment history
CIOHMulti-input ownership clustering signal
DUST / DUST_SPENDINGDust + normal co-spend linkage pattern
CHANGE_DETECTIONPayment and change outputs become distinguishable
CONSOLIDATION / CLUSTER_MERGEInput histories merged into one traceable cluster
SCRIPT_TYPE_MIXINGMixed script families create a wallet fingerprint
UTXO_AGE_SPREADOld/new spread leaks dormancy behavior
EXCHANGE_ORIGINProbable exchange withdrawal origin signature
TAINTED_UTXO_MERGETainted + clean merge propagates contamination
BEHAVIORAL_FINGERPRINTConsistent transaction style re-identifies wallet
-| Detector Type | Meaning | -|---------------|---------| -| `ADDRESS_REUSE` | Same address received multiple payments, linking history | -| `CIOH` | Multi-input ownership clustering signal | -| `DUST` / `DUST_SPENDING` | Dust detection and dust+normal co-spend linkage | -| `CHANGE_DETECTION` | Payment/change outputs become easy to distinguish | -| `CONSOLIDATION` / `CLUSTER_MERGE` | Input histories merged into one cluster | -| `SCRIPT_TYPE_MIXING` | Mixed input script families create fingerprint | -| `UTXO_AGE_SPREAD` | Old/new UTXO spread leaks dormancy patterns | -| `EXCHANGE_ORIGIN` | Probable exchange batch-withdrawal origin | -| `TAINTED_UTXO_MERGE` | Tainted + clean input merge propagates taint | -| `BEHAVIORAL_FINGERPRINT` | Transaction style consistency re-identifies wallet | -| Warnings: `DORMANT_UTXOS`, `DIRECT_TAINT` | Non-finding risk signals shown separately | +

Warnings: DORMANT_UTXOS and DIRECT_TAINT are shown as contextual risk signals.

--- -# How It Works +## How It Works +
+
-
+
-
- -**1. Parse** -- Extract addresses from descriptor -- Support all common formats +

01

+

Parse

+
    +
  • Extract addresses from descriptor
  • +
  • Normalize all common formats
  • +
-
+
-**2. Fetch** -- On-chain history per address -- Uses Bitcoin node / API +

02

+

Fetch

+
    +
  • Load on-chain history per address
  • +
  • Use Bitcoin node or indexed API source
  • +
-
+
-**3. Analyze** -- Apply privacy heuristics -- Flag each UTXO with findings +

03

+

Analyze

+
    +
  • Apply privacy heuristics and warnings
  • +
  • Flag each UTXO with findings and evidence
  • +
@@ -132,51 +211,105 @@ wpkh([xpub...]/0/*) → Analyze --- -# Architecture +## Architecture -``` +```txt stealth/ -├── frontend/ # React + Vite — input, loading, report -└── backend/ # Java/Quarkus — descriptor parsing, chain data, analysis +|-- frontend/ # React + Vite: input, loading, report +`-- backend/ # Java/Quarkus: parsing, chain data, analysis ``` -- **Read-only** — no keys, no storage, no transmission of descriptors -- **Self-hostable** — point at your own node for max privacy +
+
+

Security Model

+

Read-only

+

No private keys, no descriptor storage, no hidden transmission path.

+
+
+

Deployment

+

Self-hostable

+

Point to your own node for maximum privacy and deterministic trust.

+
+
--- -# Demo Flow +## Demo Flow -1. **Input screen** — paste descriptor, click Analyze -2. **Loading** — fetches and analyzes -3. **Report** — summary bar (findings / warnings / tx analyzed) -4. Expandable finding cards: type, severity, description, structured evidence +
+
    +
  1. Input Paste descriptor and trigger analysis
  2. +
  3. Load Fetch + parse + detect in one pipeline
  4. +
  5. Report Summary bar: findings / warnings / transactions
  6. +
  7. Inspect Expand finding cards for severity and evidence payloads
  8. +
+
--- -# Why It Matters +## Demo -- **Users** — understand exposure before consolidating or spending -- **Wallets** — integrate as pre-spend check -- **Researchers** — study privacy heuristics at scale -- **Privacy-first** — no cloud, no logs, no tracking +
+ +

2x playback and compressed for lightweight deck rendering.

+
--- -# Thank You +## Roadmap -**STEALTH** +
+
+

Expanded Heuristics

+
    +
  • LEGACY_SCRIPT_EXPOSURE — old script usage (p2pkh / nested-only flows) shrinking anonymity set
  • +
  • ADDRESS_GAP_LEAK — sparse derivation usage exposing wallet generation behavior
  • +
  • AMOUNT_FINGERPRINT — repeated denomination templates across spends
  • +
  • TIME_PATTERN_FINGERPRINT — recurring timing cadence linking sessions
  • +
+
+
+

Improvements

+
    +
  • Mainnet Support

  • +
  • Mobile Support

  • +
  • Cluster Visualization

  • +
  • One-click solution
  • +
+
+
-Bitcoin Wallet Privacy Analyzer +

Roadmap detectors are additive and keep the same read-only, no-key security model.

--- -# Appendix — Supported Descriptors +
+

Thank You

+

STEALTH

+

Bitcoin Wallet Privacy Analyzer

+

Protect privacy before you broadcast intent.

+
-- `wpkh(...)` — native SegWit -- `pkh(...)` — legacy -- `sh(wpkh(...))` — nested SegWit -- `tr(...)` — Taproot -- Multisig variants +--- -All analysis uses publicly available on-chain data. +## Appendix — Supported Descriptors + +
+
    +
  • wpkh(...) — native SegWit
  • +
  • pkh(...) — legacy
  • +
  • sh(wpkh(...)) — nested SegWit
  • +
  • tr(...) — Taproot
  • +
  • Multisig variants
  • +
+
+ +

All analysis relies only on publicly available on-chain data.

diff --git a/slides/style.css b/slides/style.css index beb0d75..370256b 100644 --- a/slides/style.css +++ b/slides/style.css @@ -1,84 +1,319 @@ -/* Stealth theme overrides — matches frontend App.css */ +/* Global Stealth deck skin */ :root { --slidev-theme-primary: #00d4aa; --slidev-theme-accent: #00d4aa; - --stealth-bg: #080c14; - --stealth-surface: #0f1623; - --stealth-surface-2: #162030; - --stealth-border: #1e2d45; - --stealth-accent: #00d4aa; - --stealth-danger: #ff4d6d; - --stealth-warning: #f4a261; - --stealth-safe: #2ec4b6; - --stealth-text: #e8edf5; - --stealth-text-muted: #6b7a99; + --bg: #080c14; + --surface: #0f1623; + --surface-2: #162030; + --border: #1e2d45; + --border-hover: #2a3f5e; + --accent: #00d4aa; + --accent-dim: rgba(0, 212, 170, 0.14); + --accent-glow: rgba(0, 212, 170, 0.32); + --danger: #ff4d6d; + --warning: #f4a261; + --text: #e8edf5; + --text-muted: #93a2bf; } .slidev-layout { - background-color: var(--stealth-bg) !important; - color: var(--stealth-text) !important; - font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important; + background: + radial-gradient(1200px 500px at 90% -20%, rgba(0, 212, 170, 0.12), transparent 60%), + radial-gradient(900px 500px at -10% 110%, rgba(46, 196, 182, 0.08), transparent 60%), + var(--bg); + color: var(--text); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; } -.slidev-layout h1, -.slidev-layout h2, -.slidev-layout h3 { - color: var(--stealth-text) !important; - font-weight: 700; +h1, +h2, +h3 { letter-spacing: -0.02em; } -.slidev-layout h1 { - font-size: 2.5rem !important; +h2 { + margin-bottom: 1rem; + font-size: 2.2rem; } -.slidev-layout h1 .accent { - color: var(--stealth-accent) !important; +.hero-wrap { + display: grid; + gap: 0.9rem; + padding: 2.2rem; + border: 1px solid var(--border); + border-radius: 18px; + background: linear-gradient(145deg, rgba(22, 32, 48, 0.65), rgba(15, 22, 35, 0.9)); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(0, 212, 170, 0.12) inset; } -.slidev-layout p, -.slidev-layout li { - color: var(--stealth-text-muted) !important; +.hero-wrap.end { + margin-top: 2rem; } -.slidev-layout a { - color: var(--stealth-accent) !important; +.eyebrow { + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--text-muted); + font-size: 0.72rem; + font-weight: 700; } -.slidev-layout code { - font-family: 'JetBrains Mono', 'Fira Code', monospace !important; - background: var(--stealth-surface) !important; - color: var(--stealth-accent) !important; - border: 1px solid var(--stealth-border) !important; - border-radius: 6px !important; +.hero-title { + font-size: 4.4rem; + line-height: 0.95; + margin: 0; } -.slidev-layout .badge-danger { - background: rgba(255, 77, 109, 0.15); - color: var(--stealth-danger); - border: 1px solid rgba(255, 77, 109, 0.3); +.accent { + color: var(--accent); + text-shadow: 0 0 20px var(--accent-glow); } -.slidev-layout .badge-warning { - background: rgba(244, 162, 97, 0.15); - color: var(--stealth-warning); - border: 1px solid rgba(244, 162, 97, 0.3); +.hero-subtitle { + margin: 0; + color: #c7d2e7; + font-size: 1.25rem; } -.slidev-layout .badge-safe { - background: rgba(46, 196, 182, 0.15); - color: var(--stealth-safe); - border: 1px solid rgba(46, 196, 182, 0.3); +.hero-copy { + margin: 0; + max-width: 70ch; + color: var(--text-muted); } -.slidev-layout .card { - background: var(--stealth-surface) !important; - border: 1px solid var(--stealth-border) !important; - border-radius: 12px !important; +.hero-chips { + display: flex; + gap: 0.6rem; + flex-wrap: wrap; } -.slidev-layout .btn-primary { - background: var(--stealth-accent) !important; - color: #080c14 !important; - font-weight: 700 !important; +.chip { + border: 1px solid var(--border); + border-radius: 999px; + padding: 0.2rem 0.65rem; + font-size: 0.78rem; + color: #c5d3eb; + background: rgba(22, 32, 48, 0.65); +} + +.chip-safe { + border-color: rgba(0, 212, 170, 0.32); + color: var(--accent); + background: var(--accent-dim); +} + +.split { + display: grid; + gap: 1rem; +} + +.split.two { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.split.three { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.panel { + border: 1px solid var(--border); + border-radius: 14px; + padding: 1rem; + background: linear-gradient(180deg, rgba(15, 22, 35, 0.95), rgba(13, 19, 31, 0.95)); +} + +.code-panel pre { + margin-top: 0.2rem; +} + +.kicker { + margin: 0 0 0.45rem; + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: 0.7rem; + color: var(--text-muted); +} + +.strong { + margin: 0; + font-size: 1.1rem; + font-weight: 700; +} + +.muted { + margin: 0.45rem 0 0; + color: var(--text-muted); +} + +.list { + margin: 0; + padding-left: 1.1rem; + display: grid; + gap: 0.42rem; +} + +.mt { + margin-top: 1rem; +} + +.result-arrow { + margin: 0.6rem 0 0; + color: var(--accent); + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 0.9rem; +} + +.detector-table { + width: 100%; + border-collapse: collapse; + border: 1px solid var(--border); + border-radius: 10px; + overflow: hidden; + background: rgba(15, 22, 35, 0.88); + font-size: 0.86rem; +} + +.detector-table th, +.detector-table td { + border-bottom: 1px solid rgba(30, 45, 69, 0.7); + padding: 0.5rem 0.62rem; + text-align: left; + vertical-align: top; +} + +.detector-table th { + color: #b6c5de; + background: rgba(22, 32, 48, 0.85); + text-transform: uppercase; + letter-spacing: 0.06em; + font-size: 0.72rem; +} + +.step-index { + font-family: 'JetBrains Mono', 'Fira Code', monospace; + color: var(--accent); + margin: 0; +} + +.flow { + margin: 0; + padding-left: 1.2rem; + display: grid; + gap: 0.55rem; +} + +.flow span { + color: var(--accent); + font-weight: 700; + margin-right: 0.3rem; +} + +.accent-panel { + border-color: rgba(0, 212, 170, 0.26); + box-shadow: 0 0 0 1px rgba(0, 212, 170, 0.1) inset; +} + +.footnote { + margin-top: 0.8rem; + color: var(--text-muted); + font-size: 0.83rem; +} + +code { + font-family: 'JetBrains Mono', 'Fira Code', monospace; +} + +/* Seesaw slide */ +.seesaw-wrap { + display: flex; + justify-content: center; + align-items: center; + min-height: 340px; + padding: 1rem; +} +.seesaw { + position: relative; + width: 100%; + max-width: 560px; +} +.seesaw-beam-bar { + position: absolute; + left: 50%; + top: 58%; + transform: translate(-50%, -50%) rotate(-14deg); + width: 90%; + height: 8px; + background: linear-gradient(90deg, var(--border), var(--border-hover), var(--border)); + border-radius: 4px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} +.seesaw-beam { + display: flex; + align-items: flex-start; + justify-content: center; + position: relative; + height: 180px; +} +.seesaw-pivot { + position: absolute; + left: 50%; + bottom: 0; + transform: translateX(-50%); + width: 0; + height: 0; + border-left: 14px solid transparent; + border-right: 14px solid transparent; + border-bottom: 24px solid var(--border); + z-index: 2; +} +.seesaw-side { + display: flex; + align-items: center; + justify-content: center; + flex: 1; + min-height: 96px; + padding: 0.8rem; + transition: transform 0.4s ease; +} +.seesaw-side.heavy { + transform: translateY(28px) rotate(-16deg); +} +.seesaw-side.light { + transform: translateY(-28px) rotate(16deg); +} +.chainalysis-wordmark { + font-size: 2.4rem; + font-weight: 800; + letter-spacing: -0.02em; + color: #ff6f2c; + text-shadow: 0 0 14px rgba(255, 111, 44, 0.22); +} +.user-label { + font-size: 1.4rem; + font-weight: 700; + color: var(--accent); + text-shadow: 0 0 12px var(--accent-glow); +} + +.user-stealth-stack { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.25rem; +} + +.stealth-wordmark { + font-size: 1.65rem; + font-weight: 800; + letter-spacing: 0.04em; + color: var(--text); +} + +.seesaw-balanced .seesaw-beam-bar { + transform: translate(-50%, -50%) rotate(0deg); +} + +.seesaw-balanced .seesaw-side.heavy, +.seesaw-balanced .seesaw-side.light { + transform: translateY(0) rotate(0deg); }