Crate brk_server

Crate brk_server 

Source
Expand description

§brk_server

HTTP server providing REST API access to Bitcoin analytics data

Crates.io Documentation

§Overview

This crate provides a high-performance HTTP server built on axum that exposes Bitcoin blockchain analytics data through a comprehensive REST API. It integrates with the entire BRK ecosystem, serving data from indexers, computers, and parsers with intelligent caching, compression, and multiple output formats.

Key Features:

  • RESTful API for blockchain data queries with flexible filtering
  • Multiple output formats: JSON, CSV
  • Intelligent caching system with ETags and conditional requests
  • HTTP compression (Gzip, Brotli, Deflate, Zstd) for bandwidth efficiency
  • Static file serving for web interfaces and documentation
  • Bitcoin address and transaction lookup endpoints
  • Vector database query interface with pagination
  • Health monitoring and status endpoints

Target Use Cases:

  • Bitcoin data APIs for applications and research
  • Web-based blockchain explorers and analytics dashboards
  • Research data export and analysis tools
  • Integration with external systems requiring Bitcoin data

§Installation

cargo add brk_server

§Quick Start

use brk_server::Server;
use brk_interface::Interface;
use std::path::PathBuf;

// Initialize interface with your data sources
let interface = Interface::new(/* your config */);

// Optional static file serving directory
let files_path = Some(PathBuf::from("./web"));

// Create and start server
let server = Server::new(interface, files_path);

// Start server with optional MCP (Model Context Protocol) support
server.serve(true).await?;

§API Overview

§Core Endpoints

Blockchain Queries:

  • GET /api/address/{address} - Address information, balance, transaction counts
  • GET /api/tx/{txid} - Transaction details including version, locktime
  • GET /api/vecs/{variant} - Vector database queries with filtering

System Information:

  • GET /api/vecs/index-count - Total number of indexes available
  • GET /api/vecs/id-count - Vector ID statistics
  • GET /api/vecs/indexes - List of available data indexes
  • GET /health - Service health status
  • GET /version - Server version information

§Vector Database API

Query Interface:

  • GET /api/vecs/query - Generic vector query with parameters
  • GET /api/vecs/{variant}?from={start}&to={end}&format={format} - Range queries

Supported Parameters:

  • from / to: Range filtering (height, timestamp, date-based)
  • format: Output format (json, csv)
  • Pagination parameters for large datasets

§Address API Response Format

{
  "address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2",
  "type": "p2pkh",
  "index": 12345,
  "chain_stats": {
    "funded_txo_sum": 500000000,
    "spent_txo_sum": 400000000,
    "utxo_count": 5,
    "balance": 100000000,
    "balance_usd": 4200.5,
    "realized_value": 450000000,
    "avg_cost_basis": 45000.0
  }
}

§Examples

§Basic Server Setup

use brk_server::Server;
use brk_interface::Interface;

// Initialize with BRK interface
let interface = Interface::builder()
    .with_indexer_path("./data/indexer")
    .with_computer_path("./data/computer")
    .build()?;

let server = Server::new(interface, None);

// Server automatically finds available port starting from 3110
server.serve(false).await?;

§Address Balance Lookup

# Get address information
curl http://localhost:3110/api/address/1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2

# Response includes balance, transaction counts, USD value
{
  "address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2",
  "type": "p2pkh",
  "chain_stats": {
    "balance": 100000000,
    "balance_usd": 4200.50,
    "utxo_count": 5
  }
}

§Data Export Queries

# Export height-to-price data as CSV
curl "http://localhost:3110/api/vecs/height-to-price?from=800000&to=800100&format=csv" \
  -H "Accept-Encoding: gzip"

# Query with caching - subsequent requests return 304 Not Modified
curl "http://localhost:3110/api/vecs/dateindex-to-price-ohlc?from=0&to=1000" \
  -H "If-None-Match: \"etag-hash\""

§Transaction Details

# Get transaction information
curl http://localhost:3110/api/tx/abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890

# Response includes version, locktime, and internal indexing
{
  "txid": "abcdef...",
  "index": 98765,
  "version": 2,
  "locktime": 0
}

§Architecture

§Server Stack

  • HTTP Framework: axum with async/await for high concurrency
  • Compression: Multi-algorithm support (Gzip, Brotli, Deflate, Zstd)
  • Caching: quick_cache with LRU eviction and ETag validation
  • Tracing: Request/response logging with latency tracking
  • Static Files: Optional web interface serving

§Caching Strategy

The server implements intelligent caching:

  • ETags: Generated from data version and query parameters
  • Conditional Requests: 304 Not Modified responses for unchanged data
  • Memory Cache: LRU cache with configurable capacity (5,000 entries)
  • Cache Control: must-revalidate headers for data consistency

§Request Processing

  1. Route Matching: Path-based routing to appropriate handlers
  2. Parameter Validation: Query parameter parsing and validation
  3. Data Retrieval: Interface calls to indexer/computer components
  4. Caching Logic: ETag generation and cache lookup
  5. Format Conversion: JSON/CSV output formatting
  6. Compression: Response compression based on Accept-Encoding
  7. Response: HTTP response with appropriate headers

§Static File Serving

Optional static file serving supports:

  • Web interface hosting for blockchain explorers
  • Documentation and API reference serving
  • Asset serving (CSS, JS, images) with proper MIME types
  • Directory browsing with index.html fallback

§Configuration

§Environment Variables

The server automatically configures itself but respects:

  • Port selection: Starts at 3110, increments if unavailable
  • Compression: Enabled by default for all supported algorithms
  • CORS: Permissive headers for cross-origin requests

§Memory Management

  • Cache size: 5,000 entries by default
  • Request weight limits: 65MB maximum per query
  • Timeout handling: 50ms cache guard timeout
  • Compression: Adaptive based on content type and size

§Code Analysis Summary

Main Components: Server struct with AppState containing interface, cache, and file paths
HTTP Framework: Built on axum with middleware for compression, tracing, and CORS
API Routes: Address lookup, transaction details, vector queries, and system information
Caching Layer: quick_cache integration with ETag-based conditional requests
Data Integration: Direct interface to BRK indexer, computer, parser, and fetcher components
Static Serving: Optional file serving for web interfaces and documentation
Architecture: Async HTTP server with intelligent caching and multi-format data export capabilities


This README was generated by Claude Code

§Example

use std::{path::Path, thread::sleep, time::Duration};

use bitcoincore_rpc::RpcApi;
use brk_computer::Computer;

use brk_error::Result;
use brk_fetcher::Fetcher;
use brk_indexer::Indexer;
use brk_interface::Interface;
use brk_parser::Parser;
use brk_server::Server;
use vecdb::Exit;

pub fn main() -> Result<()> {
    brk_logger::init(Some(Path::new(".log")))?;

    let process = true;

    let bitcoin_dir = Path::new("");

    let rpc = Box::leak(Box::new(bitcoincore_rpc::Client::new(
        "http://localhost:8332",
        bitcoincore_rpc::Auth::CookieFile(bitcoin_dir.join(".cookie")),
    )?));
    let exit = Exit::new();
    exit.set_ctrlc_handler();

    let parser = Parser::new(bitcoin_dir.join("blocks"), rpc);

    let outputs_dir = Path::new("../../_outputs");

    let mut indexer = Indexer::forced_import(outputs_dir)?;

    let fetcher = Some(Fetcher::import(true, None)?);

    let mut computer = Computer::forced_import(outputs_dir, &indexer, fetcher)?;

    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()?
        .block_on(async {
            let interface = Interface::build(&parser, &indexer, &computer);

            let server = Server::new(interface, None);

            let server = tokio::spawn(async move {
                server.serve(true).await.unwrap();
            });

            if process {
                loop {
                    let block_count = rpc.get_block_count()?;

                    let starting_indexes = indexer.index(&parser, rpc, &exit, true)?;

                    computer.compute(&indexer, starting_indexes, &parser, &exit)?;

                    while block_count == rpc.get_block_count()? {
                        sleep(Duration::from_secs(1))
                    }
                }
            }

            #[allow(unreachable_code)]
            server.await.unwrap();

            Ok(())
        })
}

Structs§

AppState
Server

Constants§

VERSION