Blockchain Data Indexing
Overview
The Blockchain Data Indexing module provides a structured and efficient mechanism for querying and caching blockchain data such as transaction histories, balances, blocks, and unspent outputs. Its primary purpose is to abstract away the complexity of interacting directly with blockchain nodes by leveraging an indexing service—Blockbook—to offer normalized and enriched blockchain data through a consistent API interface.
This module addresses the challenges of querying large blockchain datasets efficiently, ensuring quick access to transactional and balance information across multiple blockchain types. It supports both UTXO-based and account-based blockchains by providing a unified data representation and access patterns.
Core Concepts and Purpose
Indexing and Caching: Rather than querying blockchain nodes directly for every request, this module interacts with Blockbook, a blockchain indexer that maintains a cache and index of blockchain data optimized for queries.
Normalized Data Representation: It provides "normalized" transaction and address data, ensuring consistent structure across different blockchains, simplifying client integration.
Comprehensive Data Access: Supports a wide range of queries including transaction details, address balances and histories, block data, unspent outputs, and balance history over time.
API Abstraction Layer: The module acts as an API client and controller layer that exposes REST endpoints to fetch indexed blockchain data, abstracting network-specific details from the consumer.
Error Handling and Resilience: Implements retry logic and error wrapping to handle network or server errors gracefully.
Real-time Event Integration: Works alongside WebSocket clients to enable live subscription to blockchain events (covered in WebSocket Event Subscription topic).
Key Functionalities and Workflows
1. REST API Client and Controller
The module exposes a set of RESTful API endpoints to fetch blockchain data. It relies on an HTTP client (`axios`) configured with retry mechanisms and timeout policies to communicate with the Blockbook indexer backend.
For example, the `Blockbook` class (in `blockbook/src/controller.ts`) provides methods such as:
getTransaction(txid: string): Retrieves normalized transaction details by transaction ID.getAddress(address: string, page?, pageSize?, from?, to?, details?, contract?): Fetches balance and transaction history for an address with pagination and detail level options.getXpub(xpub: string, page?, pageSize?, from?, to?, details?, tokens?): Returns balances and transactions for extended public keys (xpubs), supporting BIP44/49/84 derivations for UTXO coins.getUtxo(account: string, confirmed?): Lists unspent transaction outputs for a Bitcoin-type address or xpub.getBlock(block: string, page?): Retrieves block data by height or hash, including transactions.balanceHistory(account: string, from?, to?, fiatcurrency?, groupBy?): Provides historical balance data grouped by time intervals.
Each method wraps HTTP requests to the Blockbook backend, returning typed results or throwing wrapped errors (`ApiError`) on failure.
2. Data Normalization and Type Definitions
The module defines extensive TypeScript interfaces (in `blockbook/src/models.ts`) representing the blockchain data structures such as transactions (`Tx`), addresses (`Address`), blocks (`Block`), tokens, and unspent outputs (`Utxo`). These interfaces ensure consistent data models across different blockchain implementations, facilitating uniform API consumption.
For example, the `Tx` interface covers common transaction fields and coin-specific extensions like `ethereumSpecific`, allowing clients to handle details relevant to each blockchain.
3. Error Handling and Retries
The HTTP client is enhanced with `axios-retry` to automatically retry failed requests based on network errors or HTTP status codes indicative of transient failures. Timeouts are managed to prevent hanging requests. This design ensures higher reliability when querying the indexing service.
4. API Endpoint Annotations and Documentation
The code is decorated with metadata using [tsoa](/projects/291/68848) decorators (`@Route`, `@Get`, `@Example`) to define API routes, HTTP methods, and example responses. This supports automated OpenAPI documentation generation, promoting clarity and ease of integration.
5. Interaction with Other System Components
Blockbook Indexer Backend: The module acts as a client to the Blockbook service, which indexes blockchain data and maintains caches. It sends HTTP requests to Blockbook's REST API endpoints to retrieve data.
WebSocket Event Subscription Module: While this module handles on-demand queries, real-time updates to blockchain data are handled by the WebSocket Event Subscription submodule that listens to live blockchain events and pushes updates to clients.
Unified API Layer: The indexing module is part of the unified API layer, where it provides foundational blockchain data to upper-level API controllers that may combine multiple data sources and apply additional business logic.
Logging and Observability: Uses a scoped logger (
@shapeshiftoss/logger) to trace API call durations and errors, facilitating monitoring and troubleshooting.
Design Patterns and Approaches
API Client Wrapper Pattern: The
Blockbookclass wraps HTTP calls into typed methods, encapsulating the interaction details with the indexing backend.Decorator-Based Routing: Uses decorators for clean, declarative API endpoint definitions enhancing maintainability.
Retry and Resilience Pattern: Integrates retry policies at the HTTP client layer to handle intermittent failures transparently.
Typed Data Models: Strong typing via interfaces ensures that data contracts are explicit and errors can be caught early.
Separation of Concerns: Clear division between data models, client communication, and API routing/controller logic.
Code Example Snippets
Blockbook REST API Method Example
@Get('tx/{txid}')
async getTransaction(@Path() txid: string): Promise<Tx> {
const { data } = await this.instance.get<Tx>(`api/v2/tx/${txid}`)
return data
}
This method queries the Blockbook API for a transaction by ID and returns normalized transaction data.
Address Query with Pagination and Detail Levels
@Get('address/{address}')
async getAddress(
@Path() address: string,
@Query() page?: number,
@Query() pageSize?: number,
@Query() details?: 'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txslight' | 'txs',
): Promise<Address> {
const { data } = await this.instance.get<Address>(`api/v2/address/${address}`, {
params: { page, pageSize, details }
})
return data
}
Supports fetching address data with selectable detail levels to optimize performance and data volume.
Visualization: Flowchart of Blockchain Data Indexing Query Workflow
flowchart TD
Client[Client Request] -->|REST API Call| API_Module[Blockchain Data Indexing Module]
API_Module -->|HTTP Request| Blockbook[Blockbook Indexer Backend]
Blockbook -->|Indexed Blockchain Data| API_Module
API_Module -->|Normalized Response| Client
API_Module -->|Logs & Metrics| Logger[Logging & Monitoring]
The client issues a request which the indexing module translates into an HTTP request to the Blockbook indexer.
Blockbook returns indexed and cached blockchain data.
The module normalizes and returns data to the client while logging request metrics.
This detailed documentation covers the Blockchain Data Indexing module’s purpose, design, and operation within the system, illustrating how it provides efficient, normalized access to blockchain data via the Blockbook indexer.