Skip to content

Commit 9999efc

Browse files
liuchengxuclaude
andauthored
Add web-based block explorer and transaction indexer (#98)
* Add web-based block explorer for Subcoin Initial implementation of a React-based block explorer that connects to a running Subcoin node via JSON-RPC. Features: - Dashboard with latest blocks and network status - Block detail view with transaction list - Transaction detail view with inputs/outputs - Network status page with peer information - Dark theme with Bitcoin orange accents Tech stack: - React 18 + TypeScript - Vite (bundler with RPC proxy for CORS) - Tailwind CSS - Bun (package manager) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add real-time updates, connection status, and search to explorer - WebSocket client subscribing to chain_subscribeNewHeads for live block updates - Connection status indicator with auto-reconnect and exponential backoff - Search bar supporting block height, block hash, and transaction ID lookups - Dashboard shows "Live" indicator and last update timestamp when connected - Fallback to 60-second polling when WebSocket is disconnected 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add UI polish: loading skeletons, mobile layout, copy buttons, confirmations Short-term improvements: - Loading skeleton components for Dashboard, BlockDetail, TransactionDetail, Network - Responsive mobile layout with hamburger menu and collapsible navigation - Copy-to-clipboard buttons for block hashes, merkle roots, and transaction IDs - Block confirmation count badge (green for 6+, yellow for 1-5) - Responsive table columns that hide on smaller screens 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add transaction linking, editable endpoint, and fix sync target display - Add blockchain_getBlockWithTxids RPC methods to return blocks with pre-computed transaction IDs for clickable tx links in explorer - Make RPC endpoint editable in header (persisted to localStorage) - Fix sync target display by correctly parsing network_status response format (downloading/importing with nested target field) - Remove broken Network page temporarily - Add dynamic Vite proxy routing to support custom endpoints 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add paginated Block List page - New /blocks route with paginated block list (25 per page) - First/Prev/Next/Last navigation buttons - Block counter showing range and page info - Jump to block feature for quick navigation - Added Blocks link to navigation (desktop and mobile) - Added "View all blocks" link on Dashboard 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Improve transaction indexer robustness with state machine - Add IndexerState enum to track indexer state for crash recovery - Fix bug: fresh start on existing chain now indexes all blocks - Save progress every 1000 blocks during historical indexing - Add progress logging with ETA during historical indexing - Replace panic with graceful error handling in run() - Remove unused legacy functions and constants 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add Bitcoin Core compatible RPC layer for electrs integration Introduces subcoin-rpc-bitcoind crate providing Bitcoin Core-style RPC methods that enable Subcoin to work with electrs and other Bitcoin ecosystem tools. New RPC methods (enabled with --bitcoind-rpc flag): - Blockchain: getbestblockhash, getblockchaininfo, getblockcount, getblockhash, getblock, getblockheader - Raw transactions: getrawtransaction, sendrawtransaction, decoderawtransaction - Network: getnetworkinfo - Mempool: getrawmempool, getmempoolinfo, getmempoolentry (placeholder) - Mining: estimatesmartfee Both subcoin-rpc (explorer) and subcoin-rpc-bitcoind (electrs) APIs can run simultaneously on the same RPC server. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add transaction pagination to Block details page Implements client-side pagination for transactions in the block detail view with 25 transactions per page. Features include: - Page navigation with First/Prev/Next/Last buttons - Clickable page numbers with ellipsis for large page counts - Shows "Showing X to Y of Z transactions" status - Preserves global transaction index (e.g., #50, #51, not #0, #1) - Smooth scroll to transactions section on page change - URL-based page state (?page=N) for shareable links 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add block size and weight display to Block details page Display estimated block statistics including: - Size (in bytes/KB/MB) - Weight (in WU/kWU/MWU) - Virtual size (vsize) - SegWit transaction count and percentage Uses client-side calculation based on transaction data according to BIP 141 weight formula: base_size * 4 + witness_size 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Improve initial block download (IBD) sync performance This commit addresses several bottlenecks identified during initial sync from the Bitcoin p2p network: 1. Preserve downloaded blocks on peer switch - Modified restart() to keep already-downloaded blocks in memory - Only clears pending requests, not completed downloads - Prevents wasting bandwidth when sync peer changes 2. Relax ping latency threshold during IBD - Added PEER_LATENCY_THRESHOLD_INITIAL_SYNC (5s vs 2s normal) - During major syncing, higher latency peers are tolerated - Reduces unnecessary sync restarts due to high-latency disconnections 3. Add block request pipelining - Added should_pipeline() method to trigger next batch at 50% completion - Reduces idle time between batch downloads - Improves single-peer throughput utilization 4. Fix stall detection during header download - Added touch_progress() to update last_progress_time on header receipt - Prevents false stall detection during long header downloads - Applied to both regular and prefetch headers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add address indexing with RPC endpoints and explorer UI Backend: - Rewrite subcoin-indexer with SQLite-based address/UTXO indexing - Add batch processing (100 blocks/transaction) during historical sync - Use per-block transactions during live sync for quick availability - Add address_* RPC endpoints: getBalance, getHistory, getUtxos, getTxCount - Add address_indexerStatus RPC to expose sync progress - Add blockchain_decodeScriptPubkey RPC for address decoding - Make indexer database path network-aware (mainnet/testnet/signet/regtest) Frontend: - Add AddressView page with balance, transaction history, and UTXOs - Update TransactionDetail to show decoded addresses with links - Show indexer sync progress when transaction not found - Add address route to explorer App 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add address statistics, search, and parallel indexing - Add address statistics RPC (first/last seen, largest tx, counts) - Add collapsible statistics section in Address page (lazy-loaded) - Support Bitcoin address search in search bar - Improve endpoint settings UI with connection indicator - Remove redundant ConnectionStatus component - Add parallel block reading during historical indexing using rayon 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add rich address types display with script type badges - Add scriptType.ts utility to detect Bitcoin script types from hex: P2PKH, P2SH, P2WPKH, P2WSH, P2TR, P2PK, OP_RETURN, P2MS - Add ScriptTypeBadge component with color-coded badges for each type - Add OpReturnData component to decode OP_RETURN as readable text - Update TransactionDetail to show script type badges on outputs - Update AddressView to show script type badges on UTXOs - Add build script to shared package for TypeScript project references 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add Sankey diagram for transaction flow visualization - Create TransactionSankey component with pure SVG rendering - Display inputs on left, outputs on right with curved flow paths - Show script type badges (P2PKH, P2SH, SegWit, etc.) on outputs - Support hover highlighting to trace flow from specific inputs - Handle coinbase transactions with distinct styling - Add legend explaining node types (input, output, coinbase, OP_RETURN) - Integrate into TransactionDetail page between header and inputs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix transaction parsing for previous_output format The RPC returns previous_output as a string "txid:vout" but the TypeScript types expected an object {txid, vout}. Add transformation layer in blockchain API to parse the string format into objects. This fixes the transaction detail page and Sankey diagram which were broken when accessing inp.previous_output.txid on a string. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Make transaction Sankey diagram more compact - Reduce SVG dimensions (500x250 max instead of 700x300+) - Smaller node widths, gaps, and minimum heights - Compact padding and font sizes - Smaller legend with abbreviated labels - Add max-w-lg to constrain width on large screens 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Widen Sankey diagram nodes to show more of txid - Increase SVG width from 500 to 600 - Increase node width from 100 to 150 - Show more txid characters (12...6 instead of 8...8) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Increase Sankey diagram node heights for better readability - Increase minimum node height from 24px to 36px - Increase node gap from 4px to 6px - Simplify height calculation to ensure all nodes fit content - Dynamic SVG height based on number of nodes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Use fixed node height with independent centering per side - Each node has fixed 36px height regardless of count - Input and output sides center independently - SVG height adapts to the taller side - Simpler, more predictable layout 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Make Sankey node heights adaptive to available space - Target height of 180px for the diagram - Node heights adapt: 36px min to 60px max - 1 input = 60px tall, 2 outputs = ~71px each - More nodes = smaller nodes (down to 36px min) - Each side calculates independently 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add top padding for labels in Sankey diagram Reserve 28px at top for "Inputs" and "Outputs" labels, then center nodes in the remaining space below. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Enhance transaction detail with rich metadata and output spending status - Add output spending status API and display in TransactionSankey - Show spent/unspent badges with color coding (red/green) - Display clickable link to spending transaction with vin index - Track intra-batch spending in indexer for correct status - Enrich transaction detail page with new fields: - Size/Virtual Size and Weight (estimated from tx data) - Total Input value (fetched from previous outputs) - Fee and Fee Rate (sat/vB) calculated from inputs - outputs - SegWit/Legacy badge with distinct styling - RBF enabled/disabled indicator - Add witness data display in inputs section - Collapsible witness items with count - Sequence shown in hex with RBF indicator - Improve TransactionSankey component: - Wider nodes (290px) to fit all content - Dynamic height based on node count - Input values passed through for flow visualization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add balance history chart and improve block page layout - Add BalanceHistoryChart component using recharts library - Display balance over time on address detail page - Properly handle same-block transactions by sorting receives before sends - Move Coinbase badge after txid in block page for better alignment - Add recharts dependency to explorer package 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Optimize indexer batch updates and improve node startup reliability - Use temporary table for batch UPDATE of spent outputs, reducing ~2000 individual UPDATE statements per block to ~21 queries (bulk insert + single UPDATE with JOIN) - Move historical indexing from Indexer::new() to run() to avoid blocking RPC server startup - Add loop to catch up until fully synced before subscribing to block notifications, eliminating buffered notification concerns - Track last_indexed height properly during live sync including reorgs - Add title attribute for full txid on hover in TransactionSankey 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add P2PK address support and enhance dashboard with miner info - Add P2PK (Pay-to-Public-Key) script decoding in both RPC and indexer - Handle compressed (35 bytes) and uncompressed (67 bytes) P2PK scripts - Convert P2PK to equivalent P2PKH addresses using Hash160 - Enhance Dashboard with card-style Recent Blocks display - Show miner address (clickable link to address page) - Display block reward in BTC - Parallel block fetching for better performance - Relative time display (e.g., "5m ago") - Add subtle indexer sync status indicator - Show as stat card when indexing (progress percentage) - Small badge on Address page header when syncing - Remove unused imports and format helpers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Run indexer on dedicated thread to avoid blocking main runtime SQLite operations via sqlx use spawn_blocking internally, which can saturate the main tokio runtime's blocking thread pool during heavy indexing. This caused the indexer to fall behind block import. - Add `run_on_dedicated_thread()` method that spawns a new OS thread with its own single-threaded tokio runtime - Change `run()` from public async to private async - Update run.rs to use the new dedicated thread approach This isolates the indexer's I/O from the main async runtime, ensuring block import, networking, and RPC remain responsive while the indexer runs at full speed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Optimize spent output updates using direct PK lookups Replace temp table + correlated subquery approach with direct PK-indexed UPDATE statements for marking spent outputs. The correlated subqueries become slow as the outputs table grows, while direct PK lookups remain fast due to the (txid, vout) primary key index. Benchmark results (blocks per second): - 10K blocks: 5468 → 5725 (+5%) - 50K blocks: 2426 → 2966 (+22%) - 100K blocks: 1021 → 1375 (+35%) - 110K blocks: 813 → 1120 (+38%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Remove quotes from indexer progress log values Use Display formatting (%) instead of Debug formatting for percent and blocks_per_sec fields in tracing::info! calls. This produces cleaner log output without quotes around these values. Before: percent="45.6%" blocks_per_sec="1127" After: percent=45.6% blocks_per_sec=1127 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add tmp/ to gitignore 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Refactor: move imports to top and use to_core_arg() for network - Move std::collections::{HashMap, HashSet} import to top of file in db.rs - Replace network match blocks with network.to_core_arg() in both files - Move difficulty_from_bits to standalone function in blockchain.rs - Move SaturatedConversion import to top of file 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add TxoutType::script_type() and reuse solve() for script parsing - Add script_type() method to TxoutType returning Bitcoin Core compatible type names (pubkeyhash, scripthash, witness_v0_keyhash, etc.) - Re-export solve and TxoutType from subcoin-script crate root - Replace manual script type detection in rawtx.rs with solve().script_type() - Replace try_decode_p2pk() in blockchain.rs with solve() + TxoutType::PubKey - Remove ~45 lines of duplicated script parsing logic 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Remove unnecessary re-exports and fix clippy warnings - Remove pub re-exports of Address, AddressApiServer, and BitcoinNetwork from subcoin-rpc (unused by external consumers) - Add #[allow(clippy::type_complexity)] to functions with complex tuple types in subcoin-indexer 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ci: add disk space cleanup to clippy job The clippy job was failing due to disk space exhaustion when building librocksdb-sys. Add the same disk space cleanup step that test and build jobs already have. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ed8438e commit 9999efc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+8991
-447
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ jobs:
5656
- name: Install Protoc
5757
uses: arduino/setup-protoc@v3
5858

59+
# Buy some more disk space as we encountered the error `No space left on device`.
60+
- name: Free Disk Space (Ubuntu)
61+
uses: jlumbroso/free-disk-space@main
62+
with:
63+
android: true
64+
dotnet: true
65+
haskell: true
66+
large-packages: false
67+
docker-images: true
68+
swap-storage: false
69+
5970
- name: Run clippy
6071
run: |
6172
cargo clippy --locked --all-features --all-targets --manifest-path Cargo.toml -- -D warnings -A clippy::result_large_err

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Generated by Cargo
22
# will have compiled files and executables
33
debug/
4+
tmp/
45
target/
56

67
# These are backup files generated by rustfmt

Cargo.lock

Lines changed: 216 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ members = [
1919
"crates/subcoin-node",
2020
"crates/subcoin-primitives",
2121
"crates/subcoin-rpc",
22+
"crates/subcoin-rpc-bitcoind",
2223
"crates/subcoin-runtime",
2324
"crates/subcoin-runtime-primitives",
2425
"crates/subcoin-script",
@@ -62,10 +63,12 @@ log = { version = "0.4", default-features = false }
6263
num-bigint = "0.4.6"
6364
num-traits = "0.2.19"
6465
parking_lot = "0.12"
66+
rayon = "1.10"
6567
scale-info = { version = "2.11.6", default-features = false }
6668
serde = "1"
6769
serde_json = "1"
6870
sha2 = "0.9.9"
71+
sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio", "sqlite"] }
6972
tempfile = "3.10.1"
7073
thiserror = "1.0"
7174
tokio = "1.41.1"
@@ -138,6 +141,7 @@ subcoin-network = { path = "crates/subcoin-network" }
138141
subcoin-node = { path = "crates/subcoin-node" }
139142
subcoin-primitives = { path = "crates/subcoin-primitives" }
140143
subcoin-rpc = { path = "crates/subcoin-rpc" }
144+
subcoin-rpc-bitcoind = { path = "crates/subcoin-rpc-bitcoind" }
141145
subcoin-runtime = { path = "crates/subcoin-runtime" }
142146
subcoin-runtime-primitives = { path = "crates/subcoin-runtime-primitives", default-features = false }
143147
subcoin-script = { path = "crates/subcoin-script" }

0 commit comments

Comments
 (0)