mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-25 13:44:30 -07:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38b8a08297 | |||
| c9ffd3ad99 | |||
| 61f960de28 | |||
| da1ff2cacc | |||
| 05036c682f | |||
| 7d47bc8042 | |||
| 98cfd160ef | |||
| b5e3262b67 | |||
| 009fb35c4c | |||
| 8648d3131a | |||
| 00c316c35d | |||
| 5f8de8e756 | |||
| ee5dc8fc41 | |||
| a61926988a | |||
| bd8c4dfb6b | |||
| ce9b4bc4dd | |||
| 8b12b00114 | |||
| 1775cc1d54 | |||
| e4bd09df24 | |||
| 5e8c7da4df | |||
| c85592eefe | |||
| 05861c9113 |
+3
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
# Builds
|
||||
target
|
||||
dist
|
||||
websites/dist
|
||||
vecid-to-indexes.js
|
||||
|
||||
# Copies
|
||||
@@ -28,4 +28,6 @@ flamegraph.svg
|
||||
*.trace
|
||||
|
||||
# AI
|
||||
.claude
|
||||
CLAUDE.md
|
||||
CLAUDE*.md
|
||||
|
||||
+903
@@ -1,3 +1,906 @@
|
||||
# Changelog
|
||||
|
||||
*This changelog was generated by Claude Code.*
|
||||
|
||||
All notable changes to the Bitcoin Research Kit (BRK) project will be documented in this file.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Documentation
|
||||
- **Enhanced**: Comprehensive rewrite of all crate README files for improved clarity and developer experience
|
||||
- **Updated**: Main project README with better structure and documentation
|
||||
- **Standardized**: README format across all workspace crates with consistent styling
|
||||
- **Improved**: Documentation structure and organization across the entire project
|
||||
- **Added**: More detailed descriptions for each crate's purpose and functionality
|
||||
|
||||
### Computer Module
|
||||
- **Refactored**: Converted ComputedFrom pattern to LazyFrom pattern for improved performance
|
||||
- **Added**: New LazyVecBuilder implementation for on-demand computation
|
||||
- **Restructured**: Computer module stateful operations with improved organization
|
||||
- **Cleaned**: Removed hardcoded format specifications, now using defaults
|
||||
- **Optimized**: Vector storage operations with lazy computation patterns
|
||||
- **Refactored**: Address type handling in stateful computations
|
||||
- **Reorganized**: Stateful module structure for better maintainability
|
||||
- **Enhanced**: Builder patterns for grouped vector operations
|
||||
- **Improved**: Memory efficiency through lazy computation strategies
|
||||
|
||||
### Vector Storage
|
||||
- **Enhanced**: Vector storage engine development workflow
|
||||
- **Optimized**: Lazy vector computation patterns for better performance
|
||||
- **Improved**: Vector builder architecture with lazy evaluation
|
||||
|
||||
### Dependencies
|
||||
- **Updated**: Rayon from 1.10.0 to 1.11.0 for improved parallel processing
|
||||
- **Migrated**: vecdb from external version 0.1.0 to local development path for better development workflow
|
||||
- **Reorganized**: Workspace dependency structure for consistency
|
||||
|
||||
### Build System
|
||||
- **Added**: Rust toolchain version specification (1.89) to workspace configuration
|
||||
- **Enhanced**: Development environment consistency with toolchain pinning
|
||||
|
||||
### Server Module
|
||||
- **Improved**: API interface implementations
|
||||
- **Enhanced**: File serving functionality
|
||||
|
||||
### Indexer Module
|
||||
- **Optimized**: Integration with updated computer module patterns
|
||||
|
||||
## [v0.0.88](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.88) - 2025-08-10
|
||||
|
||||
### Website
|
||||
- **Fixed**: Conditional rendering of adjusted SOPR (Adjusted Spent Output Profit Ratio) charts in website options
|
||||
- **Improved**: SOPR chart options now only display adjusted SOPR when the data is available in the vector indexes
|
||||
- **Enhanced**: Better error handling for missing adjusted SOPR datasets
|
||||
|
||||
### Build System
|
||||
- **Updated**: Added `rust-version.workspace = true` to all crate Cargo.toml files
|
||||
- **Updated**: Bumped all crate versions from 0.0.87 to 0.0.88
|
||||
- **Added**: Minimum supported Rust version constraint (1.89) to workspace configuration
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.87...v0.0.88)
|
||||
|
||||
## [v0.0.87](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.87) - 2025-08-10
|
||||
|
||||
### Major Architecture Change
|
||||
- **BREAKING**: Extracted `brk_vecs` into separate external crate `vecdb`
|
||||
- **Moved**: Vector storage engine to separate [seqdb](https://github.com/seqdb/seqdb) repository
|
||||
- **Updated**: All crates now use `vecdb` instead of internal `brk_vecs`
|
||||
- **Improved**: Better separation of concerns with vector storage as independent library
|
||||
|
||||
### Storage Engine
|
||||
- **Renamed**: `brk_vecs` → `vecdb`
|
||||
- **Renamed**: `brk_vecs_macros` → `vecdb_derive`
|
||||
- **Updated**: All vector database operations now use `vecdb::Database` instead of `brk_vecs::File`
|
||||
- **Enhanced**: Vector storage is now a standalone crate with its own versioning
|
||||
|
||||
### Dependencies
|
||||
- **Updated**: `brk_rmcp` from 0.4.1 → 0.5.0
|
||||
- **Updated**: `cc` from 1.2.31 → 1.2.32
|
||||
- **Updated**: `hashbrown` from 0.15.4 → 0.15.5
|
||||
- **Updated**: `rapidhash` from 3.0.0 → 3.1.0
|
||||
- **Updated**: `rustversion` from 1.0.21 → 1.0.22
|
||||
- **Updated**: `slab` from 0.4.10 → 0.4.11
|
||||
|
||||
### Configuration
|
||||
- **Added**: `--exchanges` flag to control whether to fetch prices from exchange APIs
|
||||
- **Enhanced**: Price fetching now has two levels - BRK API and exchanges
|
||||
- **Improved**: Better RPC authentication error messages with troubleshooting guidance
|
||||
|
||||
### Build System
|
||||
- **Updated**: All crate versions from 0.0.85 to 0.0.87
|
||||
- **Added**: New dependency on external `vecdb` crate with derive features
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.85...v0.0.87)
|
||||
|
||||
## [v0.0.85](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.85) - 2025-08-07
|
||||
|
||||
### MCP (Model Context Protocol)
|
||||
- **Updated**: Upgraded MCP implementation
|
||||
- **Changed**: Made MCP stateless mode the default (was previously stateful to avoid breaking Claude)
|
||||
- **Improved**: Better MCP server configuration with stateless operations
|
||||
|
||||
### Logging
|
||||
- **Enhanced**: Updated logging utilities in `brk_logger`
|
||||
|
||||
### Build System
|
||||
- **Updated**: Bumped version from 0.0.84 to 0.0.85
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.84...v0.0.85)
|
||||
|
||||
## [v0.0.84](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.84) - 2025-08-07
|
||||
|
||||
### Build System
|
||||
- **Added**: Per-crate `build.rs` files for better build configuration
|
||||
- **Improved**: Cargo.lock file management and error handling
|
||||
- **Enhanced**: Build process optimization across all crates
|
||||
|
||||
### Vector Storage
|
||||
- **Fixed**: Compressed vector functionality restored
|
||||
- **Fixed**: Race condition in vector operations
|
||||
- **Improved**: Vector storage stability and performance
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.83...v0.0.84)
|
||||
|
||||
## [v0.0.83](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.83) - 2025-07-26
|
||||
|
||||
### Vector Storage
|
||||
- **Added**: Linux punch hole implementation for better disk space management
|
||||
- **Enhanced**: Memory-mapped file handling improvements
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.82...v0.0.83)
|
||||
|
||||
## [v0.0.82](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.82) - 2025-07-26
|
||||
|
||||
### Dependencies
|
||||
- **Updated**: Cleaned up Cargo dependencies
|
||||
- **Removed**: Temporarily removed rayon parallelization from computer module
|
||||
- **Enhanced**: Dependency management and build optimization
|
||||
|
||||
### Computer Module
|
||||
- **Fixed**: File initialization with minimum length and regions
|
||||
- **Improved**: Memory management and performance optimizations
|
||||
- **Added**: Flush and punch operations for better disk usage
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.81...v0.0.82)
|
||||
|
||||
## [v0.0.81](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.81) - 2025-07-17
|
||||
|
||||
### Vector Storage
|
||||
- **Fixed**: Holes export functionality in vector storage
|
||||
- **Enhanced**: Vector storage reliability
|
||||
|
||||
### MCP Integration
|
||||
- **Updated**: Now uses `rust-rmcp` instead of `brk-rmcp` for better compatibility
|
||||
|
||||
### Computer Module
|
||||
- **Improved**: Performance optimizations
|
||||
- **Fixed**: Stateful operations
|
||||
- **Enhanced**: Vector-to-store conversion process (parts 1 & 2)
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.80...v0.0.81)
|
||||
|
||||
## [v0.0.80](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.80) - 2025-07-13
|
||||
|
||||
### MCP (Model Context Protocol)
|
||||
- **Removed**: Claude results examples due to dead links
|
||||
- **Cleaned**: MCP example documentation
|
||||
|
||||
### Vector Storage
|
||||
- **Added**: Local and shared `stored_len` tracking to raw vector variant
|
||||
- **Enhanced**: Vector storage metadata management
|
||||
|
||||
### Dependencies
|
||||
- **Updated**: Multiple crate upgrades for better compatibility
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.79...v0.0.80)
|
||||
|
||||
## [v0.0.79](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.79) - 2025-07-13
|
||||
|
||||
### Dependencies
|
||||
- **Updated**: Upgraded multiple crates for better compatibility and performance
|
||||
|
||||
### Website
|
||||
- **Applied**: Dataset changes to chart configurations
|
||||
- **Enhanced**: Chart display with updated dataset handling
|
||||
|
||||
### Vector Storage
|
||||
- **Fixed**: Header reading functionality for existing vector files
|
||||
- **Improved**: Memory management by removing mmap storage from struct
|
||||
- **Enhanced**: Vector file handling reliability
|
||||
|
||||
### Store Operations
|
||||
- **Reverted**: Bloom filters back to default settings due to slow read performance
|
||||
- **Optimized**: Memory usage for future v3 improvements
|
||||
- **Fixed**: Missing store-related files
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.78...v0.0.79)
|
||||
|
||||
## [v0.0.78](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.78) - 2025-07-13
|
||||
|
||||
### Build System
|
||||
- **Updated**: All crate versions bumped for version consistency across workspace
|
||||
- **Enhanced**: Version consistency across workspace
|
||||
|
||||
### Computer Module
|
||||
- **Optimized**: Reduced number of ratio datasets for some cohorts to improve performance
|
||||
- **Enhanced**: Performance optimizations across computation modules
|
||||
- **Improved**: Resource utilization and memory management
|
||||
- **Fixed**: Coarse lazy indexes functionality
|
||||
- **Removed**: Debug statements for cleaner production builds
|
||||
|
||||
### Global Improvements
|
||||
- **Added**: Semester-based time intervals
|
||||
- **Changed**: Coarser intervals now computed instead of eager evaluation
|
||||
- **Fixed**: Various global module issues
|
||||
- **Enhanced**: Performance and resource improvements
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.76...v0.0.78)
|
||||
|
||||
## [v0.0.76](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.76) - 2025-07-09
|
||||
|
||||
### Bundler
|
||||
- **Updated**: Upgraded Rolldown dependency for better bundling performance
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.75...v0.0.76)
|
||||
|
||||
## [v0.0.75](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.75) - 2025-07-09
|
||||
|
||||
### Computer Module
|
||||
- **Fixed**: Reduced parallel thread count for `compute_rest_part2` to prevent external drive crashes
|
||||
- **Improved**: Better handling of external storage devices during computation
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.74...v0.0.75)
|
||||
|
||||
## [v0.0.74](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.74) - 2025-07-09
|
||||
|
||||
### Computer Module
|
||||
- **Fixed**: Parallel computation crashes on external drives
|
||||
- **Enhanced**: Storage device compatibility improvements
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.73...v0.0.74)
|
||||
|
||||
## [v0.0.73](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.73) - 2025-07-09
|
||||
|
||||
### Website
|
||||
- **Fixed**: Chart panes now maintain equal sizes
|
||||
- **Removed**: Automatic saving of pane heights preference
|
||||
- **Updated**: Lightweight Charts to v5.0.8
|
||||
- **Added**: TODO functionality initialization
|
||||
- **Enhanced**: Website development snapshots
|
||||
|
||||
### Dependencies
|
||||
- **Updated**: Global dependency upgrades across all crates
|
||||
- **Enhanced**: System-wide dependency management
|
||||
|
||||
### Computer Module
|
||||
- **Added**: More up-to and from datasets for enhanced analysis
|
||||
- **Implemented**: Store functionality (parts 2-10)
|
||||
- **Enhanced**: Data storage architecture with store-based approach
|
||||
- **Replaced**: Value enum with Cow for better memory efficiency
|
||||
|
||||
### Global System
|
||||
- **Moved**: `addressindex_to_outputindex` stores from computer to indexer module
|
||||
- **Renamed**: Various stores for better organization
|
||||
- **Enhanced**: Global module fixes and improvements
|
||||
|
||||
### Indexer
|
||||
- **Renamed**: Store organization for better clarity
|
||||
- **Enhanced**: Store management improvements
|
||||
|
||||
### Documentation
|
||||
- **Added**: MCP (Model Context Protocol) README documentation
|
||||
- **Enhanced**: Developer documentation
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.71...v0.0.73)
|
||||
|
||||
## [v0.0.71](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.71) - 2025-06-25
|
||||
|
||||
### MCP (Model Context Protocol)
|
||||
- **Enhanced**: Small improvements to MCP functionality
|
||||
- **Updated**: MCP integration refinements
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.70...v0.0.71)
|
||||
|
||||
## [v0.0.70](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.70) - 2025-06-25
|
||||
|
||||
### Fetcher Module
|
||||
- **Fixed**: BRK API URL endpoints
|
||||
- **Enhanced**: Price fetching reliability
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.69...v0.0.70)
|
||||
|
||||
## [v0.0.69](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.69) - 2025-06-24
|
||||
|
||||
### Vector Storage
|
||||
- **Reverted**: Storing file in struct (was overwhelming system resources)
|
||||
- **Improved**: Memory management for vector operations
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.68...v0.0.69)
|
||||
|
||||
## [v0.0.68](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.68) - 2025-06-24
|
||||
|
||||
### Core System
|
||||
- **Increased**: Maximum open files limit for better file handling
|
||||
- **Enhanced**: System resource management
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.67...v0.0.68)
|
||||
|
||||
## [v0.0.67](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.67) - 2025-06-24
|
||||
|
||||
### Server Module
|
||||
- **Changed**: Now uses ETag for vectors instead of date modified for better caching
|
||||
- **Enhanced**: HTTP caching strategy improvements
|
||||
|
||||
### Vector Storage
|
||||
- **Unified**: Single file with header approach for vector storage
|
||||
- **Improved**: Vector file organization and management
|
||||
|
||||
### MCP Integration
|
||||
- **Added**: MCP part 2 implementation
|
||||
- **Refactored**: Server MCP integration
|
||||
- **Enhanced**: MCP functionality expansion
|
||||
|
||||
### Global Improvements
|
||||
- **Renamed**: Various indexes for better clarity
|
||||
- **Updated**: Index naming conventions
|
||||
- **Fixed**: Web index type imports
|
||||
|
||||
### CLI
|
||||
- **Removed**: Duplicate 'h' short index in query functionality
|
||||
|
||||
### Documentation
|
||||
- **Updated**: README improvements
|
||||
- **Updated**: MCP documentation
|
||||
- **Updated**: Changelog titles
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.66...v0.0.67)
|
||||
|
||||
## [v0.0.66](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.66) - 2025-06-19
|
||||
|
||||
### Cointime Implementation
|
||||
- **Added**: Cointime functionality part 1
|
||||
- **Implemented**: Cumulative destroyed coinblocks and cointime calculations
|
||||
- **Enhanced**: Bitcoin time-based analytics
|
||||
|
||||
### Website
|
||||
- **Fixed**: Options cumulative possible vecids type issue
|
||||
- **Enhanced**: Chart options handling
|
||||
|
||||
### Documentation
|
||||
- **Merged**: Pull request #18 with typo fixes from StevenBlack
|
||||
- **Improved**: Code quality and documentation accuracy
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.65...v0.0.66)
|
||||
|
||||
## [v0.0.65](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.65) - 2025-06-17
|
||||
|
||||
### Website
|
||||
- **Fixed**: Hang issue when candles fetched are slightly different than before
|
||||
- **Enhanced**: Chart data fetching reliability
|
||||
|
||||
### Documentation
|
||||
- **Improved**: Minor refinements to brk_cli README
|
||||
- **Enhanced**: CLI documentation clarity
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.64...v0.0.65)
|
||||
|
||||
## [v0.0.64](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.64) - 2025-06-17
|
||||
|
||||
### Datasets
|
||||
- **Added**: Multiple new realized datasets and charts
|
||||
- **Added**: 4-year Z-score calculations
|
||||
- **Added**: 200-day Simple Moving Average (SMA)
|
||||
- **Added**: Mayer's Multiple indicator
|
||||
- **Enhanced**: Financial analysis capabilities
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.63...v0.0.64)
|
||||
|
||||
## [v0.0.63](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.63) - 2025-06-16
|
||||
|
||||
### Website
|
||||
- **Fixed**: Web interface stutter issue (follow-up fix)
|
||||
- **Added**: PWA (Progressive Web App) assets
|
||||
- **Enhanced**: User experience improvements
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.62...v0.0.63)
|
||||
|
||||
## [v0.0.62](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.62) - 2025-06-16
|
||||
|
||||
### Website
|
||||
- **Fixed**: Stutter on update and save default chart settings to URL parameters
|
||||
- **Enhanced**: Chart settings persistence
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.61...v0.0.62)
|
||||
|
||||
## [v0.0.61](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.61) - 2025-06-15
|
||||
|
||||
### Website
|
||||
- **Fixed**: Error in Safari lockdown mode
|
||||
- **Improved**: Charts now update instead of setData when possible for better performance
|
||||
|
||||
### Computer Module
|
||||
- **Fixed**: OHLC data opening when fetched from different API than previous OHLC (preparation fix)
|
||||
- **Enhanced**: API data source consistency
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.59...v0.0.61)
|
||||
|
||||
## [v0.0.59](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.59) - 2025-06-15
|
||||
|
||||
### Computer Module
|
||||
- **Fixed**: Open of OHLC data when fetched from different API than previous OHLC
|
||||
- **Enhanced**: API data consistency handling
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.58...v0.0.59)
|
||||
|
||||
## [v0.0.58](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.58) - 2025-06-15
|
||||
|
||||
### Bundler
|
||||
- **Fixed**: BRK bundler usage
|
||||
- **Removed**: HTML minify crate for improved bundling
|
||||
- **Deployed**: brk_rolldown bundler with edge case fixes
|
||||
- **Initialized**: Working bundler version
|
||||
- **Added**: Basic bundling functionality
|
||||
|
||||
### Server
|
||||
- **Updated**: Cache control for bundled websites (continued improvements)
|
||||
- **Enhanced**: Website serving performance
|
||||
- **Enhanced**: Static file serving performance
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.56...v0.0.58)
|
||||
|
||||
## [v0.0.56](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.56) - 2025-06-13
|
||||
|
||||
### Computer Module
|
||||
- **Enhanced**: Stateful operations now reset when blockchain reorganization detected
|
||||
- **Improved**: Blockchain state management reliability
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.55...v0.0.56)
|
||||
|
||||
## [v0.0.55](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.55) - 2025-06-13
|
||||
|
||||
### Global
|
||||
- **Fixed**: Multiple system-wide fixes and improvements
|
||||
- **Enhanced**: Overall system stability
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.54...v0.0.55)
|
||||
|
||||
## [v0.0.54](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.54) - 2025-06-12
|
||||
|
||||
### Website
|
||||
- **Enhanced**: Filter possible index choices in charts (initial implementation)
|
||||
- **Enhanced**: Filter possible index choices in charts
|
||||
- **Fixed**: CSS styling issues
|
||||
- **Added**: Auto price series type for default website
|
||||
- **Added**: Live price functionality
|
||||
- **Removed**: ScrollToSelected functionality
|
||||
|
||||
### Vector Storage
|
||||
- **Fixed**: Compressed vector functionality (ongoing improvements)
|
||||
- **Fixed**: Compressed vector functionality (still has slow parallel read performance)
|
||||
- **Changed**: CLI defaults to raw format for performance
|
||||
- **Changed**: CLI now defaults to raw format for better performance
|
||||
|
||||
### Indexer
|
||||
- **Enhanced**: Raw format only support
|
||||
- **Limited**: Now only supports raw format
|
||||
- **Fixed**: Global system fixes
|
||||
- **Fixed**: Various global fixes
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.52...v0.0.54)
|
||||
|
||||
## [v0.0.52](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.52) - 2025-06-11
|
||||
|
||||
### Website
|
||||
- **Fixed**: Service worker and related components
|
||||
- **Enhanced**: Progressive Web App functionality
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.51...v0.0.52)
|
||||
|
||||
## [v0.0.51](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.51) - 2025-06-11
|
||||
|
||||
### Website
|
||||
- **Enhanced**: Default website improvements and fixes
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.50...v0.0.51)
|
||||
|
||||
## [v0.0.50](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.50) - 2025-06-11
|
||||
|
||||
### Website
|
||||
- **Fixed**: Minimum bar spacing for charts
|
||||
- **Enhanced**: Chart display improvements
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.49...v0.0.50)
|
||||
|
||||
## [v0.0.49](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.49) - 2025-06-11
|
||||
|
||||
### Build System
|
||||
- **Updated**: Set full version of all crates for better versioning consistency
|
||||
- **Enhanced**: Cargo workspace version management
|
||||
|
||||
### Website
|
||||
- **Updated**: Multiple default website snapshots and improvements
|
||||
|
||||
### Documentation
|
||||
- **Updated**: README improvements
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.48...v0.0.49)
|
||||
|
||||
## [v0.0.48](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.48) - 2025-06-09
|
||||
|
||||
### Website
|
||||
- **Enhanced**: Default website snapshots and continued development
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.47...v0.0.48)
|
||||
|
||||
## [v0.0.47](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.47) - 2025-06-08
|
||||
|
||||
### Server
|
||||
- **Added**: Documentation for index-t-value functionality
|
||||
- **Added**: Support for `/api/X-to-Y` endpoints
|
||||
- **Fixed**: Query CLI functionality
|
||||
- **Added**: Meta API endpoints for better API discoverability
|
||||
|
||||
### CLI
|
||||
- **Added**: Count parameter to query functionality
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.46...v0.0.47)
|
||||
|
||||
## [v0.0.46](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.46) - 2025-06-08
|
||||
|
||||
### Server
|
||||
- **Added**: DDoS protection for better server security
|
||||
- **Enhanced**: Request rate limiting and protection
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.45...v0.0.46)
|
||||
|
||||
## [v0.0.45](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.45) - 2025-06-08
|
||||
|
||||
### Website
|
||||
- **Moved**: Service worker to root directory
|
||||
- **Updated**: Service worker functionality
|
||||
- **Enhanced**: PWA capabilities
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.44...v0.0.45)
|
||||
|
||||
## [v0.0.44](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.44) - 2025-06-07
|
||||
|
||||
### Server
|
||||
- **Fixed**: Existing folder endpoints functionality
|
||||
- **Enhanced**: File serving reliability
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.43...v0.0.44)
|
||||
|
||||
## [v0.0.43](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.43) - 2025-06-07
|
||||
|
||||
### Website
|
||||
- **Updated**: Dependencies and fixed CSS issues
|
||||
- **Enhanced**: Styling and layout improvements
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.42...v0.0.43)
|
||||
|
||||
## [v0.0.42](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.42) - 2025-06-07
|
||||
|
||||
### Global System
|
||||
- **Fixed**: Multiple system fixes and improvements
|
||||
- **Enhanced**: System-wide stability improvements
|
||||
- **Resolved**: Data accuracy issues that were causing incorrect results
|
||||
|
||||
### Fetcher
|
||||
- **Added**: Support for new API endpoints
|
||||
- **Enhanced**: Data fetching capabilities
|
||||
|
||||
### Computer Module
|
||||
- **Fixed**: Coinblocks overflow issue
|
||||
- **Enhanced**: Calculation accuracy and stability
|
||||
- **Increased**: Flush frequency for better data persistence
|
||||
- **Fixed**: Eager mode functionality
|
||||
- **Enhanced**: Performance and reliability
|
||||
|
||||
### Documentation
|
||||
- **Reset**: Changelog for clean organization
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.40...v0.0.42)
|
||||
|
||||
## [v0.0.40](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.40) - 2025-05-25
|
||||
|
||||
### Global System
|
||||
- **Fixed**: Multiple system-wide issues
|
||||
- **Enhanced**: Overall stability and performance
|
||||
- **Resolved**: Data accuracy issues in global computations
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.39...v0.0.40)
|
||||
|
||||
## [v0.0.39](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.39) - 2025-05-25
|
||||
|
||||
### UTXO System
|
||||
- **Implemented**: UTXO dataset functionality (initial part 1)
|
||||
- **Implemented**: UTXO dataset functionality (part 1)
|
||||
- **Added**: Foundation for unspent transaction output analysis
|
||||
- **Added**: Unspent transaction output tracking and analysis
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.37...v0.0.39)
|
||||
|
||||
## [v0.0.37](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.37) - 2025-05-14
|
||||
|
||||
### DCA Analysis
|
||||
- **Added**: Dollar-Cost Averaging (DCA) classes and analysis
|
||||
- **Enhanced**: Investment strategy analytics
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.36...v0.0.37)
|
||||
|
||||
## [v0.0.36](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.36) - 2025-05-13
|
||||
|
||||
### Website
|
||||
- **Added**: Price line functionality to charts
|
||||
- **Enhanced**: Chart visualization capabilities
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.35...v0.0.36)
|
||||
|
||||
## [v0.0.35](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.35) - 2025-05-12
|
||||
|
||||
### Financial Analytics
|
||||
- **Added**: Investment return analysis (lump sum vs DCA)
|
||||
- **Added**: Average and ratio datasets
|
||||
- **Added**: Market Simple Moving Averages (SMAs)
|
||||
- **Enhanced**: Financial analysis tools
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.34...v0.0.35)
|
||||
|
||||
## [v0.0.34](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.34) - 2025-05-09
|
||||
|
||||
### Website
|
||||
- **Added**: Market charts functionality
|
||||
- **Enhanced**: Chart display and interaction
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.33...v0.0.34)
|
||||
|
||||
## [v0.0.33](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.33) - 2025-05-08
|
||||
|
||||
### Vector Storage
|
||||
- **Fixed**: Computed vector eager path functionality
|
||||
- **Enhanced**: Path deletion for lazy vectors
|
||||
- **Improved**: Vector storage reliability
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.32...v0.0.33)
|
||||
|
||||
## [v0.0.32](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.32) - 2025-05-08
|
||||
|
||||
### Vector Storage - Lazy Computation
|
||||
- **Completed**: Lazy computation implementation
|
||||
- **Enhanced**: On-demand vector calculation
|
||||
- **Improved**: Memory efficiency for large datasets
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.31...v0.0.32)
|
||||
|
||||
## [v0.0.31](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.31) - 2025-04-30
|
||||
|
||||
### Global System
|
||||
- **Fixed**: Multiple system fixes and improvements
|
||||
- **Updated**: Fixed old X (Twitter) links that directed to impersonator accounts
|
||||
- **Enhanced**: Link security and accuracy
|
||||
|
||||
### Computer Module
|
||||
- **Removed**: Last indexes in favor of count-based approach
|
||||
- **Enhanced**: Index management efficiency
|
||||
|
||||
### Vector Storage
|
||||
- **Improved**: Vector iteration with `into_iter` for range collection
|
||||
- **Enhanced**: Iterator performance and memory usage
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.30...v0.0.31)
|
||||
|
||||
## [v0.0.30](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.30) - 2025-04-23
|
||||
|
||||
### Website
|
||||
- **Fixed**: Chart data fetching issues
|
||||
- **Enhanced**: Data loading reliability
|
||||
|
||||
### Global System
|
||||
- **Added**: Pay-to-Anchor (P2A) support
|
||||
- **Enhanced**: Bitcoin script type support
|
||||
- **Improved**: Transaction parsing capabilities
|
||||
|
||||
### Storage System
|
||||
- **Unified**: Single keyspace for all stores using fjall
|
||||
- **Enhanced**: Storage efficiency and organization
|
||||
|
||||
### Vector Storage
|
||||
- **Implemented**: Lazy/eager computation model
|
||||
- **Enhanced**: Flexible computation strategies
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.29...v0.0.30)
|
||||
|
||||
## [v0.0.29](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.29) - 2025-04-22
|
||||
|
||||
### Website (Kibo)
|
||||
- **Fixed**: TypeScript ignore additions for better compilation
|
||||
- **Enhanced**: Database functionality (part 2)
|
||||
- **Improved**: Database integration and management
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.28...v0.0.29)
|
||||
|
||||
## [v0.0.28](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.28) - 2025-04-19
|
||||
|
||||
### Website (Kibo)
|
||||
- **Implemented**: Database functionality (part 1)
|
||||
- **Added**: Initial database integration for kibo website
|
||||
|
||||
### Computer Module
|
||||
- **Enhanced**: Computer + kibo integration (part 14) with fixes
|
||||
- **Improved**: Cross-module functionality
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.27...v0.0.28)
|
||||
|
||||
## [v0.0.27](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.27) - 2025-04-18
|
||||
|
||||
### Distribution
|
||||
- **Fixed**: Tag version formatting issues
|
||||
- **Enhanced**: Release packaging improvements
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.26...v0.0.27)
|
||||
|
||||
## [v0.0.26](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.26) - 2025-04-18
|
||||
|
||||
### Distribution
|
||||
- **Fixed**: Ubuntu version compatibility
|
||||
- **Enhanced**: Linux distribution support
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.25...v0.0.26)
|
||||
|
||||
## [v0.0.25](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.25) - 2025-04-18
|
||||
|
||||
### Distribution
|
||||
- **Updated**: Ubuntu version for better compatibility
|
||||
- **Added**: Manual trigger for GitHub Actions
|
||||
- **Enhanced**: CI/CD pipeline flexibility
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.24...v0.0.25)
|
||||
|
||||
## [v0.0.24](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.24) - 2025-04-18
|
||||
|
||||
### Dependencies
|
||||
- **Updated**: Upgraded multiple crates for better performance
|
||||
- **Enhanced**: Dependency management
|
||||
|
||||
### Computer Module
|
||||
- **Enhanced**: Computer + kibo integration (part 13)
|
||||
- **Cleanup**: Kibo-related code organization
|
||||
- **Continued**: Computer module development (part 12)
|
||||
|
||||
### Documentation
|
||||
- **Updated**: Parser README documentation
|
||||
- **Enhanced**: Documentation clarity and completeness
|
||||
- **Updated**: Discord community links
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.23...v0.0.24)
|
||||
|
||||
## [v0.0.23](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.23) - 2025-04-14
|
||||
|
||||
### Dependencies
|
||||
- **Updated**: Dependency upgrades across the workspace
|
||||
- **Enhanced**: Compatibility and performance
|
||||
- **Enhanced**: System compatibility
|
||||
|
||||
### Computer Module
|
||||
- **Enhanced**: Computer + kibo integration (parts 10-11)
|
||||
- **Improved**: Cross-component functionality
|
||||
- **Improved**: Cross-module functionality
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.21...v0.0.23)
|
||||
|
||||
## [v0.0.21](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.21) - 2025-04-11
|
||||
|
||||
### Computer Module
|
||||
- **Enhanced**: Computer + kibo integration (part 9)
|
||||
- **Continued**: Ongoing computer module development
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.20...v0.0.21)
|
||||
|
||||
## [v0.0.20](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.20) - 2025-04-10
|
||||
|
||||
### Global Cleanup
|
||||
- **Cleaned**: Old files and legacy code
|
||||
- **Enhanced**: Codebase organization
|
||||
|
||||
### Vector Storage
|
||||
- **Reworked**: Vector storage implementation (parts 1-4)
|
||||
- **Optimized**: Performance improvements
|
||||
- **Enhanced**: Memory efficiency
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.19...v0.0.20)
|
||||
|
||||
## [v0.0.19](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.19) - 2025-04-07
|
||||
|
||||
### Computer Module
|
||||
- **Enhanced**: Computer functionality (parts 5-7)
|
||||
- **Improved**: Data processing capabilities
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.18...v0.0.19)
|
||||
|
||||
## [v0.0.18](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.18) - 2025-04-05
|
||||
|
||||
### Computer Module
|
||||
- **Enhanced**: Computer functionality (part 4)
|
||||
- **Continued**: Core computer development
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.17...v0.0.18)
|
||||
|
||||
## [v0.0.17](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.17) - 2025-04-05
|
||||
|
||||
### Distribution
|
||||
- **Moved**: Configuration to `config.toml` format
|
||||
- **Enhanced**: Configuration management
|
||||
|
||||
### Storage
|
||||
- **Initialized**: Disk-based storage functionality
|
||||
- **Enhanced**: Data persistence capabilities
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.16...v0.0.17)
|
||||
|
||||
## [v0.0.16](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.16) - 2025-04-04
|
||||
|
||||
### Server
|
||||
- **Fixed**: Repository version path issues
|
||||
- **Enhanced**: Static file serving
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.15...v0.0.16)
|
||||
|
||||
## [v0.0.15](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.15) - 2025-04-04
|
||||
|
||||
### Server
|
||||
- **Fixed**: Missing 'v' in version handling
|
||||
- **Enhanced**: Version management
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.14...v0.0.15)
|
||||
|
||||
## [v0.0.14](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.14) - 2025-04-04
|
||||
|
||||
### Server
|
||||
- **Fixed**: Downloaded repository version path
|
||||
- **Enhanced**: File path resolution
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.13...v0.0.14)
|
||||
|
||||
## [v0.0.13](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.13) - 2025-04-04
|
||||
|
||||
### Global System
|
||||
- **Enhanced**: System-wide improvements and snapshots
|
||||
- **Fixed**: Various stability issues
|
||||
|
||||
### Documentation
|
||||
- **Added**: Warning notices in README
|
||||
- **Enhanced**: User guidance and safety information
|
||||
|
||||
### Website (Kibo)
|
||||
- **Fixed**: Simulation functionality
|
||||
- **Enhanced**: User interface improvements
|
||||
- **Converted**: TypeScript types to JSDoc for better compatibility
|
||||
- **Upgraded**: Signals library to tresshaked v0.2.4
|
||||
|
||||
### Server
|
||||
- **Added**: API documentation
|
||||
- **Enhanced**: Development experience
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.12...v0.0.13)
|
||||
|
||||
## [v0.0.12](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.12) - 2025-04-02
|
||||
|
||||
### Server
|
||||
- **Enhanced**: API documentation and functionality
|
||||
- **Improved**: Developer experience
|
||||
|
||||
### Website (Kibo)
|
||||
- **Enhanced**: Various improvements (parts 1-5)
|
||||
- **Changed**: Font from Satoshi to Geist
|
||||
- **Improved**: Typography and design
|
||||
|
||||
### Documentation
|
||||
- **Updated**: README files with badges and shields
|
||||
- **Enhanced**: Project presentation
|
||||
|
||||
### Dependencies
|
||||
- **Updated**: Crate updates across workspace
|
||||
- **Enhanced**: Compatibility and performance
|
||||
|
||||
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0...v0.0.12)
|
||||
|
||||
## [v0.0](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0) - 2025-02-23
|
||||
|
||||
### Major Project Rewrite
|
||||
- **BREAKING**: Complete rewrite from legacy "kibo" to modern "BRK" (Bitcoin Research Kit)
|
||||
- **New**: Modular Rust workspace architecture with separate crates
|
||||
- **Added**: Modern build system with Cargo workspace
|
||||
- **Created**: New vector storage engine (`brk_vecs`, later became `vecdb`)
|
||||
- **Established**: Clear separation between parser, indexer, computer, and server modules
|
||||
- **Added**: MCP (Model Context Protocol) integration
|
||||
- **Added**: RESTful API server with multiple output formats
|
||||
- **Added**: Comprehensive CLI interface
|
||||
- **Enhanced**: Performance and scalability improvements
|
||||
|
||||
---
|
||||
|
||||
## Previous Releases (Legacy from kibo project)
|
||||
|
||||
<!--
|
||||
# v0.X.Y | WIP
|
||||

|
||||
|
||||
Generated
+291
-240
File diff suppressed because it is too large
Load Diff
+20
-18
@@ -4,10 +4,11 @@ members = ["crates/*"]
|
||||
package.description = "The Bitcoin Research Kit is a suite of tools designed to extract, compute and display data stored on a Bitcoin Core node"
|
||||
package.license = "MIT"
|
||||
package.edition = "2024"
|
||||
package.version = "0.0.84"
|
||||
package.version = "0.0.89"
|
||||
package.homepage = "https://bitcoinresearchkit.org"
|
||||
package.repository = "https://github.com/bitcoinresearchkit/brk"
|
||||
package.readme = "README.md"
|
||||
package.rust-version = "1.89"
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
@@ -25,21 +26,19 @@ inherits = "release"
|
||||
axum = "0.8.4"
|
||||
bitcoin = { version = "0.32.7", features = ["serde"] }
|
||||
bitcoincore-rpc = "0.19.0"
|
||||
brk_bundler = { version = "0.0.84", path = "crates/brk_bundler" }
|
||||
brk_cli = { version = "0.0.84", path = "crates/brk_cli" }
|
||||
brk_computer = { version = "0.0.84", path = "crates/brk_computer" }
|
||||
brk_structs = { version = "0.0.84", path = "crates/brk_structs" }
|
||||
brk_error = { version = "0.0.84", path = "crates/brk_error" }
|
||||
brk_fetcher = { version = "0.0.84", path = "crates/brk_fetcher" }
|
||||
brk_indexer = { version = "0.0.84", path = "crates/brk_indexer" }
|
||||
brk_interface = { version = "0.0.84", path = "crates/brk_interface" }
|
||||
brk_logger = { version = "0.0.84", path = "crates/brk_logger" }
|
||||
brk_mcp = { version = "0.0.84", path = "crates/brk_mcp" }
|
||||
brk_parser = { version = "0.0.84", path = "crates/brk_parser" }
|
||||
brk_server = { version = "0.0.84", path = "crates/brk_server" }
|
||||
brk_store = { version = "0.0.84", path = "crates/brk_store" }
|
||||
brk_vecs = { version = "0.0.84", path = "crates/brk_vecs" }
|
||||
brk_vecs_macros = { version = "0.0.84", path = "crates/brk_vecs_macros" }
|
||||
brk_bundler = { version = "0.0.89", path = "crates/brk_bundler" }
|
||||
brk_cli = { version = "0.0.89", path = "crates/brk_cli" }
|
||||
brk_computer = { version = "0.0.89", path = "crates/brk_computer" }
|
||||
brk_error = { version = "0.0.89", path = "crates/brk_error" }
|
||||
brk_fetcher = { version = "0.0.89", path = "crates/brk_fetcher" }
|
||||
brk_indexer = { version = "0.0.89", path = "crates/brk_indexer" }
|
||||
brk_interface = { version = "0.0.89", path = "crates/brk_interface" }
|
||||
brk_logger = { version = "0.0.89", path = "crates/brk_logger" }
|
||||
brk_mcp = { version = "0.0.89", path = "crates/brk_mcp" }
|
||||
brk_parser = { version = "0.0.89", path = "crates/brk_parser" }
|
||||
brk_server = { version = "0.0.89", path = "crates/brk_server" }
|
||||
brk_store = { version = "0.0.89", path = "crates/brk_store" }
|
||||
brk_structs = { version = "0.0.89", path = "crates/brk_structs" }
|
||||
byteview = "=0.6.1"
|
||||
derive_deref = "1.1.1"
|
||||
fjall = "2.11.2"
|
||||
@@ -47,12 +46,14 @@ jiff = "0.2.15"
|
||||
log = "0.4.27"
|
||||
minreq = { version = "2.14.0", features = ["https", "serde_json"] }
|
||||
parking_lot = "0.12.4"
|
||||
rayon = "1.10.0"
|
||||
rayon = "1.11.0"
|
||||
serde = "1.0.219"
|
||||
serde_bytes = "0.11.17"
|
||||
serde_derive = "1.0.219"
|
||||
serde_json = { version = "1.0.142", features = ["float_roundtrip"] }
|
||||
serde_json = { version = "1.0.143", features = ["float_roundtrip"] }
|
||||
tokio = { version = "1.47.1", features = ["rt-multi-thread"] }
|
||||
# vecdb = { path = "../seqdb/crates/vecdb", features = ["derive"]}
|
||||
vecdb = { version = "0.2.4", features = ["derive"]}
|
||||
zerocopy = "0.8.26"
|
||||
zerocopy-derive = "0.8.26"
|
||||
|
||||
@@ -67,3 +68,4 @@ cargo-dist-version = "0.29.0"
|
||||
ci = "github"
|
||||
installers = []
|
||||
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"]
|
||||
rust-toolchain-version = "1.89"
|
||||
|
||||
@@ -52,6 +52,27 @@ The primary goal of this project is to be fully-featured and accessible for ever
|
||||
|
||||
In contrast, existing alternatives tend to be either [very costly](https://studio.glassnode.com/pricing) or missing essential features, with the vast majority being closed-source and unverifiable, which fundamentally undermines the principles of Bitcoin.
|
||||
|
||||
## Hosting as a service
|
||||
|
||||
If you'd like to have your own instance hosted for you please contact [hosting@bitcoinresearchkit.org](mailto:hosting@bitcoinresearchkit.org).
|
||||
|
||||
- 2 separate dedicated servers (1 GB/s each) with different ISPs and Cloudflare integration for enhanced performance and optimal availability
|
||||
- 99.99% SLA
|
||||
- Configured for speed
|
||||
- Updates delivered at your convenience
|
||||
- Direct communication for feature requests and support
|
||||
- Bitcoin Core or Knots with desired version
|
||||
- Optional subdomains: `*.bitcoinresearchkit.org`, `*.brekit.org`, `*.kibo.money` and `*.satonomics.xyz`
|
||||
- Logo featured in the Readme if desired
|
||||
|
||||
Pricing: `0.01 BTC / month` *or* `0.1 BTC / year`
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
Deepest gratitude to the [Open Sats](https://opensats.org/) public charity. Their grant — from December 2024 to the present — has been critical in sustaining this project.
|
||||
|
||||
Heartfelt thanks go out to every donor on [Nostr](https://primal.net/p/npub1jagmm3x39lmwfnrtvxcs9ac7g300y3dusv9lgzhk2e4x5frpxlrqa73v44) and [Geyser.fund](https://geyser.fund/project/brk) whose support has ensured the availability of the [bitcoinresearchkit.org](https://bitcoinresearchkit.org) public instance.
|
||||
|
||||
## Crates
|
||||
|
||||
- [`brk`](https://crates.io/crates/brk): A wrapper around all other `brk-*` crates
|
||||
@@ -68,29 +89,6 @@ In contrast, existing alternatives tend to be either [very costly](https://studi
|
||||
- [`brk_server`](https://crates.io/crates/brk_server): A server with an API for anything from BRK
|
||||
- [`brk_store`](https://crates.io/crates/brk_store): A thin wrapper around [`fjall`](https://crates.io/crates/fjall)
|
||||
- [`brk_structs`](https://crates.io/crates/brk_structs): Structs used throughout BRK
|
||||
- [`brk_vecs`](https://crates.io/crates/brk_vecs): A KISS index/value store
|
||||
- [`brk_vecs_macros`](https://crates.io/crates/brk_vecs_macros): Macros for [`brk_vecs`](https://crates.io/crates/brk_vecs)
|
||||
|
||||
## Hosting as a service
|
||||
|
||||
If you'd like to have your own instance hosted for you please contact [hosting@bitcoinresearchkit.org](mailto:hosting@bitcoinresearchkit.org).
|
||||
|
||||
- 2 separate dedicated servers (1 GB/s each) with different ISPs and Cloudflare integration for enhanced performance and optimal availability
|
||||
- 99.99% SLA
|
||||
- Configured for speed
|
||||
- Updates delivered at your convenience
|
||||
- Direct communication for feature requests and support
|
||||
- Bitcoin Core or Knots with desired version
|
||||
- Optional subdomains: `*.bitcoinresearchkit.org`, `*.kibo.money` and `*.satonomics.xyz`
|
||||
- Logo featured in the Readme if desired
|
||||
|
||||
Pricing: `0.01 BTC / month` *or* `0.1 BTC / year`
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
Deepest gratitude to the [Open Sats](https://opensats.org/) public charity. Their grant — from December 2024 to the present — has been critical in sustaining this project.
|
||||
|
||||
Heartfelt thanks go out to every donor on [Nostr](https://primal.net/p/npub1jagmm3x39lmwfnrtvxcs9ac7g300y3dusv9lgzhk2e4x5frpxlrqa73v44) and [Geyser.fund](https://geyser.fund/project/brk) whose support has ensured the availability of the [bitcoinresearchkit.org](https://bitcoinresearchkit.org) public instance.
|
||||
|
||||
## Donate
|
||||
|
||||
|
||||
@@ -60,11 +60,6 @@
|
||||
- add extensions support (.json .csv …)
|
||||
- if format instead of extension then don't download file
|
||||
- add support for https (rustls)
|
||||
- lru cache
|
||||
- _vec_
|
||||
- add native lock file support (once it's available in stable rust)
|
||||
- improve compressed mode (slow reads)
|
||||
- add ema support
|
||||
- __docs__
|
||||
- _README_
|
||||
- add a comparison table with alternatives
|
||||
|
||||
@@ -7,6 +7,7 @@ homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
version.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[features]
|
||||
@@ -23,7 +24,6 @@ full = [
|
||||
"server",
|
||||
"store",
|
||||
"structs",
|
||||
"vecs",
|
||||
]
|
||||
bundler = ["brk_bundler"]
|
||||
computer = ["brk_computer"]
|
||||
@@ -37,7 +37,6 @@ parser = ["brk_parser"]
|
||||
server = ["brk_server"]
|
||||
store = ["brk_store"]
|
||||
structs = ["brk_structs"]
|
||||
vecs = ["brk_vecs"]
|
||||
|
||||
[dependencies]
|
||||
brk_bundler = { workspace = true, optional = true }
|
||||
@@ -53,7 +52,6 @@ brk_parser = { workspace = true, optional = true }
|
||||
brk_server = { workspace = true, optional = true }
|
||||
brk_store = { workspace = true, optional = true }
|
||||
brk_structs = { workspace = true, optional = true }
|
||||
brk_vecs = { workspace = true, optional = true }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
# brk
|
||||
|
||||
**Main wrapper crate for the Bitcoin Research Kit (BRK)**
|
||||
|
||||
The `brk` crate serves as the primary entry point for the Bitcoin Research Kit, providing a unified interface to all BRK components through feature flags. It enables developers to selectively include only the components they need while maintaining a consistent API.
|
||||
|
||||
## What it provides
|
||||
|
||||
- **Unified Access**: Single crate providing access to the entire BRK ecosystem
|
||||
- **Feature-based Selection**: Choose only the components you need
|
||||
- **Consistent Versioning**: All components versioned together for compatibility
|
||||
- **Simplified Dependencies**: Single dependency instead of multiple individual crates
|
||||
|
||||
## Available Components
|
||||
|
||||
### Core Data Pipeline
|
||||
- **`parser`** ([brk_parser](../brk_parser/)) - High-performance Bitcoin block parser
|
||||
- **`indexer`** ([brk_indexer](../brk_indexer/)) - Blockchain data indexer with dual storage
|
||||
- **`computer`** ([brk_computer](../brk_computer/)) - Analytics engine for computed datasets
|
||||
- **`interface`** ([brk_interface](../brk_interface/)) - Unified data query and formatting API
|
||||
|
||||
### Infrastructure
|
||||
- **`structs`** ([brk_structs](../brk_structs/)) - Bitcoin-aware type system and data structures
|
||||
- **`error`** ([brk_error](../brk_error/)) - Centralized error handling
|
||||
- **`store`** ([brk_store](../brk_store/)) - Blockchain-aware key-value storage
|
||||
|
||||
### External Integration
|
||||
- **`fetcher`** ([brk_fetcher](../brk_fetcher/)) - Bitcoin price data fetcher with multi-source fallback
|
||||
- **`server`** ([brk_server](../brk_server/)) - HTTP server with REST API
|
||||
- **`mcp`** ([brk_mcp](../brk_mcp/)) - Model Context Protocol for LLM integration
|
||||
|
||||
### Utilities
|
||||
- **`cli`** ([brk_cli](../brk_cli/)) - Command line interface (always enabled)
|
||||
- **`logger`** ([brk_logger](../brk_logger/)) - Logging utilities
|
||||
- **`bundler`** ([brk_bundler](../brk_bundler/)) - Asset bundling for web interfaces
|
||||
|
||||
## Usage
|
||||
|
||||
### Full Installation
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
brk = { version = "0.0.88", features = ["full"] }
|
||||
```
|
||||
|
||||
```rust
|
||||
use brk::*;
|
||||
|
||||
// Access all components
|
||||
let config = cli::Config::load()?;
|
||||
let parser = parser::Parser::new(/* ... */);
|
||||
let indexer = indexer::Indexer::forced_import("./data")?;
|
||||
let computer = computer::Computer::forced_import("./data", &indexer, None)?;
|
||||
```
|
||||
|
||||
### Selective Components
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
brk = { version = "0.0.88", features = ["parser", "indexer", "computer"] }
|
||||
```
|
||||
|
||||
```rust
|
||||
use brk::{parser, indexer, computer};
|
||||
|
||||
// Core data pipeline only
|
||||
let parser = parser::Parser::new(blocks_dir, output_dir, rpc);
|
||||
let mut indexer = indexer::Indexer::forced_import(output_dir)?;
|
||||
let mut computer = computer::Computer::forced_import(output_dir, &indexer, None)?;
|
||||
```
|
||||
|
||||
### Minimal Setup
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
brk = { version = "0.0.88", features = ["structs", "parser"] }
|
||||
```
|
||||
|
||||
```rust
|
||||
use brk::{structs, parser};
|
||||
|
||||
// Just parsing and types
|
||||
let height = structs::Height::new(800_000);
|
||||
let parser = parser::Parser::new(blocks_dir, output_dir, rpc);
|
||||
```
|
||||
|
||||
## Feature Flags
|
||||
|
||||
| Feature | Description | Dependencies |
|
||||
|---------|-------------|--------------|
|
||||
| `full` | Enable all components | All crates |
|
||||
| `cli` | Command line interface | Always enabled |
|
||||
| `structs` | Core type system | Foundation for other crates |
|
||||
| `error` | Error handling | Used by most crates |
|
||||
| `parser` | Block parsing | `structs`, `error` |
|
||||
| `store` | Key-value storage | `structs`, `error` |
|
||||
| `indexer` | Blockchain indexing | `parser`, `store` |
|
||||
| `computer` | Analytics computation | `indexer`, `fetcher` (optional) |
|
||||
| `fetcher` | Price data fetching | `structs`, `error` |
|
||||
| `interface` | Data query API | `indexer`, `computer` |
|
||||
| `server` | HTTP server | `interface`, `mcp` |
|
||||
| `mcp` | LLM integration | `interface` |
|
||||
| `logger` | Logging utilities | Standalone |
|
||||
| `bundler` | Asset bundling | Standalone |
|
||||
|
||||
## Common Usage Patterns
|
||||
|
||||
### Complete BRK Instance
|
||||
|
||||
```rust
|
||||
use brk::*;
|
||||
|
||||
// Full data pipeline setup
|
||||
let config = cli::Config::load()?;
|
||||
let rpc = /* Bitcoin Core RPC client */;
|
||||
let parser = parser::Parser::new(config.blocks_dir, config.output_dir, rpc);
|
||||
let mut indexer = indexer::Indexer::forced_import(&config.output_dir)?;
|
||||
let mut computer = computer::Computer::forced_import(&config.output_dir, &indexer, None)?;
|
||||
let interface = interface::Interface::build(&indexer, &computer);
|
||||
let server = server::Server::new(interface, config.website_path);
|
||||
|
||||
// Start server
|
||||
server.serve(true).await?;
|
||||
```
|
||||
|
||||
### Data Analysis
|
||||
|
||||
```rust
|
||||
use brk::{indexer, computer, interface};
|
||||
|
||||
// Analysis-focused setup
|
||||
let indexer = indexer::Indexer::forced_import("./brk_data")?;
|
||||
let computer = computer::Computer::forced_import("./brk_data", &indexer, None)?;
|
||||
let interface = interface::Interface::build(&indexer, &computer);
|
||||
|
||||
// Query data
|
||||
let params = interface::Params {
|
||||
index: interface::Index::Height,
|
||||
ids: vec!["price_usd", "difficulty"].into(),
|
||||
rest: interface::ParamsOpt::default()
|
||||
.set_from(-100)
|
||||
.set_format(interface::Format::CSV),
|
||||
};
|
||||
|
||||
let csv_data = interface.search_and_format(params)?;
|
||||
```
|
||||
|
||||
### Custom Integration
|
||||
|
||||
```rust
|
||||
use brk::{structs, parser, error};
|
||||
|
||||
// Custom application with BRK components
|
||||
fn analyze_blocks() -> error::Result<()> {
|
||||
let parser = parser::Parser::new(blocks_dir, output_dir, rpc);
|
||||
|
||||
parser.parse(None, None)
|
||||
.iter()
|
||||
.take(1000) // First 1000 blocks
|
||||
.for_each(|(height, block, hash)| {
|
||||
println!("Block {}: {} transactions", height, block.txdata.len());
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Version Compatibility
|
||||
|
||||
All BRK crates are released together with synchronized versions. When using the `brk` wrapper crate, you're guaranteed compatibility between all components.
|
||||
|
||||
- **Current version**: 0.0.88
|
||||
- **Rust MSRV**: 1.89+
|
||||
- **Bitcoin Core**: v25.0 - v29.0
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
The `brk` crate itself adds no runtime overhead - it simply re-exports the underlying crates. Performance characteristics depend on which components you use:
|
||||
|
||||
- **Full pipeline**: ~13-15 hours initial sync, ~40GB storage overhead
|
||||
- **Parser only**: ~4 minutes to parse entire blockchain
|
||||
- **Indexer only**: ~7-8 hours to index blockchain, ~5-6GB RAM usage
|
||||
- **Server**: Low latency API responses with caching and compression
|
||||
|
||||
## Dependencies
|
||||
|
||||
The `brk` crate's dependencies are determined by enabled features. Core dependencies include:
|
||||
|
||||
- `brk_cli` - Always included for configuration and CLI support
|
||||
- Individual `brk_*` crates based on enabled features
|
||||
- Transitive dependencies from enabled components
|
||||
|
||||
For specific dependency information, see individual crate READMEs.
|
||||
|
||||
---
|
||||
|
||||
*This README was generated by Claude Code*
|
||||
@@ -1 +0,0 @@
|
||||
fn main() {}
|
||||
@@ -1,4 +1,4 @@
|
||||
#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
#[cfg(feature = "bundler")]
|
||||
#[doc(inline)]
|
||||
@@ -50,7 +50,3 @@ pub use brk_server as server;
|
||||
#[cfg(feature = "store")]
|
||||
#[doc(inline)]
|
||||
pub use brk_store as store;
|
||||
|
||||
#[cfg(feature = "vecs")]
|
||||
#[doc(inline)]
|
||||
pub use brk_vecs as vecs;
|
||||
|
||||
@@ -6,6 +6,7 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
# brk_bundler
|
||||
|
||||
**Asset bundling for BRK web interfaces using Rolldown**
|
||||
|
||||
`brk_bundler` provides JavaScript/TypeScript bundling capabilities for BRK's web interfaces. It's a thin wrapper around Rolldown (Rust-based Rollup alternative) with BRK-specific configuration for building optimized web assets with file watching and automatic rebuilding.
|
||||
|
||||
## What it provides
|
||||
|
||||
- **JavaScript Bundling**: Modern ES modules bundling with minification
|
||||
- **File Watching**: Automatic rebuilding on source file changes
|
||||
- **Asset Processing**: Copies and processes static assets
|
||||
- **Version Injection**: Automatic version string replacement in service workers
|
||||
- **Development Mode**: Live rebuilding for rapid development
|
||||
|
||||
## Key Features
|
||||
|
||||
### Bundling Capabilities
|
||||
- **ES Module Support**: Modern JavaScript bundling with tree-shaking
|
||||
- **Minification**: Automatic code minification for production builds
|
||||
- **Source Maps**: Generated source maps for debugging
|
||||
- **Entry Point Processing**: Configurable entry points with hashed output names
|
||||
|
||||
### File System Operations
|
||||
- **Directory Copying**: Copies entire source directories to distribution
|
||||
- **Selective Processing**: Special handling for specific file types
|
||||
- **Path Resolution**: Automatic path resolution and asset linking
|
||||
|
||||
### Development Features
|
||||
- **Hot Rebuilding**: Automatic rebuilds on file changes
|
||||
- **Watch Mode**: Monitors source files and triggers rebuilds
|
||||
- **Version Replacement**: Injects build version into service workers
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Bundling
|
||||
|
||||
```rust
|
||||
use brk_bundler::bundle;
|
||||
use std::path::Path;
|
||||
|
||||
// Bundle without watching (production)
|
||||
let websites_path = Path::new("./websites");
|
||||
let source_folder = "default";
|
||||
let dist_path = bundle(websites_path, source_folder, false).await?;
|
||||
|
||||
println!("Bundled to: {:?}", dist_path);
|
||||
```
|
||||
|
||||
### Development Mode with Watching
|
||||
|
||||
```rust
|
||||
// Bundle with file watching (development)
|
||||
let dist_path = bundle(websites_path, "default", true).await?;
|
||||
|
||||
// Bundler now watches for changes and rebuilds automatically
|
||||
// This will run in the background until the process exits
|
||||
```
|
||||
|
||||
### Integration with BRK CLI
|
||||
|
||||
```rust
|
||||
// Typically called from brk_cli when serving websites
|
||||
async fn setup_website(config: &Config) -> Result<PathBuf> {
|
||||
let websites_path = config.websites_path();
|
||||
let source_folder = match config.website_mode {
|
||||
WebsiteMode::Default => "default",
|
||||
WebsiteMode::Custom => "custom",
|
||||
WebsiteMode::None => return Ok(PathBuf::new()),
|
||||
};
|
||||
|
||||
// Bundle the website assets
|
||||
let dist_path = bundle(websites_path, source_folder, config.dev_mode).await?;
|
||||
|
||||
Ok(dist_path)
|
||||
}
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
The bundler expects this directory structure:
|
||||
|
||||
```
|
||||
websites/
|
||||
├── default/ # Default website source
|
||||
│ ├── index.html # Main HTML file
|
||||
│ ├── service-worker.js # Service worker (version injected)
|
||||
│ ├── scripts/ # JavaScript/TypeScript source
|
||||
│ │ ├── entry.js # Main entry point
|
||||
│ │ ├── main.js # Application logic
|
||||
│ │ └── ... # Other JS modules
|
||||
│ └── assets/ # Static assets
|
||||
└── dist/ # Generated output directory
|
||||
├── index.html # Processed HTML with updated script references
|
||||
├── service-worker.js # Service worker with version injected
|
||||
├── scripts/ # Bundled and minified JavaScript
|
||||
│ └── main-[hash].js # Hashed output file
|
||||
└── assets/ # Copied static assets
|
||||
```
|
||||
|
||||
## Bundling Process
|
||||
|
||||
1. **Clean**: Removes existing `dist/` directory
|
||||
2. **Copy**: Copies all source files to `dist/`
|
||||
3. **Bundle JavaScript**:
|
||||
- Processes `scripts/entry.js` as entry point
|
||||
- Generates minified bundle with source maps
|
||||
- Creates hashed filename for cache busting
|
||||
4. **Process HTML**: Updates script references to hashed filenames
|
||||
5. **Process Service Worker**: Injects current version string
|
||||
6. **Watch** (if enabled): Monitors for file changes and rebuilds
|
||||
|
||||
## Configuration
|
||||
|
||||
The bundler uses Rolldown with these optimized settings:
|
||||
|
||||
```rust
|
||||
BundlerOptions {
|
||||
input: Some(vec![source_entry.into()]), // scripts/entry.js
|
||||
dir: Some("./dist/scripts".to_string()), // Output directory
|
||||
cwd: Some(websites_path), // Working directory
|
||||
minify: Some(RawMinifyOptions::Bool(true)), // Enable minification
|
||||
sourcemap: Some(SourceMapType::File), // Generate source maps
|
||||
..Default::default()
|
||||
}
|
||||
```
|
||||
|
||||
## File Watching
|
||||
|
||||
In watch mode, the bundler monitors:
|
||||
|
||||
- **Source files**: Non-script files are copied on change
|
||||
- **JavaScript files**: Trigger full rebuild via Rolldown watcher
|
||||
- **HTML files**: Processed to update script references
|
||||
- **Service worker**: Version injection on changes
|
||||
|
||||
### Watch Events Handled
|
||||
|
||||
- `Create` - New files added
|
||||
- `Modify` - Existing files changed
|
||||
- Ignores `Delete` and other events
|
||||
|
||||
## Version Injection
|
||||
|
||||
Service workers get automatic version injection:
|
||||
|
||||
```javascript
|
||||
// In source service-worker.js
|
||||
const VERSION = '__VERSION__';
|
||||
|
||||
// After bundling
|
||||
const VERSION = 'v0.0.88';
|
||||
```
|
||||
|
||||
This enables proper cache invalidation across releases.
|
||||
|
||||
## Performance Features
|
||||
|
||||
- **Async Operations**: All bundling operations are async
|
||||
- **Incremental Builds**: Only rebuilds changed files in watch mode
|
||||
- **Parallel Processing**: Uses Tokio for concurrent file operations
|
||||
- **Efficient Copying**: Direct file system operations
|
||||
|
||||
## Error Handling
|
||||
|
||||
- **Graceful Failures**: Logs errors but continues watching
|
||||
- **Path Resolution**: Automatic path absolutization and validation
|
||||
- **File System Errors**: Proper error propagation with context
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `brk_rolldown` - Rust-based Rollup bundler
|
||||
- `notify` - File system watching
|
||||
- `tokio` - Async runtime for file operations
|
||||
- `sugar_path` - Path manipulation utilities
|
||||
- `log` - Error logging
|
||||
|
||||
## Integration Points
|
||||
|
||||
The bundler integrates with:
|
||||
- **brk_cli**: Called during website setup
|
||||
- **brk_server**: Serves bundled assets
|
||||
- **Development workflow**: Provides live rebuilding
|
||||
|
||||
---
|
||||
|
||||
*This README was generated by Claude Code*
|
||||
@@ -1,3 +1,5 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
use std::{
|
||||
fs, io,
|
||||
path::{Path, PathBuf},
|
||||
|
||||
@@ -6,6 +6,7 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
@@ -18,16 +19,16 @@ brk_interface = { workspace = true }
|
||||
brk_logger = { workspace = true }
|
||||
brk_parser = { workspace = true }
|
||||
brk_server = { workspace = true }
|
||||
brk_vecs = { workspace = true }
|
||||
clap = { version = "4.5.43", features = ["string"] }
|
||||
clap_derive = "4.5.41"
|
||||
vecdb = { workspace = true }
|
||||
clap = { version = "4.5.45", features = ["string"] }
|
||||
clap_derive = "4.5.45"
|
||||
color-eyre = "0.6.5"
|
||||
log = { workspace = true }
|
||||
minreq = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
toml = "0.9.5"
|
||||
zip = { version = "4.3.0", default-features = false, features = ["deflate"] }
|
||||
zip = { version = "4.5.0", default-features = false, features = ["deflate"] }
|
||||
|
||||
[[bin]]
|
||||
name = "brk"
|
||||
|
||||
+166
-67
@@ -1,90 +1,189 @@
|
||||
# BRK CLI
|
||||
# brk_cli
|
||||
|
||||
<p align="left">
|
||||
<a href="https://github.com/bitcoinresearchkit/brk">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/bitcoinresearchkit/brk?style=social">
|
||||
</a>
|
||||
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
|
||||
<img src="https://img.shields.io/crates/l/brk" alt="License" />
|
||||
</a>
|
||||
<a href="https://crates.io/crates/brk_cli">
|
||||
<img src="https://img.shields.io/crates/v/brk_cli" alt="Version" />
|
||||
</a>
|
||||
<a href="https://docs.rs/brk_cli">
|
||||
<img src="https://img.shields.io/docsrs/brk_cli" alt="Documentation" />
|
||||
</a>
|
||||
<img src="https://img.shields.io/crates/size/brk_cli" alt="Size" />
|
||||
<a href="https://deps.rs/crate/brk_cli">
|
||||
<img src="https://deps.rs/crate/brk_cli/latest/status.svg" alt="Dependency status">
|
||||
</a>
|
||||
<a href="https://discord.gg/HaR3wpH3nr">
|
||||
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
|
||||
</a>
|
||||
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
|
||||
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
|
||||
</a>
|
||||
</p>
|
||||
**Command line interface for running complete BRK instances**
|
||||
|
||||
A command line interface to run a Bitcoin Research Kit instance.
|
||||
`brk_cli` provides the main command-line interface for operating the Bitcoin Research Kit. It orchestrates the complete data pipeline from Bitcoin Core block parsing through analytics computation to HTTP API serving, with automatic configuration management and graceful operation.
|
||||
|
||||
It's very customizable with all parameters from the underlying tools (crates) used inside.
|
||||
## What it provides
|
||||
|
||||
Run `brk -h` for more information.
|
||||
- **Complete Pipeline Orchestration**: Coordinates parser, indexer, computer, and server components
|
||||
- **Automatic Configuration**: Saves settings to `~/.brk/config.toml` for consistent operation
|
||||
- **Continuous Operation**: Handles blockchain updates and incremental processing
|
||||
- **Web Interface Options**: Configurable website serving (none, default, custom)
|
||||
- **Graceful Shutdown**: Ctrl+C handling with proper cleanup
|
||||
|
||||
## Requirements
|
||||
## Key Features
|
||||
|
||||
### Hardware
|
||||
### Pipeline Management
|
||||
- **Automatic dependency handling**: Ensures Bitcoin Core sync before processing
|
||||
- **Incremental updates**: Only processes new blocks since last run
|
||||
- **Error recovery**: Automatic retry logic and graceful error handling
|
||||
- **Resource management**: Optimized memory usage and disk I/O
|
||||
|
||||
#### Recommended
|
||||
### Configuration System
|
||||
- **Auto-save configuration**: All CLI options saved to persistent config
|
||||
- **Flexible paths**: Configurable Bitcoin directory, blocks directory, and output directory
|
||||
- **RPC authentication**: Cookie file or username/password authentication
|
||||
- **Data source options**: Configurable price fetching and exchange APIs
|
||||
|
||||
- [Latest base model Mac mini](https://www.apple.com/mac-mini/)
|
||||
- [Thunderbolt 4 SSD enclosure](https://satechi.net/products/usb4-nvme-ssd-pro-enclosure/Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC80MDE4ODQ3MDA2NzI4OA==?queryID=7961465089021ee203a60db7e62e90d2)
|
||||
- [2 TB NVMe SSD](https://shop.sandisk.com/products/ssd/internal-ssd/wd-black-sn850x-nvme-ssd?sku=WDS200T2X0E-00BCA0)
|
||||
### Operation Modes
|
||||
- **Initial sync**: Full blockchain processing from genesis
|
||||
- **Continuous operation**: Real-time processing of new blocks
|
||||
- **Update mode**: Resume from last processed block
|
||||
- **Server mode**: HTTP API with optional web interface
|
||||
|
||||
#### Minimum
|
||||
|
||||
To be determined
|
||||
|
||||
### Software
|
||||
|
||||
- [Bitcoin](https://bitcoin.org/en/full-node)
|
||||
- [Rust](https://www.rust-lang.org/tools/install)
|
||||
- Unix based operating system (Mac OS or Linux)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Ubuntu users need to install `open-ssl` via `sudo apt install libssl-dev pkg-config`
|
||||
|
||||
## Download
|
||||
|
||||
### Binaries
|
||||
|
||||
You can find a pre-built binary for your operating system in the [releases page](https://github.com/bitcoinresearchkit/brk/releases/latest).
|
||||
|
||||
### Cargo
|
||||
## Installation
|
||||
|
||||
### Binary Release
|
||||
```bash
|
||||
# Install
|
||||
cargo install brk --locked # or `cargo install brk_cli`, the result is the same
|
||||
|
||||
# Update
|
||||
cargo install brk --locked # or `cargo install-update -a` if you have `cargo-update` installed
|
||||
# Download from GitHub releases
|
||||
# https://github.com/bitcoinresearchkit/brk/releases/latest
|
||||
```
|
||||
|
||||
### Source
|
||||
### Via Cargo
|
||||
```bash
|
||||
cargo install brk --locked
|
||||
```
|
||||
|
||||
### From Source
|
||||
```bash
|
||||
git clone https://github.com/bitcoinresearchkit/brk.git
|
||||
cd brk/crates/brk
|
||||
cargo run -r
|
||||
cd brk && cargo build --release
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Run `brk -h` to view each available parameter and their respective description.
|
||||
### First Run (Configuration Setup)
|
||||
|
||||
> [!TIP]
|
||||
> Every parameter set will be saved at `~/.brk/config.toml`, which allows you to simply run `brk` next time.
|
||||
```bash
|
||||
# Basic setup with default options
|
||||
brk --brkdir ./my_brk_data
|
||||
|
||||
## Tunnel
|
||||
# Full configuration
|
||||
brk --bitcoindir ~/.bitcoin \
|
||||
--brkdir ./brk_data \
|
||||
--fetch true \
|
||||
--exchanges true \
|
||||
--website default
|
||||
```
|
||||
|
||||
The easiest way to let others access your server is to use `cloudflared` which will also cache requests. For more information see [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) documentation.
|
||||
### Subsequent Runs
|
||||
|
||||
```bash
|
||||
# Uses saved configuration from ~/.brk/config.toml
|
||||
brk
|
||||
|
||||
# Override specific options
|
||||
brk --website none --fetch false
|
||||
```
|
||||
|
||||
### Command Line Options
|
||||
|
||||
```bash
|
||||
brk --help
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
All options are automatically saved to `~/.brk/config.toml`:
|
||||
|
||||
### Core Paths
|
||||
- `--bitcoindir <PATH>` - Bitcoin Core directory (default: `~/.bitcoin`)
|
||||
- `--blocksdir <PATH>` - Block files directory (default: `bitcoindir/blocks`)
|
||||
- `--brkdir <PATH>` - BRK output directory (default: `~/.brk`)
|
||||
|
||||
### Data Sources
|
||||
- `--fetch <BOOL>` - Enable price data fetching (default: `true`)
|
||||
- `--exchanges <BOOL>` - Use exchange APIs for prices (default: `true`)
|
||||
|
||||
### Web Interface
|
||||
- `--website <OPTION>` - Web interface mode:
|
||||
- `none` - API only, no web interface
|
||||
- `default` - Built-in web interface from `websites/default/`
|
||||
- `custom` - Serve custom website from `websites/custom/`
|
||||
|
||||
### Bitcoin Core RPC
|
||||
- `--rpcconnect <IP>` - RPC host (default: `localhost`)
|
||||
- `--rpcport <PORT>` - RPC port (default: `8332`)
|
||||
- `--rpccookiefile <PATH>` - Cookie authentication file
|
||||
- `--rpcuser <USERNAME>` - Username authentication
|
||||
- `--rpcpassword <PASSWORD>` - Password authentication
|
||||
|
||||
## Operation Flow
|
||||
|
||||
1. **Configuration Loading**: Loads saved config from `~/.brk/config.toml`
|
||||
2. **Bitcoin Core Connection**: Establishes RPC connection and waits for sync
|
||||
3. **Data Pipeline Initialization**: Sets up parser, indexer, computer, and interface
|
||||
4. **Processing Loop**:
|
||||
- Index new blocks from Bitcoin Core
|
||||
- Compute analytics on new data
|
||||
- Update cached data
|
||||
5. **Server Startup**: Launches HTTP API with optional web interface
|
||||
6. **Continuous Operation**: Monitors for new blocks and processes incrementally
|
||||
|
||||
## System Requirements
|
||||
|
||||
- **Bitcoin Core**: Fully synced node with RPC enabled
|
||||
- **Storage**: ~32% of blockchain size (~233GB as of 2025)
|
||||
- **Memory**:
|
||||
- Peak: ~7-8GB during initial indexing
|
||||
- Steady state: ~4-5GB during operation
|
||||
- **OS**: macOS or Linux
|
||||
- Ubuntu: `sudo apt install libssl-dev pkg-config`
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
### Initial Sync
|
||||
- **Full blockchain processing**: ~13-15 hours total
|
||||
- **Parser phase**: ~4 minutes for block parsing
|
||||
- **Indexer phase**: ~7-8 hours for data indexing
|
||||
- **Computer phase**: ~6-7 hours for analytics computation
|
||||
|
||||
### Continuous Operation
|
||||
- **New block processing**: 3-5 seconds per block
|
||||
- **API response times**: Typically <100ms with caching
|
||||
- **Memory usage**: Stable ~4-5GB during normal operation
|
||||
|
||||
## Configuration File
|
||||
|
||||
Example `~/.brk/config.toml`:
|
||||
```toml
|
||||
bitcoindir = "/Users/username/.bitcoin"
|
||||
blocksdir = "/Users/username/.bitcoin/blocks"
|
||||
brkdir = "/Users/username/brk_data"
|
||||
fetch = true
|
||||
exchanges = true
|
||||
website = "default"
|
||||
rpcconnect = "localhost"
|
||||
rpcport = 8332
|
||||
rpccookiefile = "/Users/username/.bitcoin/.cookie"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
- **Bitcoin Core sync**: Waits for node sync before processing
|
||||
- **RPC connection**: Automatic retry logic for connection issues
|
||||
- **Processing errors**: Graceful error handling with detailed logging
|
||||
- **Graceful shutdown**: Ctrl+C handling with proper cleanup and state saving
|
||||
|
||||
## Logging
|
||||
|
||||
Logs are written to `~/.brk/brk.log` with colored console output:
|
||||
- Request/response logging with timing
|
||||
- Processing progress indicators
|
||||
- Error reporting and debugging information
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `brk_parser` - Bitcoin block parsing
|
||||
- `brk_indexer` - Blockchain data indexing
|
||||
- `brk_computer` - Analytics computation
|
||||
- `brk_interface` - Data query interface
|
||||
- `brk_server` - HTTP API server
|
||||
- `brk_logger` - Logging utilities
|
||||
- `bitcoincore_rpc` - Bitcoin Core RPC client
|
||||
- `color_eyre` - Enhanced error reporting
|
||||
|
||||
---
|
||||
|
||||
*This README was generated by Claude Code*
|
||||
|
||||
@@ -32,12 +32,17 @@ pub struct Config {
|
||||
#[arg(long, value_name = "PATH")]
|
||||
brkdir: Option<String>,
|
||||
|
||||
/// Activate fetching prices from exchanges APIs and the computation of all related datasets, default: true, saved
|
||||
/// Activate fetching prices from BRK's API and the computation of all price related datasets, default: true, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short = 'F', long, value_name = "BOOL")]
|
||||
fetch: Option<bool>,
|
||||
|
||||
/// Website served by the server (if active), default: default, saved
|
||||
/// Activate fetching prices from exchanges APIs if `fetch` is also set to `true`, default: true, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "BOOL")]
|
||||
exchanges: Option<bool>,
|
||||
|
||||
/// Website served by the server, default: default, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short, long)]
|
||||
website: Option<Website>,
|
||||
@@ -67,24 +72,9 @@ pub struct Config {
|
||||
#[arg(long, value_name = "PASSWORD")]
|
||||
rpcpassword: Option<String>,
|
||||
|
||||
/// Delay between runs, default: 0, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "SECONDS")]
|
||||
delay: Option<u64>,
|
||||
|
||||
/// Activate the Model Context Protocol (MCP) endpoint to give LLMs access to BRK (experimental), default: true, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "BOOL")]
|
||||
mcp: Option<bool>,
|
||||
|
||||
/// DEV: Activate watching the selected website's folder for changes, default: false, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "BOOL")]
|
||||
watch: Option<bool>,
|
||||
|
||||
/// DEV: Activate checking address hashes for collisions when indexing, default: false, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "BOOL")]
|
||||
#[arg(skip)]
|
||||
check_collisions: Option<bool>,
|
||||
}
|
||||
|
||||
@@ -117,6 +107,10 @@ impl Config {
|
||||
config_saved.fetch = Some(fetch);
|
||||
}
|
||||
|
||||
if let Some(exchanges) = config_args.exchanges.take() {
|
||||
config_saved.exchanges = Some(exchanges);
|
||||
}
|
||||
|
||||
if let Some(website) = config_args.website.take() {
|
||||
config_saved.website = Some(website);
|
||||
}
|
||||
@@ -141,22 +135,10 @@ impl Config {
|
||||
config_saved.rpcpassword = Some(rpcpassword);
|
||||
}
|
||||
|
||||
if let Some(delay) = config_args.delay.take() {
|
||||
config_saved.delay = Some(delay);
|
||||
}
|
||||
|
||||
if let Some(check_collisions) = config_args.check_collisions.take() {
|
||||
config_saved.check_collisions = Some(check_collisions);
|
||||
}
|
||||
|
||||
if let Some(mcp) = config_args.mcp.take() {
|
||||
config_saved.mcp = Some(mcp);
|
||||
}
|
||||
|
||||
if let Some(watch) = config_args.watch.take() {
|
||||
config_saved.watch = Some(watch);
|
||||
}
|
||||
|
||||
if config_args != Config::default() {
|
||||
dbg!(config_args);
|
||||
panic!("Didn't consume the full config")
|
||||
@@ -196,7 +178,9 @@ impl Config {
|
||||
|
||||
if self.rpc_auth().is_err() {
|
||||
println!(
|
||||
"No way found to authenticate the RPC client, please either set --rpccookiefile or --rpcuser and --rpcpassword.\nRun the program with '-h' for help."
|
||||
"Unsuccessful authentication with the RPC client.
|
||||
First make sure that `bitcoind` is running. If it is then please either set --rpccookiefile or --rpcuser and --rpcpassword as the default values seemed to have failed.
|
||||
Finally, you can run the program with '-h' for help."
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
@@ -247,10 +231,6 @@ impl Config {
|
||||
self.rpcport
|
||||
}
|
||||
|
||||
pub fn delay(&self) -> Option<u64> {
|
||||
self.delay
|
||||
}
|
||||
|
||||
pub fn bitcoindir(&self) -> PathBuf {
|
||||
self.bitcoindir
|
||||
.as_ref()
|
||||
@@ -311,22 +291,18 @@ impl Config {
|
||||
self.fetch.is_none_or(|b| b)
|
||||
}
|
||||
|
||||
pub fn exchanges(&self) -> bool {
|
||||
self.exchanges.is_none_or(|b| b)
|
||||
}
|
||||
|
||||
pub fn fetcher(&self) -> Option<Fetcher> {
|
||||
self.fetch()
|
||||
.then(|| Fetcher::import(Some(self.harsdir().as_path())).unwrap())
|
||||
.then(|| Fetcher::import(self.exchanges(), Some(self.harsdir().as_path())).unwrap())
|
||||
}
|
||||
|
||||
pub fn check_collisions(&self) -> bool {
|
||||
self.check_collisions.is_some_and(|b| b)
|
||||
}
|
||||
|
||||
pub fn mcp(&self) -> bool {
|
||||
self.mcp.is_none_or(|b| b)
|
||||
}
|
||||
|
||||
pub fn watch(&self) -> bool {
|
||||
self.watch.is_some_and(|b| b)
|
||||
}
|
||||
}
|
||||
|
||||
fn default_on_error<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
||||
|
||||
@@ -14,8 +14,8 @@ use brk_computer::Computer;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_interface::Interface;
|
||||
use brk_server::{Server, VERSION};
|
||||
use brk_vecs::Exit;
|
||||
use log::info;
|
||||
use vecdb::Exit;
|
||||
|
||||
mod bridge;
|
||||
mod config;
|
||||
@@ -29,7 +29,7 @@ pub fn main() -> color_eyre::Result<()> {
|
||||
|
||||
fs::create_dir_all(dot_brk_path())?;
|
||||
|
||||
brk_logger::init(Some(&dot_brk_log_path()));
|
||||
brk_logger::init(Some(&dot_brk_log_path()))?;
|
||||
|
||||
thread::Builder::new()
|
||||
.stack_size(256 * 1024 * 1024)
|
||||
@@ -108,9 +108,7 @@ pub fn run() -> color_eyre::Result<()> {
|
||||
|
||||
interface.generate_bridge_file(website, websites_path.as_path())?;
|
||||
|
||||
let watch = config.watch();
|
||||
|
||||
Some(bundle(&websites_path, website.to_folder_name(), watch).await?)
|
||||
Some(bundle(&websites_path, website.to_folder_name(), true).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -120,10 +118,8 @@ pub fn run() -> color_eyre::Result<()> {
|
||||
bundle_path,
|
||||
);
|
||||
|
||||
let mcp = config.mcp();
|
||||
|
||||
tokio::spawn(async move {
|
||||
server.serve(mcp).await.unwrap();
|
||||
server.serve(true).await.unwrap();
|
||||
});
|
||||
|
||||
sleep(Duration::from_secs(1));
|
||||
@@ -136,13 +132,9 @@ pub fn run() -> color_eyre::Result<()> {
|
||||
info!("{} blocks found.", block_count + 1);
|
||||
|
||||
let starting_indexes =
|
||||
indexer.index(&parser, rpc, &exit, config.check_collisions())?;
|
||||
indexer.index(&parser, rpc, &exit, config.check_collisions()).unwrap();
|
||||
|
||||
computer.compute(&indexer, starting_indexes, &exit)?;
|
||||
|
||||
if let Some(delay) = config.delay() {
|
||||
sleep(Duration::from_secs(delay))
|
||||
}
|
||||
computer.compute(&indexer, starting_indexes, &exit).unwrap();
|
||||
|
||||
info!("Waiting for new blocks...");
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
@@ -17,9 +18,10 @@ brk_fetcher = { workspace = true }
|
||||
brk_indexer = { workspace = true }
|
||||
brk_logger = { workspace = true }
|
||||
brk_parser = { workspace = true }
|
||||
brk_vecs = { workspace = true }
|
||||
vecdb = { workspace = true }
|
||||
derive_deref = { workspace = true }
|
||||
log = { workspace = true }
|
||||
pco = "0.4.6"
|
||||
rayon = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
zerocopy = { workspace = true }
|
||||
|
||||
+198
-26
@@ -1,28 +1,200 @@
|
||||
# BRK Computer
|
||||
# brk_computer
|
||||
|
||||
<p align="left">
|
||||
<a href="https://github.com/bitcoinresearchkit/brk">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/bitcoinresearchkit/brk?style=social">
|
||||
</a>
|
||||
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
|
||||
<img src="https://img.shields.io/crates/l/brk" alt="License" />
|
||||
</a>
|
||||
<a href="https://crates.io/crates/brk_computer">
|
||||
<img src="https://img.shields.io/crates/v/brk_computer" alt="Version" />
|
||||
</a>
|
||||
<a href="https://docs.rs/brk_computer">
|
||||
<img src="https://img.shields.io/docsrs/brk_computer" alt="Documentation" />
|
||||
</a>
|
||||
<img src="https://img.shields.io/crates/size/brk_computer" alt="Size" />
|
||||
<a href="https://deps.rs/crate/brk_computer">
|
||||
<img src="https://deps.rs/crate/brk_computer/latest/status.svg" alt="Dependency status">
|
||||
</a>
|
||||
<a href="https://discord.gg/HaR3wpH3nr">
|
||||
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
|
||||
</a>
|
||||
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
|
||||
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
|
||||
</a>
|
||||
</p>
|
||||
**Bitcoin analytics engine that transforms indexed blockchain data into comprehensive metrics**
|
||||
|
||||
A dataset computer, built on top of `brk_indexer` and `brk_fetcher`. It computes any dataset you can think of and if it doesn't feel free to create an issue.
|
||||
`brk_computer` is the computational layer of BRK that processes indexed blockchain data to generate analytics across multiple specialized domains. It provides comprehensive Bitcoin metrics with efficient storage and lazy computation for optimal performance.
|
||||
|
||||
## What it provides
|
||||
|
||||
- **Comprehensive Analytics**: 9 specialized domains covering all aspects of Bitcoin analysis
|
||||
- **Lazy Computation**: On-demand calculation with dependency tracking and caching
|
||||
- **Incremental Updates**: Only processes new data since last computation
|
||||
- **Memory Efficiency**: ~100MB operation footprint via compressed storage and memory mapping
|
||||
- **Multi-timeframe Analysis**: Daily, weekly, monthly, quarterly, yearly perspectives
|
||||
|
||||
## Nine Analytics Domains
|
||||
|
||||
The computer processes data through a fixed dependency chain:
|
||||
|
||||
1. **indexes** - Time-based indexing (date/height mappings, epoch calculations)
|
||||
2. **constants** - Baseline values and reference metrics
|
||||
3. **blocks** - Block analytics (sizes, intervals, transaction counts, weight)
|
||||
4. **mining** - Mining economics (hashrate, difficulty, rewards, epochs)
|
||||
5. **fetched** - External price data integration (optional)
|
||||
6. **price** - OHLC data across multiple timeframes (optional, requires fetched)
|
||||
7. **transactions** - Transaction analysis (fees, sizes, patterns, RBF detection)
|
||||
8. **market** - Price correlations and market metrics (optional, requires price)
|
||||
9. **stateful** - UTXO tracking and accumulated state computations
|
||||
10. **cointime** - Coin age and time-based value analysis
|
||||
|
||||
## Key Features
|
||||
|
||||
### Computation Strategy
|
||||
- **Fixed dependency chain**: Ensures data consistency across all domains
|
||||
- **Parallel processing**: Uses Rayon for performance optimization
|
||||
- **State management**: Rollback capabilities for error recovery
|
||||
- **Incremental updates**: Only computes new data since last run
|
||||
|
||||
### Analytics Capabilities
|
||||
- **Multi-timeframe analysis**: Daily, weekly, monthly, quarterly, yearly aggregations
|
||||
- **Chain-based metrics**: Height, difficulty epoch, halving epoch indexing
|
||||
- **Price correlation**: Both dollar and satoshi denominated metrics
|
||||
- **DCA analysis**: Dollar Cost Averaging with configurable periods
|
||||
- **Supply analysis**: Circulating, realized, unrealized supply metrics
|
||||
- **Address cohort tracking**: Analysis across different Bitcoin address types
|
||||
- **UTXO cohort analysis**: Realized/unrealized gains tracking
|
||||
- **Coin time analysis**: Understanding Bitcoin velocity and dormancy
|
||||
|
||||
### Storage Optimization
|
||||
- **Compressed vectors**: Efficient disk storage with lazy computation
|
||||
- **Memory mapping**: Minimal RAM usage during operation
|
||||
- **Version management**: Automatic invalidation on schema changes
|
||||
- **Dependency tracking**: Smart recomputation based on data changes
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Setup (No Price Data)
|
||||
|
||||
```rust
|
||||
use brk_computer::Computer;
|
||||
use brk_indexer::Indexer;
|
||||
use vecdb::Exit;
|
||||
|
||||
// Setup without external price data
|
||||
let indexer = Indexer::forced_import("./brk_data")?;
|
||||
let mut computer = Computer::forced_import("./brk_data", &indexer, None)?;
|
||||
|
||||
// Setup exit handler
|
||||
let exit = Exit::new();
|
||||
exit.set_ctrlc_handler();
|
||||
|
||||
// Compute all analytics
|
||||
let starting_indexes = indexer.get_starting_indexes();
|
||||
computer.compute(&indexer, starting_indexes, &exit)?;
|
||||
```
|
||||
|
||||
### Advanced Setup (With Price Data)
|
||||
|
||||
```rust
|
||||
use brk_fetcher::Fetcher;
|
||||
|
||||
// Setup with external price data for market analytics
|
||||
let fetcher = Some(Fetcher::import(true, None)?);
|
||||
let mut computer = Computer::forced_import("./brk_data", &indexer, fetcher)?;
|
||||
|
||||
// Compute all analytics including price/market domains
|
||||
computer.compute(&indexer, starting_indexes, &exit)?;
|
||||
```
|
||||
|
||||
### Accessing Computed Data
|
||||
|
||||
```rust
|
||||
// Access all computed vectors
|
||||
let all_vecs = computer.vecs(); // Returns Vec<&dyn AnyCollectableVec>
|
||||
|
||||
// Access specific domain data
|
||||
let block_metrics = &computer.blocks;
|
||||
let mining_data = &computer.mining;
|
||||
let transaction_stats = &computer.transactions;
|
||||
|
||||
// Access price data (if available)
|
||||
if let Some(price_data) = &computer.price {
|
||||
// Use OHLC data
|
||||
}
|
||||
```
|
||||
|
||||
### Incremental Updates
|
||||
|
||||
```rust
|
||||
// Continuous computation loop
|
||||
loop {
|
||||
// Get latest indexes from indexer
|
||||
let current_indexes = indexer.get_current_indexes();
|
||||
|
||||
// Compute only new data
|
||||
computer.compute(&indexer, current_indexes, &exit)?;
|
||||
|
||||
// Check for exit signal
|
||||
if exit.is_signaled() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Wait before next update
|
||||
sleep(Duration::from_secs(60));
|
||||
}
|
||||
```
|
||||
|
||||
## Core Computer Structure
|
||||
|
||||
```rust
|
||||
pub struct Computer {
|
||||
pub indexes: indexes::Vecs, // Time indexing
|
||||
pub constants: constants::Vecs, // Baseline values
|
||||
pub blocks: blocks::Vecs, // Block analytics
|
||||
pub mining: mining::Vecs, // Mining economics
|
||||
pub market: market::Vecs, // Market metrics (optional)
|
||||
pub price: Option<price::Vecs>, // OHLC price data (optional)
|
||||
pub transactions: transactions::Vecs, // Transaction analysis
|
||||
pub stateful: stateful::Vecs, // UTXO tracking
|
||||
pub fetched: Option<fetched::Vecs>, // External data (optional)
|
||||
pub cointime: cointime::Vecs, // Coin age analysis
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
**Benchmarked on MacBook Pro M3 Pro:**
|
||||
- **Initial computation**: ~6-7 hours for complete Bitcoin blockchain
|
||||
- **Storage efficiency**: All computed datasets total ~40GB
|
||||
- **Incremental updates**: 3-5 seconds per new block
|
||||
- **Memory footprint**: Peak ~7-8GB during computation, ~100MB during operation
|
||||
- **Dependencies**: Price data domains optional (fetched, price, market)
|
||||
|
||||
## Domain-Specific Analytics
|
||||
|
||||
### Block Analytics
|
||||
- Block sizes, weights, transaction counts
|
||||
- Block intervals and mining statistics
|
||||
- Fee analysis per block
|
||||
|
||||
### Mining Economics
|
||||
- Hashrate estimation and difficulty tracking
|
||||
- Mining reward analysis
|
||||
- Epoch-based calculations
|
||||
|
||||
### Transaction Analysis
|
||||
- Fee rate distributions
|
||||
- RBF (Replace-By-Fee) detection
|
||||
- Output type analysis
|
||||
- Transaction size patterns
|
||||
|
||||
### Market Metrics (Optional)
|
||||
- Price correlations with on-chain metrics
|
||||
- Market cap calculations
|
||||
- DCA analysis across timeframes
|
||||
|
||||
### Stateful Analysis
|
||||
- UTXO set tracking
|
||||
- Address cohort analysis
|
||||
- Realized/unrealized gains
|
||||
- Supply distribution metrics
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Indexed data**: Requires completed `brk_indexer` output
|
||||
- **Storage space**: Additional ~40GB for computed datasets
|
||||
- **Memory**: 8GB+ RAM recommended for initial computation
|
||||
- **CPU**: Multi-core recommended for parallel processing
|
||||
- **Price data**: Optional external price feeds for market analytics
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `brk_indexer` - Source of indexed blockchain data
|
||||
- `brk_fetcher` - External price data (optional)
|
||||
- `vecdb` - Vector database with lazy computation
|
||||
- `rayon` - Parallel processing framework
|
||||
- `brk_structs` - Bitcoin-aware type system
|
||||
|
||||
---
|
||||
|
||||
*This README was generated by Claude Code*
|
||||
@@ -9,10 +9,10 @@ use brk_error::Result;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_parser::Parser;
|
||||
use brk_vecs::Exit;
|
||||
use vecdb::Exit;
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
brk_logger::init(Some(Path::new(".log")));
|
||||
brk_logger::init(Some(Path::new(".log")))?;
|
||||
|
||||
let bitcoin_dir = Path::new(&std::env::var("HOME").unwrap())
|
||||
.join("Library")
|
||||
@@ -38,7 +38,7 @@ pub fn main() -> Result<()> {
|
||||
|
||||
let mut indexer = Indexer::forced_import(&outputs_dir)?;
|
||||
|
||||
let fetcher = Fetcher::import(None)?;
|
||||
let fetcher = Fetcher::import(true, None)?;
|
||||
|
||||
let mut computer = Computer::forced_import(&outputs_dir, &indexer, Some(fetcher))?;
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_computer::PriceToAmount;
|
||||
use brk_error::Result;
|
||||
use brk_structs::Height;
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
let path = Path::new(&std::env::var("HOME").unwrap())
|
||||
.join(".brk")
|
||||
.join("computed/stateful/states");
|
||||
let mut price_to_amount = PriceToAmount::create(&path, "addrs_above_1btc_under_10btc");
|
||||
dbg!(price_to_amount.import_at_or_before(Height::new(890000))?);
|
||||
dbg!(price_to_amount);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{path::Path, sync::Arc};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
@@ -6,9 +6,7 @@ use brk_structs::{
|
||||
CheckedSub, DifficultyEpoch, HalvingEpoch, Height, StoredU32, StoredU64, Timestamp, Version,
|
||||
Weight,
|
||||
};
|
||||
use brk_vecs::{
|
||||
AnyCollectableVec, Computation, EagerVec, Exit, File, Format, PAGE_SIZE, VecIterator,
|
||||
};
|
||||
use vecdb::{AnyCollectableVec, Database, EagerVec, Exit, PAGE_SIZE, VecIterator};
|
||||
|
||||
use crate::grouped::Source;
|
||||
|
||||
@@ -22,7 +20,7 @@ const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
file: Arc<File>,
|
||||
db: Database,
|
||||
|
||||
pub height_to_interval: EagerVec<Height, Timestamp>,
|
||||
pub height_to_vbytes: EagerVec<Height, StoredU64>,
|
||||
@@ -37,40 +35,29 @@ pub struct Vecs {
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
parent: &Path,
|
||||
version: Version,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let file = Arc::new(File::open(&parent.join("blocks"))?);
|
||||
file.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
pub fn forced_import(parent: &Path, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
let db = Database::open(&parent.join("blocks"))?;
|
||||
db.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
|
||||
Ok(Self {
|
||||
height_to_interval: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_interval: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"interval",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
timeindexes_to_timestamp: ComputedVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"timestamp",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_first(),
|
||||
)?,
|
||||
indexes_to_block_interval: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"block_interval",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_percentiles()
|
||||
@@ -78,65 +65,54 @@ impl Vecs {
|
||||
.add_average(),
|
||||
)?,
|
||||
indexes_to_block_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"block_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_block_weight: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"block_weight",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_block_size: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"block_size",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
height_to_vbytes: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_vbytes: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"vbytes",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
indexes_to_block_vbytes: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"block_vbytes",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
difficultyepoch_to_timestamp: EagerVec::forced_import(
|
||||
&file,
|
||||
difficultyepoch_to_timestamp: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"timestamp",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
halvingepoch_to_timestamp: EagerVec::forced_import(
|
||||
&file,
|
||||
halvingepoch_to_timestamp: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"timestamp",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
|
||||
file,
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -148,7 +124,7 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_(indexer, indexes, starting_indexes, exit)?;
|
||||
self.file.flush_then_punch()?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::{path::Path, sync::Arc};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, CheckedSub, Dollars, StoredF64, Version};
|
||||
use brk_vecs::{AnyCollectableVec, Computation, Exit, File, Format, PAGE_SIZE, VecIterator};
|
||||
use vecdb::{AnyCollectableVec, Database, Exit, PAGE_SIZE, VecIterator};
|
||||
|
||||
use super::{
|
||||
Indexes,
|
||||
@@ -18,7 +18,7 @@ const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
file: Arc<File>,
|
||||
db: Database,
|
||||
|
||||
pub indexes_to_coinblocks_created: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_coinblocks_stored: ComputedVecsFromHeight<StoredF64>,
|
||||
@@ -50,251 +50,203 @@ impl Vecs {
|
||||
pub fn forced_import(
|
||||
parent: &Path,
|
||||
version: Version,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
) -> Result<Self> {
|
||||
let file = Arc::new(File::open(&parent.join("cointime"))?);
|
||||
file.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
let db = Database::open(&parent.join("cointime"))?;
|
||||
db.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
|
||||
let compute_dollars = price.is_some();
|
||||
|
||||
Ok(Self {
|
||||
indexes_to_coinblocks_created: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"coinblocks_created",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_coinblocks_stored: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"coinblocks_stored",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_liveliness: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"liveliness",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaultedness: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"vaultedness",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_activity_to_vaultedness_ratio: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"activity_to_vaultedness_ratio",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_supply: ComputedValueVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"vaulted_supply",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
compute_dollars,
|
||||
indexes,
|
||||
)?,
|
||||
indexes_to_active_supply: ComputedValueVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"active_supply",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
compute_dollars,
|
||||
indexes,
|
||||
)?,
|
||||
indexes_to_thermo_cap: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"thermo_cap",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_investor_cap: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"investor_cap",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_cap: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"vaulted_cap",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_active_cap: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"active_cap",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_price: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"vaulted_price",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"vaulted_price",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
true,
|
||||
)?,
|
||||
indexes_to_active_price: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"active_price",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_active_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"active_price",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
true,
|
||||
)?,
|
||||
indexes_to_true_market_mean: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"true_market_mean",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_true_market_mean_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"true_market_mean",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
true,
|
||||
)?,
|
||||
indexes_to_cointime_value_destroyed: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"cointime_value_destroyed",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_cointime_value_created: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"cointime_value_created",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_cointime_value_stored: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"cointime_value_stored",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_cointime_price: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"cointime_price",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_cointime_cap: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"cointime_cap",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_cointime_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"cointime_price",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
true,
|
||||
)?,
|
||||
|
||||
file,
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -318,7 +270,7 @@ impl Vecs {
|
||||
stateful,
|
||||
exit,
|
||||
)?;
|
||||
self.file.flush_then_punch()?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::{path::Path, sync::Arc};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{StoredU16, Version};
|
||||
use brk_vecs::{AnyCollectableVec, AnyVec, Computation, Exit, File, Format};
|
||||
use brk_structs::{StoredI16, StoredU16, Version};
|
||||
use vecdb::{AnyCollectableVec, AnyVec, Database, Exit};
|
||||
|
||||
use crate::grouped::Source;
|
||||
|
||||
@@ -17,67 +17,116 @@ const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
file: Arc<File>,
|
||||
db: Database,
|
||||
|
||||
pub constant_0: ComputedVecsFromHeight<StoredU16>,
|
||||
pub constant_1: ComputedVecsFromHeight<StoredU16>,
|
||||
pub constant_2: ComputedVecsFromHeight<StoredU16>,
|
||||
pub constant_3: ComputedVecsFromHeight<StoredU16>,
|
||||
pub constant_4: ComputedVecsFromHeight<StoredU16>,
|
||||
pub constant_50: ComputedVecsFromHeight<StoredU16>,
|
||||
pub constant_100: ComputedVecsFromHeight<StoredU16>,
|
||||
pub constant_minus_1: ComputedVecsFromHeight<StoredI16>,
|
||||
pub constant_minus_2: ComputedVecsFromHeight<StoredI16>,
|
||||
pub constant_minus_3: ComputedVecsFromHeight<StoredI16>,
|
||||
pub constant_minus_4: ComputedVecsFromHeight<StoredI16>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
parent: &Path,
|
||||
version: Version,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let file = Arc::new(File::open(&parent.join("constants"))?);
|
||||
pub fn forced_import(parent: &Path, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
let db = Database::open(&parent.join("constants"))?;
|
||||
|
||||
Ok(Self {
|
||||
constant_0: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"constant_0",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_1: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"constant_1",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_2: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"constant_2",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_3: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"constant_3",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_4: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"constant_4",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_50: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"constant_50",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_100: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"constant_100",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_minus_1: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"constant_minus_1",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_minus_2: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"constant_minus_2",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_minus_3: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"constant_minus_3",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
constant_minus_4: ComputedVecsFromHeight::forced_import(
|
||||
&db,
|
||||
"constant_minus_4",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
|
||||
file,
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -89,7 +138,7 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_(indexer, indexes, starting_indexes, exit)?;
|
||||
self.file.flush_then_punch()?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -134,6 +183,57 @@ impl Vecs {
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_2.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredU16::new(2)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_3.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredU16::new(3)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_4.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredU16::new(4)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_50.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
@@ -168,6 +268,74 @@ impl Vecs {
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_minus_1.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredI16::new(-1)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_minus_2.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredI16::new(-2)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_minus_3.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredI16::new(-3)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_minus_4.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_to(
|
||||
starting_indexes.height,
|
||||
indexes.height_to_date.len(),
|
||||
indexes.height_to_date.version(),
|
||||
|i| (i, StoredI16::new(-4)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -175,8 +343,15 @@ impl Vecs {
|
||||
[
|
||||
self.constant_0.vecs(),
|
||||
self.constant_1.vecs(),
|
||||
self.constant_2.vecs(),
|
||||
self.constant_3.vecs(),
|
||||
self.constant_4.vecs(),
|
||||
self.constant_50.vecs(),
|
||||
self.constant_100.vecs(),
|
||||
self.constant_minus_1.vecs(),
|
||||
self.constant_minus_2.vecs(),
|
||||
self.constant_minus_3.vecs(),
|
||||
self.constant_minus_4.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
use std::{path::Path, sync::Arc};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{DateIndex, Height, OHLCCents, Version};
|
||||
use brk_vecs::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, Exit, File, GenericStoredVec, RawVec,
|
||||
StoredIndex, VecIterator,
|
||||
use vecdb::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, Database, Exit, GenericStoredVec,
|
||||
RawVec, StoredIndex, VecIterator,
|
||||
};
|
||||
|
||||
use super::{Indexes, indexes};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
file: Arc<File>,
|
||||
db: Database,
|
||||
fetcher: Fetcher,
|
||||
|
||||
pub dateindex_to_ohlc_in_cents: RawVec<DateIndex, OHLCCents>,
|
||||
@@ -22,23 +22,23 @@ pub struct Vecs {
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(parent: &Path, fetcher: Fetcher, version: Version) -> Result<Self> {
|
||||
let file = Arc::new(File::open(&parent.join("fetched"))?);
|
||||
let db = Database::open(&parent.join("fetched"))?;
|
||||
|
||||
Ok(Self {
|
||||
fetcher,
|
||||
|
||||
dateindex_to_ohlc_in_cents: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc_in_cents",
|
||||
version + Version::ZERO,
|
||||
)?,
|
||||
height_to_ohlc_in_cents: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc_in_cents",
|
||||
version + Version::ZERO,
|
||||
)?,
|
||||
|
||||
file,
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_(indexer, indexes, starting_indexes, exit)?;
|
||||
self.file.flush_then_punch()?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_structs::Version;
|
||||
use brk_vecs::{
|
||||
use vecdb::{
|
||||
AnyBoxedIterableVec, AnyCloneableIterableVec, AnyCollectableVec, AnyIterableVec, Computation,
|
||||
ComputedVec, ComputedVecFrom2, Exit, File, Format, FromCoarserIndex, StoredIndex,
|
||||
ComputedVec, ComputedVecFrom2, Database, Exit, Format, FromCoarserIndex, StoredIndex,
|
||||
};
|
||||
|
||||
use crate::grouped::{EagerVecBuilder, VecBuilderOptions};
|
||||
@@ -40,7 +38,7 @@ where
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
format: Format,
|
||||
@@ -67,7 +65,7 @@ where
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("first"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -92,7 +90,7 @@ where
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
file,
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -101,7 +99,7 @@ where
|
||||
source
|
||||
.as_ref()
|
||||
.unwrap_or_else(|| {
|
||||
dbg!(file, name, I::to_string());
|
||||
dbg!(db, name, I::to_string());
|
||||
panic!()
|
||||
})
|
||||
.clone()
|
||||
@@ -125,7 +123,7 @@ where
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("min"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -150,7 +148,7 @@ where
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("max"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -175,7 +173,7 @@ where
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("average"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -207,7 +205,7 @@ where
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
file,
|
||||
db,
|
||||
&(if !options.last && !options.average && !options.min && !options.max {
|
||||
name.to_string()
|
||||
} else {
|
||||
@@ -242,7 +240,7 @@ where
|
||||
Box::new(
|
||||
ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
file,
|
||||
db,
|
||||
&suffix("cumulative"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_error::{Error, Result};
|
||||
use brk_structs::{CheckedSub, StoredU64, Version};
|
||||
use brk_vecs::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, EagerVec, Exit, File, Format,
|
||||
use vecdb::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, Database, EagerVec, Exit, Format,
|
||||
GenericStoredVec, StoredIndex, StoredRaw,
|
||||
};
|
||||
|
||||
@@ -38,8 +36,17 @@ where
|
||||
I: StoredIndex,
|
||||
T: ComputedType,
|
||||
{
|
||||
pub fn forced_import_compressed(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
Self::forced_import(db, name, version, Format::Compressed, options)
|
||||
}
|
||||
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
format: Format,
|
||||
@@ -61,7 +68,7 @@ where
|
||||
first: options.first.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("first"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -71,13 +78,13 @@ where
|
||||
}),
|
||||
last: options.last.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(file, name, version + Version::ZERO, format).unwrap(),
|
||||
EagerVec::forced_import(db, name, version + Version::ZERO, format).unwrap(),
|
||||
)
|
||||
}),
|
||||
min: options.min.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("min"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -88,7 +95,7 @@ where
|
||||
max: options.max.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("max"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -99,7 +106,7 @@ where
|
||||
median: options.median.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("median"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -110,7 +117,7 @@ where
|
||||
average: options.average.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("average"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -121,7 +128,7 @@ where
|
||||
sum: options.sum.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&(if !options.last && !options.average && !options.min && !options.max {
|
||||
name.to_string()
|
||||
} else {
|
||||
@@ -136,7 +143,7 @@ where
|
||||
cumulative: options.cumulative.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&suffix("cumulative"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -147,7 +154,7 @@ where
|
||||
_90p: options._90p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("90p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -158,7 +165,7 @@ where
|
||||
_75p: options._75p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("75p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -169,7 +176,7 @@ where
|
||||
_25p: options._25p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("25p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -180,7 +187,7 @@ where
|
||||
_10p: options._10p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&maybe_suffix("10p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
@@ -203,7 +210,7 @@ where
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
self.validate_computed_version_or_reset_file(source.version())?;
|
||||
self.validate_computed_version_or_reset(source.version())?;
|
||||
|
||||
let index = self.starting_index(max_from);
|
||||
|
||||
@@ -234,7 +241,7 @@ where
|
||||
where
|
||||
I2: StoredIndex + StoredRaw + CheckedSub<I2>,
|
||||
{
|
||||
self.validate_computed_version_or_reset_file(
|
||||
self.validate_computed_version_or_reset(
|
||||
source.version() + first_indexes.version() + count_indexes.version(),
|
||||
)?;
|
||||
|
||||
@@ -253,10 +260,10 @@ where
|
||||
|
||||
first_indexes
|
||||
.iter_at(index)
|
||||
.try_for_each(|(i, first_index)| -> Result<()> {
|
||||
.try_for_each(|(index, first_index)| -> Result<()> {
|
||||
let first_index = first_index.into_owned();
|
||||
|
||||
let count_index = count_indexes_iter.unwrap_get_inner(i);
|
||||
let count_index = count_indexes_iter.unwrap_get_inner(index);
|
||||
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
let f = source_iter
|
||||
@@ -305,7 +312,7 @@ where
|
||||
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
max.forced_push_at(
|
||||
i,
|
||||
index,
|
||||
*values
|
||||
.last()
|
||||
.ok_or(Error::Str("expect some"))
|
||||
@@ -327,27 +334,27 @@ where
|
||||
}
|
||||
|
||||
if let Some(_90p) = self._90p.as_mut() {
|
||||
_90p.forced_push_at(i, get_percentile(&values, 0.90), exit)?;
|
||||
_90p.forced_push_at(index, get_percentile(&values, 0.90), exit)?;
|
||||
}
|
||||
|
||||
if let Some(_75p) = self._75p.as_mut() {
|
||||
_75p.forced_push_at(i, get_percentile(&values, 0.75), exit)?;
|
||||
_75p.forced_push_at(index, get_percentile(&values, 0.75), exit)?;
|
||||
}
|
||||
|
||||
if let Some(median) = self.median.as_mut() {
|
||||
median.forced_push_at(i, get_percentile(&values, 0.50), exit)?;
|
||||
median.forced_push_at(index, get_percentile(&values, 0.50), exit)?;
|
||||
}
|
||||
|
||||
if let Some(_25p) = self._25p.as_mut() {
|
||||
_25p.forced_push_at(i, get_percentile(&values, 0.25), exit)?;
|
||||
_25p.forced_push_at(index, get_percentile(&values, 0.25), exit)?;
|
||||
}
|
||||
|
||||
if let Some(_10p) = self._10p.as_mut() {
|
||||
_10p.forced_push_at(i, get_percentile(&values, 0.10), exit)?;
|
||||
_10p.forced_push_at(index, get_percentile(&values, 0.10), exit)?;
|
||||
}
|
||||
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
min.forced_push_at(i, *values.first().unwrap(), exit)?;
|
||||
min.forced_push_at(index, *values.first().unwrap(), exit)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,18 +364,18 @@ where
|
||||
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
let avg = sum / len;
|
||||
average.forced_push_at(i, avg, exit)?;
|
||||
average.forced_push_at(index, avg, exit)?;
|
||||
}
|
||||
|
||||
if needs_sum_or_cumulative {
|
||||
if let Some(sum_vec) = self.sum.as_mut() {
|
||||
sum_vec.forced_push_at(i, sum, exit)?;
|
||||
sum_vec.forced_push_at(index, sum, exit)?;
|
||||
}
|
||||
|
||||
if let Some(cumulative_vec) = self.cumulative.as_mut() {
|
||||
let t = cumulative.unwrap() + sum;
|
||||
cumulative.replace(t);
|
||||
cumulative_vec.forced_push_at(i, t, exit)?;
|
||||
cumulative_vec.forced_push_at(index, t, exit)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -403,7 +410,7 @@ where
|
||||
panic!("unsupported");
|
||||
}
|
||||
|
||||
self.validate_computed_version_or_reset_file(
|
||||
self.validate_computed_version_or_reset(
|
||||
VERSION + first_indexes.version() + count_indexes.version(),
|
||||
)?;
|
||||
|
||||
@@ -426,10 +433,10 @@ where
|
||||
|
||||
first_indexes
|
||||
.iter_at(index)
|
||||
.try_for_each(|(i, first_index, ..)| -> Result<()> {
|
||||
.try_for_each(|(index, first_index, ..)| -> Result<()> {
|
||||
let first_index = first_index.into_owned();
|
||||
|
||||
let count_index = count_indexes_iter.unwrap_get_inner(i);
|
||||
let count_index = count_indexes_iter.unwrap_get_inner(index);
|
||||
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
let v = source_first_iter
|
||||
@@ -468,7 +475,7 @@ where
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
values.sort_unstable();
|
||||
max.forced_push_at(i, *values.last().unwrap(), exit)?;
|
||||
max.forced_push_at(index, *values.last().unwrap(), exit)?;
|
||||
}
|
||||
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
@@ -479,7 +486,7 @@ where
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
values.sort_unstable();
|
||||
min.forced_push_at(i, *values.first().unwrap(), exit)?;
|
||||
min.forced_push_at(index, *values.first().unwrap(), exit)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,7 +504,7 @@ where
|
||||
// TODO: Multiply by count then divide by cumulative
|
||||
// Right now it's not 100% accurate as there could be more or less elements in the lower timeframe (28 days vs 31 days in a month for example)
|
||||
let avg = cumulative / len;
|
||||
average.forced_push_at(i, avg, exit)?;
|
||||
average.forced_push_at(index, avg, exit)?;
|
||||
}
|
||||
|
||||
if needs_sum_or_cumulative {
|
||||
@@ -511,13 +518,13 @@ where
|
||||
let sum = values.into_iter().fold(T::from(0), |a, b| a + b);
|
||||
|
||||
if let Some(sum_vec) = self.sum.as_mut() {
|
||||
sum_vec.forced_push_at(i, sum, exit)?;
|
||||
sum_vec.forced_push_at(index, sum, exit)?;
|
||||
}
|
||||
|
||||
if let Some(cumulative_vec) = self.cumulative.as_mut() {
|
||||
let t = cumulative.unwrap() + sum;
|
||||
cumulative.replace(t);
|
||||
cumulative_vec.forced_push_at(i, t, exit)?;
|
||||
cumulative_vec.forced_push_at(index, t, exit)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -665,42 +672,42 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_computed_version_or_reset_file(&mut self, version: Version) -> Result<()> {
|
||||
pub fn validate_computed_version_or_reset(&mut self, version: Version) -> Result<()> {
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
first.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
first.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
last.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
last.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
min.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
min.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
max.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
max.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(median) = self.median.as_mut() {
|
||||
median.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
median.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
average.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
average.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(sum) = self.sum.as_mut() {
|
||||
sum.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
sum.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(cumulative) = self.cumulative.as_mut() {
|
||||
cumulative.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
cumulative.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_90p) = self._90p.as_mut() {
|
||||
_90p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
_90p.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_75p) = self._75p.as_mut() {
|
||||
_75p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
_75p.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_25p) = self._25p.as_mut() {
|
||||
_25p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
_25p.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_10p) = self._10p.as_mut() {
|
||||
_10p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
_10p.validate_computed_version_or_reset(Version::ZERO + version)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -0,0 +1,395 @@
|
||||
use brk_structs::Version;
|
||||
use vecdb::{
|
||||
AnyBoxedIterableVec, AnyCloneableIterableVec, AnyCollectableVec, FromCoarserIndex,
|
||||
LazyVecFrom2, StoredIndex,
|
||||
};
|
||||
|
||||
use crate::grouped::{EagerVecBuilder, VecBuilderOptions};
|
||||
|
||||
use super::ComputedType;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[derive(Clone)]
|
||||
pub struct LazyVecBuilder<I, T, S1I, S2T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: ComputedType,
|
||||
S2T: ComputedType,
|
||||
{
|
||||
pub first: Option<Box<LazyVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub average: Option<Box<LazyVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub sum: Option<Box<LazyVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub max: Option<Box<LazyVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub min: Option<Box<LazyVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub last: Option<Box<LazyVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
pub cumulative: Option<Box<LazyVecFrom2<I, T, S1I, T, I, S2T>>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<I, T, S1I, S2T> LazyVecBuilder<I, T, S1I, S2T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: ComputedType + 'static,
|
||||
S1I: StoredIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: ComputedType,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: Option<AnyBoxedIterableVec<S1I, T>>,
|
||||
source_extra: &EagerVecBuilder<S1I, T>,
|
||||
len_source: AnyBoxedIterableVec<I, S2T>,
|
||||
options: LazyVecBuilderOptions,
|
||||
) -> Self {
|
||||
let only_one_active = options.is_only_one_active();
|
||||
|
||||
let suffix = |s: &str| format!("{name}_{s}");
|
||||
|
||||
let maybe_suffix = |s: &str| {
|
||||
if only_one_active {
|
||||
name.to_string()
|
||||
} else {
|
||||
suffix(s)
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
first: options.first.then(|| {
|
||||
Box::new(LazyVecFrom2::init(
|
||||
&maybe_suffix("first"),
|
||||
version + VERSION + Version::ZERO,
|
||||
source_extra
|
||||
.first
|
||||
.as_ref()
|
||||
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
source
|
||||
.next_at(S1I::min_from(i))
|
||||
.map(|(_, cow)| cow.into_owned())
|
||||
},
|
||||
))
|
||||
}),
|
||||
last: options.last.then(|| {
|
||||
Box::new(LazyVecFrom2::init(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
source_extra.last.as_ref().map_or_else(
|
||||
|| {
|
||||
source
|
||||
.as_ref()
|
||||
.unwrap_or_else(|| {
|
||||
dbg!(name, I::to_string());
|
||||
panic!()
|
||||
})
|
||||
.clone()
|
||||
},
|
||||
|v| v.clone(),
|
||||
),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
source
|
||||
.next_at(S1I::max_from(i, source.len()))
|
||||
.map(|(_, cow)| cow.into_owned())
|
||||
},
|
||||
))
|
||||
}),
|
||||
min: options.min.then(|| {
|
||||
Box::new(LazyVecFrom2::init(
|
||||
&maybe_suffix("min"),
|
||||
version + VERSION + Version::ZERO,
|
||||
source_extra
|
||||
.min
|
||||
.as_ref()
|
||||
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
S1I::inclusive_range_from(i, source.len())
|
||||
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
|
||||
.min()
|
||||
},
|
||||
))
|
||||
}),
|
||||
max: options.max.then(|| {
|
||||
Box::new(LazyVecFrom2::init(
|
||||
&maybe_suffix("max"),
|
||||
version + VERSION + Version::ZERO,
|
||||
source_extra
|
||||
.max
|
||||
.as_ref()
|
||||
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
S1I::inclusive_range_from(i, source.len())
|
||||
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
|
||||
.max()
|
||||
},
|
||||
))
|
||||
}),
|
||||
average: options.average.then(|| {
|
||||
Box::new(LazyVecFrom2::init(
|
||||
&maybe_suffix("average"),
|
||||
version + VERSION + Version::ZERO,
|
||||
source_extra
|
||||
.average
|
||||
.as_ref()
|
||||
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
let vec = S1I::inclusive_range_from(i, source.len())
|
||||
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
|
||||
.collect::<Vec<_>>();
|
||||
if vec.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let mut sum = T::from(0);
|
||||
let len = vec.len();
|
||||
vec.into_iter().for_each(|v| sum += v);
|
||||
Some(sum / len)
|
||||
},
|
||||
))
|
||||
}),
|
||||
sum: options.sum.then(|| {
|
||||
Box::new(LazyVecFrom2::init(
|
||||
&(if !options.last && !options.average && !options.min && !options.max {
|
||||
name.to_string()
|
||||
} else {
|
||||
maybe_suffix("sum")
|
||||
}),
|
||||
version + VERSION + Version::ZERO,
|
||||
source_extra
|
||||
.sum
|
||||
.as_ref()
|
||||
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
let vec = S1I::inclusive_range_from(i, source.len())
|
||||
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
|
||||
.collect::<Vec<_>>();
|
||||
if vec.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let mut sum = T::from(0);
|
||||
vec.into_iter().for_each(|v| sum += v);
|
||||
Some(sum)
|
||||
},
|
||||
))
|
||||
}),
|
||||
cumulative: options.cumulative.then(|| {
|
||||
Box::new(LazyVecFrom2::init(
|
||||
&suffix("cumulative"),
|
||||
version + VERSION + Version::ZERO,
|
||||
source_extra.cumulative.as_ref().unwrap().boxed_clone(),
|
||||
len_source.clone(),
|
||||
|i: I, source, len_source| {
|
||||
if i.unwrap_to_usize() >= len_source.len() {
|
||||
return None;
|
||||
}
|
||||
source
|
||||
.next_at(S1I::max_from(i, source.len()))
|
||||
.map(|(_, cow)| cow.into_owned())
|
||||
},
|
||||
))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn starting_index(&self, max_from: I) -> I {
|
||||
max_from.min(I::from(
|
||||
self.vecs().into_iter().map(|v| v.len()).min().unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn unwrap_first(&self) -> &LazyVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.first.as_ref().unwrap()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn unwrap_average(&self) -> &LazyVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.average.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_sum(&self) -> &LazyVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.sum.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_max(&self) -> &LazyVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.max.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_min(&self) -> &LazyVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.min.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_last(&self) -> &LazyVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.last.as_ref().unwrap()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn unwrap_cumulative(&self) -> &LazyVecFrom2<I, T, S1I, T, I, S2T> {
|
||||
self.cumulative.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
let mut v: Vec<&dyn AnyCollectableVec> = vec![];
|
||||
|
||||
if let Some(first) = self.first.as_ref() {
|
||||
v.push(first.as_ref());
|
||||
}
|
||||
if let Some(last) = self.last.as_ref() {
|
||||
v.push(last.as_ref());
|
||||
}
|
||||
if let Some(min) = self.min.as_ref() {
|
||||
v.push(min.as_ref());
|
||||
}
|
||||
if let Some(max) = self.max.as_ref() {
|
||||
v.push(max.as_ref());
|
||||
}
|
||||
if let Some(average) = self.average.as_ref() {
|
||||
v.push(average.as_ref());
|
||||
}
|
||||
if let Some(sum) = self.sum.as_ref() {
|
||||
v.push(sum.as_ref());
|
||||
}
|
||||
if let Some(cumulative) = self.cumulative.as_ref() {
|
||||
v.push(cumulative.as_ref());
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct LazyVecBuilderOptions {
|
||||
average: bool,
|
||||
sum: bool,
|
||||
max: bool,
|
||||
min: bool,
|
||||
first: bool,
|
||||
last: bool,
|
||||
cumulative: bool,
|
||||
}
|
||||
|
||||
impl From<VecBuilderOptions> for LazyVecBuilderOptions {
|
||||
fn from(value: VecBuilderOptions) -> Self {
|
||||
Self {
|
||||
average: value.average(),
|
||||
sum: value.sum(),
|
||||
max: value.max(),
|
||||
min: value.min(),
|
||||
first: value.first(),
|
||||
last: value.last(),
|
||||
cumulative: value.cumulative(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LazyVecBuilderOptions {
|
||||
pub fn add_first(mut self) -> Self {
|
||||
self.first = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_last(mut self) -> Self {
|
||||
self.last = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_min(mut self) -> Self {
|
||||
self.min = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_max(mut self) -> Self {
|
||||
self.max = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_average(mut self) -> Self {
|
||||
self.average = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_sum(mut self) -> Self {
|
||||
self.sum = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_cumulative(mut self) -> Self {
|
||||
self.cumulative = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_min(mut self) -> Self {
|
||||
self.min = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_max(mut self) -> Self {
|
||||
self.max = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_average(mut self) -> Self {
|
||||
self.average = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_sum(mut self) -> Self {
|
||||
self.sum = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_cumulative(mut self) -> Self {
|
||||
self.cumulative = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_minmax(mut self) -> Self {
|
||||
self.min = true;
|
||||
self.max = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_only_one_active(&self) -> bool {
|
||||
[
|
||||
self.average,
|
||||
self.sum,
|
||||
self.max,
|
||||
self.min,
|
||||
self.first,
|
||||
self.last,
|
||||
self.cumulative,
|
||||
]
|
||||
.iter()
|
||||
.filter(|b| **b)
|
||||
.count()
|
||||
== 1
|
||||
}
|
||||
|
||||
pub fn copy_self_extra(&self) -> Self {
|
||||
Self {
|
||||
cumulative: self.cumulative,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::ops::{Add, AddAssign, Div};
|
||||
|
||||
use brk_vecs::StoredCompressed;
|
||||
use vecdb::StoredCompressed;
|
||||
|
||||
pub trait ComputedType
|
||||
where
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{
|
||||
DateIndex, DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use brk_vecs::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, AnyIterableVec, Computation, EagerVec, Exit, File,
|
||||
Format,
|
||||
};
|
||||
use vecdb::{AnyCloneableIterableVec, AnyCollectableVec, AnyIterableVec, Database, EagerVec, Exit};
|
||||
|
||||
use crate::{Indexes, grouped::ComputedVecBuilder, indexes};
|
||||
use crate::{Indexes, grouped::LazyVecBuilder, indexes};
|
||||
|
||||
use super::{ComputedType, EagerVecBuilder, Source, VecBuilderOptions};
|
||||
|
||||
@@ -22,12 +17,12 @@ where
|
||||
{
|
||||
pub dateindex: Option<EagerVec<DateIndex, T>>,
|
||||
pub dateindex_extra: EagerVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
|
||||
pub semesterindex: ComputedVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T, DateIndex, YearIndex>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
|
||||
pub weekindex: LazyVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
|
||||
pub monthindex: LazyVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
|
||||
pub quarterindex: LazyVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
|
||||
pub semesterindex: LazyVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
|
||||
pub yearindex: LazyVecBuilder<YearIndex, T, DateIndex, YearIndex>,
|
||||
pub decadeindex: LazyVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
@@ -38,24 +33,21 @@ where
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
source: Source<DateIndex, T>,
|
||||
version: Version,
|
||||
format: Format,
|
||||
computation: Computation,
|
||||
indexes: &indexes::Vecs,
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
let dateindex = source.is_compute().then(|| {
|
||||
EagerVec::forced_import(file, name, version + VERSION + Version::ZERO, format).unwrap()
|
||||
EagerVec::forced_import_compressed(db, name, version + VERSION + Version::ZERO).unwrap()
|
||||
});
|
||||
|
||||
let dateindex_extra = EagerVecBuilder::forced_import(
|
||||
file,
|
||||
let dateindex_extra = EagerVecBuilder::forced_import_compressed(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options.copy_self_extra(),
|
||||
)?;
|
||||
|
||||
@@ -64,72 +56,54 @@ where
|
||||
let dateindex_source = source.vec().or(dateindex.as_ref().map(|v| v.boxed_clone()));
|
||||
|
||||
Ok(Self {
|
||||
weekindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
weekindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.weekindex_to_weekindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
monthindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.monthindex_to_monthindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
quarterindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.quarterindex_to_quarterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
semesterindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
semesterindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.semesterindex_to_semesterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
yearindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.yearindex_to_yearindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
decadeindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
dateindex_source.clone(),
|
||||
&dateindex_extra,
|
||||
indexes.decadeindex_to_decadeindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
),
|
||||
dateindex,
|
||||
dateindex_extra,
|
||||
})
|
||||
@@ -161,12 +135,11 @@ where
|
||||
)?;
|
||||
|
||||
let dateindex: Option<&EagerVec<DateIndex, T>> = None;
|
||||
self.compute_rest(indexes, starting_indexes, exit, dateindex)
|
||||
self.compute_rest(starting_indexes, exit, dateindex)
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
dateindex: Option<&impl AnyIterableVec<DateIndex, T>>,
|
||||
@@ -181,42 +154,6 @@ where
|
||||
.extend(starting_indexes.dateindex, dateindex, exit)?;
|
||||
}
|
||||
|
||||
self.weekindex.compute_if_necessary(
|
||||
starting_indexes.weekindex,
|
||||
&indexes.weekindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.compute_if_necessary(
|
||||
starting_indexes.monthindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex.compute_if_necessary(
|
||||
starting_indexes.quarterindex,
|
||||
&indexes.quarterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.semesterindex.compute_if_necessary(
|
||||
starting_indexes.semesterindex,
|
||||
&indexes.semesterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.compute_if_necessary(
|
||||
starting_indexes.yearindex,
|
||||
&indexes.yearindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.compute_if_necessary(
|
||||
starting_indexes.decadeindex,
|
||||
&indexes.decadeindex_to_yearindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_indexer::Indexer;
|
||||
@@ -7,14 +5,11 @@ use brk_structs::{
|
||||
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, SemesterIndex,
|
||||
Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use brk_vecs::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, AnyIterableVec, Computation, EagerVec, Exit, File,
|
||||
Format,
|
||||
};
|
||||
use vecdb::{AnyCloneableIterableVec, AnyCollectableVec, AnyIterableVec, Database, EagerVec, Exit};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecBuilder, Source},
|
||||
grouped::{LazyVecBuilder, Source},
|
||||
indexes,
|
||||
};
|
||||
|
||||
@@ -28,14 +23,14 @@ where
|
||||
pub height: Option<EagerVec<Height, T>>,
|
||||
pub height_extra: EagerVecBuilder<Height, T>,
|
||||
pub dateindex: EagerVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
|
||||
pub weekindex: LazyVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
|
||||
pub difficultyepoch: EagerVecBuilder<DifficultyEpoch, T>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
|
||||
pub semesterindex: ComputedVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T, DateIndex, YearIndex>,
|
||||
pub monthindex: LazyVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
|
||||
pub quarterindex: LazyVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
|
||||
pub semesterindex: LazyVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
|
||||
pub yearindex: LazyVecBuilder<YearIndex, T, DateIndex, YearIndex>,
|
||||
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
|
||||
pub decadeindex: LazyVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
@@ -47,113 +42,90 @@ where
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
source: Source<Height, T>,
|
||||
version: Version,
|
||||
format: Format,
|
||||
computation: Computation,
|
||||
indexes: &indexes::Vecs,
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
let height = source.is_compute().then(|| {
|
||||
EagerVec::forced_import(file, name, version + VERSION + Version::ZERO, format).unwrap()
|
||||
EagerVec::forced_import_compressed(db, name, version + VERSION + Version::ZERO).unwrap()
|
||||
});
|
||||
|
||||
let height_extra = EagerVecBuilder::forced_import(
|
||||
file,
|
||||
let height_extra = EagerVecBuilder::forced_import_compressed(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options.copy_self_extra(),
|
||||
)?;
|
||||
|
||||
let dateindex = EagerVecBuilder::forced_import(
|
||||
file,
|
||||
let dateindex = EagerVecBuilder::forced_import_compressed(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?;
|
||||
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
Ok(Self {
|
||||
weekindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
weekindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.weekindex_to_weekindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
monthindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.monthindex_to_monthindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
quarterindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.quarterindex_to_quarterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
semesterindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
semesterindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.semesterindex_to_semesterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
yearindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.yearindex_to_yearindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
decadeindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.decadeindex_to_decadeindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(file, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
),
|
||||
// halvingepoch: StorableVecGeneator::forced_import(db, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
height,
|
||||
height_extra,
|
||||
dateindex,
|
||||
difficultyepoch: EagerVecBuilder::forced_import(
|
||||
file,
|
||||
difficultyepoch: EagerVecBuilder::forced_import_compressed(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
})
|
||||
@@ -231,42 +203,6 @@ where
|
||||
)?;
|
||||
}
|
||||
|
||||
self.weekindex.compute_if_necessary(
|
||||
starting_indexes.weekindex,
|
||||
&indexes.weekindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.compute_if_necessary(
|
||||
starting_indexes.monthindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex.compute_if_necessary(
|
||||
starting_indexes.quarterindex,
|
||||
&indexes.quarterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.semesterindex.compute_if_necessary(
|
||||
starting_indexes.semesterindex,
|
||||
&indexes.semesterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.compute_if_necessary(
|
||||
starting_indexes.yearindex,
|
||||
&indexes.yearindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.compute_if_necessary(
|
||||
starting_indexes.decadeindex,
|
||||
&indexes.decadeindex_to_yearindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{DifficultyEpoch, Height, Version};
|
||||
use brk_vecs::{AnyCollectableVec, EagerVec, Exit, File, Format};
|
||||
use vecdb::{AnyCollectableVec, Database, EagerVec, Exit};
|
||||
|
||||
use crate::{Indexes, indexes};
|
||||
|
||||
@@ -29,20 +27,18 @@ where
|
||||
f64: From<T>,
|
||||
{
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
let height =
|
||||
EagerVec::forced_import(file, name, version + VERSION + Version::ZERO, format)?;
|
||||
EagerVec::forced_import_compressed(db, name, version + VERSION + Version::ZERO)?;
|
||||
|
||||
let height_extra = EagerVecBuilder::forced_import(
|
||||
file,
|
||||
let height_extra = EagerVecBuilder::forced_import_compressed(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options.copy_self_extra(),
|
||||
)?;
|
||||
|
||||
@@ -51,14 +47,13 @@ where
|
||||
Ok(Self {
|
||||
height,
|
||||
height_extra,
|
||||
difficultyepoch: EagerVecBuilder::forced_import(
|
||||
file,
|
||||
difficultyepoch: EagerVecBuilder::forced_import_compressed(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(file, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(db, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{
|
||||
Bitcoin, DateIndex, DecadeIndex, DifficultyEpoch, Dollars, Height, MonthIndex, QuarterIndex,
|
||||
Sats, SemesterIndex, TxIndex, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use brk_vecs::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, AnyVec, CollectableVec, Computation, EagerVec,
|
||||
Exit, File, Format, GenericStoredVec, StoredIndex, VecIterator,
|
||||
use vecdb::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, AnyVec, CollectableVec, Database, EagerVec, Exit,
|
||||
GenericStoredVec, StoredIndex, VecIterator,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecBuilder, Source},
|
||||
grouped::{LazyVecBuilder, Source},
|
||||
indexes, price,
|
||||
};
|
||||
|
||||
@@ -27,14 +25,14 @@ where
|
||||
pub txindex: Option<Box<EagerVec<TxIndex, T>>>,
|
||||
pub height: EagerVecBuilder<Height, T>,
|
||||
pub dateindex: EagerVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
|
||||
pub weekindex: LazyVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
|
||||
pub difficultyepoch: EagerVecBuilder<DifficultyEpoch, T>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
|
||||
pub semesterindex: ComputedVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T, DateIndex, YearIndex>,
|
||||
pub monthindex: LazyVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
|
||||
pub quarterindex: LazyVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
|
||||
pub semesterindex: LazyVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
|
||||
pub yearindex: LazyVecBuilder<YearIndex, T, DateIndex, YearIndex>,
|
||||
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
|
||||
pub decadeindex: LazyVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
@@ -46,119 +44,96 @@ where
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
source: Source<TxIndex, T>,
|
||||
version: Version,
|
||||
format: Format,
|
||||
computation: Computation,
|
||||
indexes: &indexes::Vecs,
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
let txindex = source.is_compute().then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(file, name, version + VERSION + Version::ZERO, format)
|
||||
EagerVec::forced_import_compressed(db, name, version + VERSION + Version::ZERO)
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let height = EagerVecBuilder::forced_import(
|
||||
file,
|
||||
let height = EagerVecBuilder::forced_import_compressed(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?;
|
||||
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
let dateindex = EagerVecBuilder::forced_import(
|
||||
file,
|
||||
let dateindex = EagerVecBuilder::forced_import_compressed(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
weekindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
weekindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.weekindex_to_weekindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
monthindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.monthindex_to_monthindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
quarterindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.quarterindex_to_quarterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
semesterindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
semesterindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.semesterindex_to_semesterindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
yearindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.yearindex_to_yearindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
file,
|
||||
),
|
||||
decadeindex: LazyVecBuilder::forced_import(
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
Computation::Lazy,
|
||||
None,
|
||||
&dateindex,
|
||||
indexes.decadeindex_to_decadeindex.boxed_clone(),
|
||||
options.into(),
|
||||
)?,
|
||||
),
|
||||
|
||||
txindex,
|
||||
height,
|
||||
dateindex,
|
||||
difficultyepoch: EagerVecBuilder::forced_import(
|
||||
file,
|
||||
difficultyepoch: EagerVecBuilder::forced_import_compressed(
|
||||
db,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(file, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(db, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -239,42 +214,6 @@ where
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.weekindex.compute_if_necessary(
|
||||
starting_indexes.weekindex,
|
||||
&indexes.weekindex_to_first_dateindex,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.compute_if_necessary(
|
||||
starting_indexes.monthindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex.compute_if_necessary(
|
||||
starting_indexes.quarterindex,
|
||||
&indexes.quarterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.semesterindex.compute_if_necessary(
|
||||
starting_indexes.semesterindex,
|
||||
&indexes.semesterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.compute_if_necessary(
|
||||
starting_indexes.yearindex,
|
||||
&indexes.yearindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.compute_if_necessary(
|
||||
starting_indexes.decadeindex,
|
||||
&indexes.decadeindex_to_yearindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.difficultyepoch.from_aligned(
|
||||
starting_indexes.difficultyepoch,
|
||||
&self.height,
|
||||
@@ -325,7 +264,7 @@ impl ComputedVecsFromTxindex<Bitcoin> {
|
||||
};
|
||||
|
||||
self.height
|
||||
.validate_computed_version_or_reset_file(txindex_version)?;
|
||||
.validate_computed_version_or_reset(txindex_version)?;
|
||||
|
||||
let starting_index = self.height.starting_index(starting_indexes.height);
|
||||
|
||||
@@ -504,7 +443,7 @@ impl ComputedVecsFromTxindex<Dollars> {
|
||||
};
|
||||
|
||||
self.height
|
||||
.validate_computed_version_or_reset_file(txindex_version)?;
|
||||
.validate_computed_version_or_reset(txindex_version)?;
|
||||
|
||||
let starting_index = self.height.starting_index(starting_indexes.height);
|
||||
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
mod builder_computed;
|
||||
mod builder_eager;
|
||||
mod builder_lazy;
|
||||
mod computed;
|
||||
mod from_dateindex;
|
||||
mod from_height;
|
||||
mod from_height_strict;
|
||||
mod from_txindex;
|
||||
mod ratio_from_dateindex;
|
||||
mod sd_from_dateindex;
|
||||
mod source;
|
||||
mod value_from_dateindex;
|
||||
mod value_from_height;
|
||||
mod value_from_txindex;
|
||||
mod value_height;
|
||||
|
||||
pub use builder_computed::*;
|
||||
pub use builder_eager::*;
|
||||
pub use builder_lazy::*;
|
||||
use computed::*;
|
||||
pub use from_dateindex::*;
|
||||
pub use from_height::*;
|
||||
pub use from_height_strict::*;
|
||||
pub use from_txindex::*;
|
||||
pub use ratio_from_dateindex::*;
|
||||
pub use sd_from_dateindex::*;
|
||||
pub use source::*;
|
||||
pub use value_from_dateindex::*;
|
||||
pub use value_from_height::*;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,707 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{CheckedSub, Date, DateIndex, Dollars, StoredF32, Version};
|
||||
use vecdb::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, BoxedVecIterator, CollectableVec,
|
||||
Database, EagerVec, Exit, GenericStoredVec, StoredIndex,
|
||||
};
|
||||
|
||||
use crate::{Indexes, grouped::source::Source, indexes};
|
||||
|
||||
use super::{ComputedVecsFromDateIndex, VecBuilderOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedStandardDeviationVecsFromDateIndex {
|
||||
days: usize,
|
||||
|
||||
pub sma: Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub _0sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub p0_5sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub p1sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub p1_5sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub p2sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub p2_5sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub p3sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub m0_5sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub m1sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub m1_5sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub m2sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub m2_5sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub m3sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub p0_5sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub p1sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub p1_5sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub p2sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub p2_5sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub p3sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub m0_5sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub m1sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub m1_5sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub m2sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub m2_5sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub m3sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub zscore: ComputedVecsFromDateIndex<StoredF32>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE;
|
||||
|
||||
impl ComputedStandardDeviationVecsFromDateIndex {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
days: usize,
|
||||
source: Source<DateIndex, StoredF32>,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let options = VecBuilderOptions::default().add_last();
|
||||
|
||||
Ok(Self {
|
||||
days,
|
||||
sma: source.is_compute().then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_sma"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p0_5sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p0_5sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p1sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p1sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p1_5sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p1_5sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p2sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p2sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p2_5sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p2_5sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p3sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p3sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m0_5sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m0_5sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m1sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m1sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m1_5sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m1_5sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m2sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m2sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m2_5sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m2_5sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m3sd: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m3sd"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
_0sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_0sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p0_5sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p0_5sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p1sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p1sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p1_5sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p1_5sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p2sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p2sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p2_5sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p2_5sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
p3sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_p3sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m0_5sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m0_5sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m1sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m1sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m1_5sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m1_5sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m2sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m2sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m2_5sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m2_5sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
m3sd_as_price: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_m3sd_as_price"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
zscore: ComputedVecsFromDateIndex::forced_import(
|
||||
db,
|
||||
&format!("{name}_zscore"),
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
source: &impl CollectableVec<DateIndex, StoredF32>,
|
||||
source_as_price: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
|
||||
) -> Result<()> {
|
||||
let min_date = DateIndex::try_from(Date::MIN_RATIO).unwrap();
|
||||
|
||||
self.sma.as_mut().unwrap().compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, _, _, starting_indexes, exit| {
|
||||
v.compute_sma_(
|
||||
starting_indexes.dateindex,
|
||||
source,
|
||||
self.days,
|
||||
exit,
|
||||
Some(min_date),
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
let sma_opt: Option<&EagerVec<DateIndex, StoredF32>> = None;
|
||||
self.compute_rest(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
sma_opt,
|
||||
source,
|
||||
source_as_price,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
sma_opt: Option<&impl AnyIterableVec<DateIndex, StoredF32>>,
|
||||
source: &impl CollectableVec<DateIndex, StoredF32>,
|
||||
source_as_price: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
|
||||
) -> Result<()> {
|
||||
let sma = sma_opt.unwrap_or_else(|| unsafe {
|
||||
std::mem::transmute(&self.sma.as_ref().unwrap().dateindex)
|
||||
});
|
||||
|
||||
let min_date = DateIndex::try_from(Date::MIN_RATIO).unwrap();
|
||||
|
||||
let source_version = source.version();
|
||||
|
||||
self.mut_vecs().iter_mut().try_for_each(|v| -> Result<()> {
|
||||
v.validate_computed_version_or_reset(
|
||||
Version::ZERO + v.inner_version() + source_version,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let starting_dateindex = self
|
||||
.mut_vecs()
|
||||
.iter()
|
||||
.map(|v| DateIndex::from(v.len()))
|
||||
.min()
|
||||
.unwrap()
|
||||
.min(starting_indexes.dateindex);
|
||||
|
||||
let mut sorted = source.collect_range(
|
||||
Some(min_date.unwrap_to_usize()),
|
||||
Some(starting_dateindex.unwrap_to_usize()),
|
||||
)?;
|
||||
|
||||
sorted.sort_unstable();
|
||||
|
||||
let mut sma_iter = sma.iter();
|
||||
|
||||
source
|
||||
.iter_at(starting_dateindex)
|
||||
.try_for_each(|(index, ratio)| -> Result<()> {
|
||||
if index < min_date {
|
||||
self.sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.p0_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
self.p1sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
self.p1_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
self.p2sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
self.p2_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
self.p3sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
self.m0_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
self.m1sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
self.m1_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
self.m2sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
self.m2_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
self.m3sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
StoredF32::NAN,
|
||||
exit,
|
||||
)?;
|
||||
} else {
|
||||
let ratio = ratio.into_owned();
|
||||
let pos = sorted.binary_search(&ratio).unwrap_or_else(|pos| pos);
|
||||
sorted.insert(pos, ratio);
|
||||
|
||||
let avg = sma_iter.unwrap_get_inner(index);
|
||||
|
||||
let population =
|
||||
index.checked_sub(min_date).unwrap().unwrap_to_usize() as f32 + 1.0;
|
||||
|
||||
let sd = StoredF32::from(
|
||||
(sorted.iter().map(|v| (**v - *avg).powi(2)).sum::<f32>() / population)
|
||||
.sqrt(),
|
||||
);
|
||||
|
||||
self.sd
|
||||
.dateindex
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.forced_push_at(index, sd, exit)?;
|
||||
self.p0_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
avg + StoredF32::from(0.5 * *sd),
|
||||
exit,
|
||||
)?;
|
||||
self.p1sd
|
||||
.dateindex
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.forced_push_at(index, avg + sd, exit)?;
|
||||
self.p1_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
avg + StoredF32::from(1.5 * *sd),
|
||||
exit,
|
||||
)?;
|
||||
self.p2sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
avg + 2 * sd,
|
||||
exit,
|
||||
)?;
|
||||
self.p2_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
avg + StoredF32::from(2.5 * *sd),
|
||||
exit,
|
||||
)?;
|
||||
self.p3sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
avg + 3 * sd,
|
||||
exit,
|
||||
)?;
|
||||
self.m0_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
avg - StoredF32::from(0.5 * *sd),
|
||||
exit,
|
||||
)?;
|
||||
self.m1sd
|
||||
.dateindex
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.forced_push_at(index, avg - sd, exit)?;
|
||||
self.m1_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
avg - StoredF32::from(1.5 * *sd),
|
||||
exit,
|
||||
)?;
|
||||
self.m2sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
avg - 2 * sd,
|
||||
exit,
|
||||
)?;
|
||||
self.m2_5sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
avg - StoredF32::from(2.5 * *sd),
|
||||
exit,
|
||||
)?;
|
||||
self.m3sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||
index,
|
||||
avg - 3 * sd,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
drop(sma_iter);
|
||||
|
||||
self.mut_vecs()
|
||||
.into_iter()
|
||||
.try_for_each(|v| v.safe_flush(exit))?;
|
||||
|
||||
[
|
||||
&mut self.sd,
|
||||
&mut self.p0_5sd,
|
||||
&mut self.p1sd,
|
||||
&mut self.p1_5sd,
|
||||
&mut self.p2sd,
|
||||
&mut self.p2_5sd,
|
||||
&mut self.p3sd,
|
||||
&mut self.m0_5sd,
|
||||
&mut self.m1sd,
|
||||
&mut self.m1_5sd,
|
||||
&mut self.m2sd,
|
||||
&mut self.m2_5sd,
|
||||
&mut self.m3sd,
|
||||
]
|
||||
.into_iter()
|
||||
.try_for_each(|v| {
|
||||
v.compute_rest(starting_indexes, exit, None as Option<&EagerVec<_, _>>)
|
||||
})?;
|
||||
|
||||
self.zscore.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_zscore(
|
||||
starting_indexes.dateindex,
|
||||
source,
|
||||
sma,
|
||||
self.sd.dateindex.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
let Some(price) = source_as_price else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let compute_as_price =
|
||||
|as_price: &mut ComputedVecsFromDateIndex<Dollars>,
|
||||
mut iter: BoxedVecIterator<DateIndex, StoredF32>| {
|
||||
as_price.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.dateindex,
|
||||
price,
|
||||
|(i, price, ..)| {
|
||||
let multiplier = iter.unwrap_get_inner(i);
|
||||
(i, price * multiplier)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
compute_as_price(&mut self._0sd_as_price, sma.iter())?;
|
||||
compute_as_price(
|
||||
&mut self.p0_5sd_as_price,
|
||||
self.p0_5sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
compute_as_price(
|
||||
&mut self.p1sd_as_price,
|
||||
self.p1sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
compute_as_price(
|
||||
&mut self.p1_5sd_as_price,
|
||||
self.p1_5sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
compute_as_price(
|
||||
&mut self.p2sd_as_price,
|
||||
self.p2sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
compute_as_price(
|
||||
&mut self.p2_5sd_as_price,
|
||||
self.p2_5sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
compute_as_price(
|
||||
&mut self.p3sd_as_price,
|
||||
self.p3sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
compute_as_price(
|
||||
&mut self.m0_5sd_as_price,
|
||||
self.m0_5sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
compute_as_price(
|
||||
&mut self.m1sd_as_price,
|
||||
self.m1sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
compute_as_price(
|
||||
&mut self.m1_5sd_as_price,
|
||||
self.m1_5sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
compute_as_price(
|
||||
&mut self.m2sd_as_price,
|
||||
self.m2sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
compute_as_price(
|
||||
&mut self.m2_5sd_as_price,
|
||||
self.m2_5sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
compute_as_price(
|
||||
&mut self.m3sd_as_price,
|
||||
self.m3sd.dateindex.as_ref().unwrap().iter(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mut_vecs(&mut self) -> [&mut EagerVec<DateIndex, StoredF32>; 13] {
|
||||
[
|
||||
self.sd.dateindex.as_mut().unwrap(),
|
||||
self.p0_5sd.dateindex.as_mut().unwrap(),
|
||||
self.p1sd.dateindex.as_mut().unwrap(),
|
||||
self.p1_5sd.dateindex.as_mut().unwrap(),
|
||||
self.p2sd.dateindex.as_mut().unwrap(),
|
||||
self.p2_5sd.dateindex.as_mut().unwrap(),
|
||||
self.p3sd.dateindex.as_mut().unwrap(),
|
||||
self.m0_5sd.dateindex.as_mut().unwrap(),
|
||||
self.m1sd.dateindex.as_mut().unwrap(),
|
||||
self.m1_5sd.dateindex.as_mut().unwrap(),
|
||||
self.m2sd.dateindex.as_mut().unwrap(),
|
||||
self.m2_5sd.dateindex.as_mut().unwrap(),
|
||||
self.m3sd.dateindex.as_mut().unwrap(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.sma.as_ref().map_or(vec![], |v| v.vecs()),
|
||||
self.sd.vecs(),
|
||||
self.p0_5sd.vecs(),
|
||||
self.p1sd.vecs(),
|
||||
self.p1_5sd.vecs(),
|
||||
self.p2sd.vecs(),
|
||||
self.p2_5sd.vecs(),
|
||||
self.p3sd.vecs(),
|
||||
self.m0_5sd.vecs(),
|
||||
self.m1sd.vecs(),
|
||||
self.m1_5sd.vecs(),
|
||||
self.m2sd.vecs(),
|
||||
self.m2_5sd.vecs(),
|
||||
self.m3sd.vecs(),
|
||||
self._0sd_as_price.vecs(),
|
||||
self.p0_5sd_as_price.vecs(),
|
||||
self.p1sd_as_price.vecs(),
|
||||
self.p1_5sd_as_price.vecs(),
|
||||
self.p2sd_as_price.vecs(),
|
||||
self.p2_5sd_as_price.vecs(),
|
||||
self.p3sd_as_price.vecs(),
|
||||
self.m0_5sd_as_price.vecs(),
|
||||
self.m1sd_as_price.vecs(),
|
||||
self.m1_5sd_as_price.vecs(),
|
||||
self.m2sd_as_price.vecs(),
|
||||
self.m2_5sd_as_price.vecs(),
|
||||
self.m3sd_as_price.vecs(),
|
||||
self.zscore.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use brk_vecs::AnyBoxedIterableVec;
|
||||
use vecdb::AnyBoxedIterableVec;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Source<I, T> {
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, DateIndex, Dollars, Sats, Version};
|
||||
use brk_vecs::{
|
||||
AnyCollectableVec, CollectableVec, Computation, EagerVec, Exit, File, Format, StoredVec,
|
||||
};
|
||||
use vecdb::{AnyCollectableVec, CollectableVec, Database, EagerVec, Exit, StoredVec};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
@@ -28,45 +24,37 @@ const VERSION: Version = Version::ZERO;
|
||||
impl ComputedValueVecsFromDateIndex {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
source: Source<DateIndex, Sats>,
|
||||
version: Version,
|
||||
format: Format,
|
||||
computation: Computation,
|
||||
options: VecBuilderOptions,
|
||||
compute_dollars: bool,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sats: ComputedVecsFromDateIndex::forced_import(
|
||||
file,
|
||||
db,
|
||||
name,
|
||||
source,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
bitcoin: ComputedVecsFromDateIndex::forced_import(
|
||||
file,
|
||||
db,
|
||||
&format!("{name}_in_btc"),
|
||||
Source::Compute,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
dollars: compute_dollars.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
file,
|
||||
db,
|
||||
&format!("{name}_in_usd"),
|
||||
Source::Compute,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)
|
||||
@@ -118,7 +106,7 @@ impl ComputedValueVecsFromDateIndex {
|
||||
) -> Result<()> {
|
||||
if let Some(dateindex) = dateindex {
|
||||
self.sats
|
||||
.compute_rest(indexes, starting_indexes, exit, Some(dateindex))?;
|
||||
.compute_rest(starting_indexes, exit, Some(dateindex))?;
|
||||
|
||||
self.bitcoin.compute_all(
|
||||
indexer,
|
||||
@@ -132,8 +120,7 @@ impl ComputedValueVecsFromDateIndex {
|
||||
} else {
|
||||
let dateindex: Option<&StoredVec<DateIndex, Sats>> = None;
|
||||
|
||||
self.sats
|
||||
.compute_rest(indexes, starting_indexes, exit, dateindex)?;
|
||||
self.sats.compute_rest(starting_indexes, exit, dateindex)?;
|
||||
|
||||
self.bitcoin.compute_all(
|
||||
indexer,
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use brk_vecs::{
|
||||
AnyCollectableVec, CollectableVec, Computation, EagerVec, Exit, File, Format, StoredVec,
|
||||
};
|
||||
use vecdb::{AnyCollectableVec, CollectableVec, Database, EagerVec, Exit, StoredVec};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
@@ -28,45 +24,37 @@ const VERSION: Version = Version::ZERO;
|
||||
impl ComputedValueVecsFromHeight {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
source: Source<Height, Sats>,
|
||||
version: Version,
|
||||
format: Format,
|
||||
computation: Computation,
|
||||
options: VecBuilderOptions,
|
||||
compute_dollars: bool,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sats: ComputedVecsFromHeight::forced_import(
|
||||
file,
|
||||
db,
|
||||
name,
|
||||
source,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
bitcoin: ComputedVecsFromHeight::forced_import(
|
||||
file,
|
||||
db,
|
||||
&format!("{name}_in_btc"),
|
||||
Source::Compute,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?,
|
||||
dollars: compute_dollars.then(|| {
|
||||
ComputedVecsFromHeight::forced_import(
|
||||
file,
|
||||
db,
|
||||
&format!("{name}_in_usd"),
|
||||
Source::Compute,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, Close, Dollars, Height, Sats, TxIndex, Version};
|
||||
use brk_vecs::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, CollectableVec, Computation, ComputedVecFrom3,
|
||||
Exit, File, Format, LazyVecFrom1, StoredIndex, StoredVec,
|
||||
use vecdb::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, CollectableVec, Database, Exit, LazyVecFrom1,
|
||||
LazyVecFrom3, StoredIndex, StoredVec,
|
||||
};
|
||||
|
||||
use crate::{Indexes, grouped::Source, indexes, price};
|
||||
@@ -19,16 +17,7 @@ pub struct ComputedValueVecsFromTxindex {
|
||||
pub bitcoin: ComputedVecsFromTxindex<Bitcoin>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub dollars_txindex: Option<
|
||||
ComputedVecFrom3<
|
||||
TxIndex,
|
||||
Dollars,
|
||||
TxIndex,
|
||||
Bitcoin,
|
||||
TxIndex,
|
||||
Height,
|
||||
Height,
|
||||
Close<Dollars>,
|
||||
>,
|
||||
LazyVecFrom3<TxIndex, Dollars, TxIndex, Bitcoin, TxIndex, Height, Height, Close<Dollars>>,
|
||||
>,
|
||||
pub dollars: Option<ComputedVecsFromTxindex<Dollars>>,
|
||||
}
|
||||
@@ -38,13 +27,11 @@ const VERSION: Version = Version::ZERO;
|
||||
impl ComputedValueVecsFromTxindex {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
indexes: &indexes::Vecs,
|
||||
source: Source<TxIndex, Sats>,
|
||||
version: Version,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
price: Option<&price::Vecs>,
|
||||
options: VecBuilderOptions,
|
||||
) -> Result<Self> {
|
||||
@@ -54,12 +41,10 @@ impl ComputedValueVecsFromTxindex {
|
||||
let name_in_usd = format!("{name}_in_usd");
|
||||
|
||||
let sats = ComputedVecsFromTxindex::forced_import(
|
||||
file,
|
||||
db,
|
||||
name,
|
||||
source.clone(),
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?;
|
||||
@@ -79,23 +64,18 @@ impl ComputedValueVecsFromTxindex {
|
||||
);
|
||||
|
||||
let bitcoin = ComputedVecsFromTxindex::forced_import(
|
||||
file,
|
||||
db,
|
||||
&name_in_btc,
|
||||
Source::None,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)?;
|
||||
|
||||
let dollars_txindex = price.map(|price| {
|
||||
ComputedVecFrom3::forced_import_or_init_from_3(
|
||||
computation,
|
||||
file,
|
||||
LazyVecFrom3::init(
|
||||
&name_in_usd,
|
||||
version + VERSION,
|
||||
format,
|
||||
bitcoin_txindex.boxed_clone(),
|
||||
indexes.txindex_to_height.boxed_clone(),
|
||||
price.chainindexes_to_close.height.boxed_clone(),
|
||||
@@ -117,7 +97,6 @@ impl ComputedValueVecsFromTxindex {
|
||||
})
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
@@ -127,12 +106,10 @@ impl ComputedValueVecsFromTxindex {
|
||||
dollars_txindex,
|
||||
dollars: compute_dollars.then(|| {
|
||||
ComputedVecsFromTxindex::forced_import(
|
||||
file,
|
||||
db,
|
||||
&name_in_usd,
|
||||
Source::None,
|
||||
version + VERSION,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
options,
|
||||
)
|
||||
@@ -210,12 +187,6 @@ impl ComputedValueVecsFromTxindex {
|
||||
if let Some(dollars) = self.dollars.as_mut() {
|
||||
let dollars_txindex = self.dollars_txindex.as_mut().unwrap();
|
||||
|
||||
dollars_txindex.compute_if_necessary(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
dollars.compute_rest_from_bitcoin(
|
||||
indexer,
|
||||
indexes,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use brk_vecs::{AnyCollectableVec, CollectableVec, EagerVec, Exit, File, Format, StoredVec};
|
||||
use vecdb::{AnyCollectableVec, CollectableVec, Database, EagerVec, Exit, Format, StoredVec};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
@@ -23,7 +21,7 @@ const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl ComputedHeightValueVecs {
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
name: &str,
|
||||
source: Source<Height, Sats>,
|
||||
version: Version,
|
||||
@@ -32,18 +30,18 @@ impl ComputedHeightValueVecs {
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sats: source.is_compute().then(|| {
|
||||
EagerVec::forced_import(file, name, version + VERSION + Version::ZERO, format)
|
||||
EagerVec::forced_import(db, name, version + VERSION + Version::ZERO, format)
|
||||
.unwrap()
|
||||
}),
|
||||
bitcoin: EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&format!("{name}_in_btc"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
dollars: compute_dollars.then(|| {
|
||||
EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&format!("{name}_in_usd"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
|
||||
+147
-364
@@ -1,4 +1,4 @@
|
||||
use std::{ops::Deref, path::Path, sync::Arc};
|
||||
use std::{ops::Deref, path::Path};
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
@@ -10,16 +10,16 @@ use brk_structs::{
|
||||
P2WPKHBytes, P2WSHAddressIndex, P2WSHBytes, QuarterIndex, Sats, SemesterIndex, StoredU64,
|
||||
Timestamp, TxIndex, Txid, UnknownOutputIndex, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use brk_vecs::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, Computation, ComputedVec, ComputedVecFrom1,
|
||||
ComputedVecFrom2, EagerVec, Exit, File, Format, PAGE_SIZE, StoredIndex, VecIterator,
|
||||
use vecdb::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, Database, EagerVec, Exit, LazyVecFrom1,
|
||||
LazyVecFrom2, PAGE_SIZE, StoredIndex, VecIterator,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
file: Arc<File>,
|
||||
db: Database,
|
||||
|
||||
pub dateindex_to_date: EagerVec<DateIndex, Date>,
|
||||
pub dateindex_to_dateindex: EagerVec<DateIndex, DateIndex>,
|
||||
@@ -34,7 +34,7 @@ pub struct Vecs {
|
||||
pub difficultyepoch_to_first_height: EagerVec<DifficultyEpoch, Height>,
|
||||
pub difficultyepoch_to_height_count: EagerVec<DifficultyEpoch, StoredU64>,
|
||||
pub emptyoutputindex_to_emptyoutputindex:
|
||||
ComputedVecFrom1<EmptyOutputIndex, EmptyOutputIndex, EmptyOutputIndex, TxIndex>,
|
||||
LazyVecFrom1<EmptyOutputIndex, EmptyOutputIndex, EmptyOutputIndex, TxIndex>,
|
||||
pub halvingepoch_to_first_height: EagerVec<HalvingEpoch, Height>,
|
||||
pub halvingepoch_to_halvingepoch: EagerVec<HalvingEpoch, HalvingEpoch>,
|
||||
pub height_to_date: EagerVec<Height, Date>,
|
||||
@@ -45,7 +45,7 @@ pub struct Vecs {
|
||||
pub height_to_height: EagerVec<Height, Height>,
|
||||
pub height_to_timestamp_fixed: EagerVec<Height, Timestamp>,
|
||||
pub height_to_txindex_count: EagerVec<Height, StoredU64>,
|
||||
pub inputindex_to_inputindex: ComputedVecFrom1<InputIndex, InputIndex, InputIndex, OutputIndex>,
|
||||
pub inputindex_to_inputindex: LazyVecFrom1<InputIndex, InputIndex, InputIndex, OutputIndex>,
|
||||
pub monthindex_to_dateindex_count: EagerVec<MonthIndex, StoredU64>,
|
||||
pub monthindex_to_first_dateindex: EagerVec<MonthIndex, DateIndex>,
|
||||
pub monthindex_to_monthindex: EagerVec<MonthIndex, MonthIndex>,
|
||||
@@ -53,27 +53,27 @@ pub struct Vecs {
|
||||
pub monthindex_to_semesterindex: EagerVec<MonthIndex, SemesterIndex>,
|
||||
pub monthindex_to_yearindex: EagerVec<MonthIndex, YearIndex>,
|
||||
pub opreturnindex_to_opreturnindex:
|
||||
ComputedVecFrom1<OpReturnIndex, OpReturnIndex, OpReturnIndex, TxIndex>,
|
||||
pub outputindex_to_outputindex: ComputedVecFrom1<OutputIndex, OutputIndex, OutputIndex, Sats>,
|
||||
LazyVecFrom1<OpReturnIndex, OpReturnIndex, OpReturnIndex, TxIndex>,
|
||||
pub outputindex_to_outputindex: LazyVecFrom1<OutputIndex, OutputIndex, OutputIndex, Sats>,
|
||||
pub outputindex_to_txindex: EagerVec<OutputIndex, TxIndex>,
|
||||
pub p2aaddressindex_to_p2aaddressindex:
|
||||
ComputedVecFrom1<P2AAddressIndex, P2AAddressIndex, P2AAddressIndex, P2ABytes>,
|
||||
LazyVecFrom1<P2AAddressIndex, P2AAddressIndex, P2AAddressIndex, P2ABytes>,
|
||||
pub p2msoutputindex_to_p2msoutputindex:
|
||||
ComputedVecFrom1<P2MSOutputIndex, P2MSOutputIndex, P2MSOutputIndex, TxIndex>,
|
||||
LazyVecFrom1<P2MSOutputIndex, P2MSOutputIndex, P2MSOutputIndex, TxIndex>,
|
||||
pub p2pk33addressindex_to_p2pk33addressindex:
|
||||
ComputedVecFrom1<P2PK33AddressIndex, P2PK33AddressIndex, P2PK33AddressIndex, P2PK33Bytes>,
|
||||
LazyVecFrom1<P2PK33AddressIndex, P2PK33AddressIndex, P2PK33AddressIndex, P2PK33Bytes>,
|
||||
pub p2pk65addressindex_to_p2pk65addressindex:
|
||||
ComputedVecFrom1<P2PK65AddressIndex, P2PK65AddressIndex, P2PK65AddressIndex, P2PK65Bytes>,
|
||||
LazyVecFrom1<P2PK65AddressIndex, P2PK65AddressIndex, P2PK65AddressIndex, P2PK65Bytes>,
|
||||
pub p2pkhaddressindex_to_p2pkhaddressindex:
|
||||
ComputedVecFrom1<P2PKHAddressIndex, P2PKHAddressIndex, P2PKHAddressIndex, P2PKHBytes>,
|
||||
LazyVecFrom1<P2PKHAddressIndex, P2PKHAddressIndex, P2PKHAddressIndex, P2PKHBytes>,
|
||||
pub p2shaddressindex_to_p2shaddressindex:
|
||||
ComputedVecFrom1<P2SHAddressIndex, P2SHAddressIndex, P2SHAddressIndex, P2SHBytes>,
|
||||
LazyVecFrom1<P2SHAddressIndex, P2SHAddressIndex, P2SHAddressIndex, P2SHBytes>,
|
||||
pub p2traddressindex_to_p2traddressindex:
|
||||
ComputedVecFrom1<P2TRAddressIndex, P2TRAddressIndex, P2TRAddressIndex, P2TRBytes>,
|
||||
LazyVecFrom1<P2TRAddressIndex, P2TRAddressIndex, P2TRAddressIndex, P2TRBytes>,
|
||||
pub p2wpkhaddressindex_to_p2wpkhaddressindex:
|
||||
ComputedVecFrom1<P2WPKHAddressIndex, P2WPKHAddressIndex, P2WPKHAddressIndex, P2WPKHBytes>,
|
||||
LazyVecFrom1<P2WPKHAddressIndex, P2WPKHAddressIndex, P2WPKHAddressIndex, P2WPKHBytes>,
|
||||
pub p2wshaddressindex_to_p2wshaddressindex:
|
||||
ComputedVecFrom1<P2WSHAddressIndex, P2WSHAddressIndex, P2WSHAddressIndex, P2WSHBytes>,
|
||||
LazyVecFrom1<P2WSHAddressIndex, P2WSHAddressIndex, P2WSHAddressIndex, P2WSHBytes>,
|
||||
pub quarterindex_to_first_monthindex: EagerVec<QuarterIndex, MonthIndex>,
|
||||
pub quarterindex_to_monthindex_count: EagerVec<QuarterIndex, StoredU64>,
|
||||
pub quarterindex_to_quarterindex: EagerVec<QuarterIndex, QuarterIndex>,
|
||||
@@ -82,12 +82,12 @@ pub struct Vecs {
|
||||
pub semesterindex_to_semesterindex: EagerVec<SemesterIndex, SemesterIndex>,
|
||||
pub txindex_to_height: EagerVec<TxIndex, Height>,
|
||||
pub txindex_to_input_count:
|
||||
ComputedVecFrom2<TxIndex, StoredU64, TxIndex, InputIndex, InputIndex, OutputIndex>,
|
||||
LazyVecFrom2<TxIndex, StoredU64, TxIndex, InputIndex, InputIndex, OutputIndex>,
|
||||
pub txindex_to_output_count:
|
||||
ComputedVecFrom2<TxIndex, StoredU64, TxIndex, OutputIndex, OutputIndex, Sats>,
|
||||
pub txindex_to_txindex: ComputedVecFrom1<TxIndex, TxIndex, TxIndex, Txid>,
|
||||
LazyVecFrom2<TxIndex, StoredU64, TxIndex, OutputIndex, OutputIndex, Sats>,
|
||||
pub txindex_to_txindex: LazyVecFrom1<TxIndex, TxIndex, TxIndex, Txid>,
|
||||
pub unknownoutputindex_to_unknownoutputindex:
|
||||
ComputedVecFrom1<UnknownOutputIndex, UnknownOutputIndex, UnknownOutputIndex, TxIndex>,
|
||||
LazyVecFrom1<UnknownOutputIndex, UnknownOutputIndex, UnknownOutputIndex, TxIndex>,
|
||||
pub weekindex_to_dateindex_count: EagerVec<WeekIndex, StoredU64>,
|
||||
pub weekindex_to_first_dateindex: EagerVec<WeekIndex, DateIndex>,
|
||||
pub weekindex_to_weekindex: EagerVec<WeekIndex, WeekIndex>,
|
||||
@@ -98,52 +98,34 @@ pub struct Vecs {
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
parent: &Path,
|
||||
version: Version,
|
||||
indexer: &Indexer,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
) -> Result<Self> {
|
||||
let file = Arc::new(File::open(&parent.join("indexes"))?);
|
||||
file.set_min_len(PAGE_SIZE * 10_000_000)?;
|
||||
pub fn forced_import(parent: &Path, version: Version, indexer: &Indexer) -> Result<Self> {
|
||||
let db = Database::open(&parent.join("indexes"))?;
|
||||
db.set_min_len(PAGE_SIZE * 10_000_000)?;
|
||||
|
||||
let outputindex_to_outputindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
let outputindex_to_outputindex = LazyVecFrom1::init(
|
||||
"outputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.outputindex_to_value.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
);
|
||||
|
||||
let inputindex_to_inputindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
let inputindex_to_inputindex = LazyVecFrom1::init(
|
||||
"inputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.inputindex_to_outputindex.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
);
|
||||
|
||||
let txindex_to_txindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
let txindex_to_txindex = LazyVecFrom1::init(
|
||||
"txindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.txindex_to_txid.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
);
|
||||
|
||||
let txindex_to_input_count = ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
&file,
|
||||
let txindex_to_input_count = LazyVecFrom2::init(
|
||||
"input_count",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.txindex_to_first_inputindex.boxed_clone(),
|
||||
indexer.vecs.inputindex_to_outputindex.boxed_clone(),
|
||||
|index: TxIndex, txindex_to_first_inputindex_iter, inputindex_to_outputindex_iter| {
|
||||
@@ -159,14 +141,11 @@ impl Vecs {
|
||||
StoredU64::from((start..end).count())
|
||||
})
|
||||
},
|
||||
)?;
|
||||
);
|
||||
|
||||
let txindex_to_output_count = ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
&file,
|
||||
let txindex_to_output_count = LazyVecFrom2::init(
|
||||
"output_count",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.txindex_to_first_outputindex.boxed_clone(),
|
||||
indexer.vecs.outputindex_to_value.boxed_clone(),
|
||||
|index: TxIndex, txindex_to_first_outputindex_iter, outputindex_to_value_iter| {
|
||||
@@ -182,116 +161,80 @@ impl Vecs {
|
||||
StoredU64::from((start..end).count())
|
||||
})
|
||||
},
|
||||
)?;
|
||||
);
|
||||
|
||||
let p2pk33addressindex_to_p2pk33addressindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
let p2pk33addressindex_to_p2pk33addressindex = LazyVecFrom1::init(
|
||||
"p2pk33addressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.p2pk33addressindex_to_p2pk33bytes.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
let p2pk65addressindex_to_p2pk65addressindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
);
|
||||
let p2pk65addressindex_to_p2pk65addressindex = LazyVecFrom1::init(
|
||||
"p2pk65addressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.p2pk65addressindex_to_p2pk65bytes.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
let p2pkhaddressindex_to_p2pkhaddressindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
);
|
||||
let p2pkhaddressindex_to_p2pkhaddressindex = LazyVecFrom1::init(
|
||||
"p2pkhaddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.p2pkhaddressindex_to_p2pkhbytes.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
let p2shaddressindex_to_p2shaddressindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
);
|
||||
let p2shaddressindex_to_p2shaddressindex = LazyVecFrom1::init(
|
||||
"p2shaddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.p2shaddressindex_to_p2shbytes.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
let p2traddressindex_to_p2traddressindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
);
|
||||
let p2traddressindex_to_p2traddressindex = LazyVecFrom1::init(
|
||||
"p2traddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.p2traddressindex_to_p2trbytes.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
let p2wpkhaddressindex_to_p2wpkhaddressindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
);
|
||||
let p2wpkhaddressindex_to_p2wpkhaddressindex = LazyVecFrom1::init(
|
||||
"p2wpkhaddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.p2wpkhaddressindex_to_p2wpkhbytes.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
let p2wshaddressindex_to_p2wshaddressindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
);
|
||||
let p2wshaddressindex_to_p2wshaddressindex = LazyVecFrom1::init(
|
||||
"p2wshaddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.p2wshaddressindex_to_p2wshbytes.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
let p2aaddressindex_to_p2aaddressindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
);
|
||||
let p2aaddressindex_to_p2aaddressindex = LazyVecFrom1::init(
|
||||
"p2aaddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.p2aaddressindex_to_p2abytes.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
let p2msoutputindex_to_p2msoutputindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
);
|
||||
let p2msoutputindex_to_p2msoutputindex = LazyVecFrom1::init(
|
||||
"p2msoutputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.p2msoutputindex_to_txindex.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
let emptyoutputindex_to_emptyoutputindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
);
|
||||
let emptyoutputindex_to_emptyoutputindex = LazyVecFrom1::init(
|
||||
"emptyoutputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.emptyoutputindex_to_txindex.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
let unknownoutputindex_to_unknownoutputindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
);
|
||||
let unknownoutputindex_to_unknownoutputindex = LazyVecFrom1::init(
|
||||
"unknownoutputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.unknownoutputindex_to_txindex.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
let opreturnindex_to_opreturnindex = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
);
|
||||
let opreturnindex_to_opreturnindex = LazyVecFrom1::init(
|
||||
"opreturnindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.opreturnindex_to_txindex.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
)?;
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
emptyoutputindex_to_emptyoutputindex,
|
||||
@@ -312,266 +255,223 @@ impl Vecs {
|
||||
txindex_to_txindex,
|
||||
unknownoutputindex_to_unknownoutputindex,
|
||||
|
||||
dateindex_to_date: EagerVec::forced_import(
|
||||
&file,
|
||||
dateindex_to_date: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"date",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
dateindex_to_dateindex: EagerVec::forced_import(
|
||||
&file,
|
||||
dateindex_to_dateindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"dateindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
dateindex_to_first_height: EagerVec::forced_import(
|
||||
&file,
|
||||
dateindex_to_first_height: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"first_height",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
dateindex_to_monthindex: EagerVec::forced_import(
|
||||
&file,
|
||||
dateindex_to_monthindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"monthindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
dateindex_to_weekindex: EagerVec::forced_import(
|
||||
&file,
|
||||
dateindex_to_weekindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"weekindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
decadeindex_to_decadeindex: EagerVec::forced_import(
|
||||
&file,
|
||||
decadeindex_to_decadeindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"decadeindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
decadeindex_to_first_yearindex: EagerVec::forced_import(
|
||||
&file,
|
||||
decadeindex_to_first_yearindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"first_yearindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
difficultyepoch_to_difficultyepoch: EagerVec::forced_import(
|
||||
&file,
|
||||
difficultyepoch_to_difficultyepoch: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"difficultyepoch",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
difficultyepoch_to_first_height: EagerVec::forced_import(
|
||||
&file,
|
||||
difficultyepoch_to_first_height: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"first_height",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
halvingepoch_to_first_height: EagerVec::forced_import(
|
||||
&file,
|
||||
halvingepoch_to_first_height: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"first_height",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
halvingepoch_to_halvingepoch: EagerVec::forced_import(
|
||||
&file,
|
||||
halvingepoch_to_halvingepoch: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"halvingepoch",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_date: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_date: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"date",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_difficultyepoch: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_difficultyepoch: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"difficultyepoch",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_halvingepoch: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_halvingepoch: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"halvingepoch",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_height: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_height: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"height",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
monthindex_to_first_dateindex: EagerVec::forced_import(
|
||||
&file,
|
||||
monthindex_to_first_dateindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"first_dateindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
monthindex_to_monthindex: EagerVec::forced_import(
|
||||
&file,
|
||||
monthindex_to_monthindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"monthindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
monthindex_to_quarterindex: EagerVec::forced_import(
|
||||
&file,
|
||||
monthindex_to_quarterindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"quarterindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
monthindex_to_semesterindex: EagerVec::forced_import(
|
||||
&file,
|
||||
monthindex_to_semesterindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"semesterindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
monthindex_to_yearindex: EagerVec::forced_import(
|
||||
&file,
|
||||
monthindex_to_yearindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"yearindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
quarterindex_to_first_monthindex: EagerVec::forced_import(
|
||||
&file,
|
||||
quarterindex_to_first_monthindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"first_monthindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
semesterindex_to_first_monthindex: EagerVec::forced_import(
|
||||
&file,
|
||||
semesterindex_to_first_monthindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"first_monthindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
weekindex_to_first_dateindex: EagerVec::forced_import(
|
||||
&file,
|
||||
weekindex_to_first_dateindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"first_dateindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
yearindex_to_first_monthindex: EagerVec::forced_import(
|
||||
&file,
|
||||
yearindex_to_first_monthindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"first_monthindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
quarterindex_to_quarterindex: EagerVec::forced_import(
|
||||
&file,
|
||||
quarterindex_to_quarterindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"quarterindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
semesterindex_to_semesterindex: EagerVec::forced_import(
|
||||
&file,
|
||||
semesterindex_to_semesterindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"semesterindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
weekindex_to_weekindex: EagerVec::forced_import(
|
||||
&file,
|
||||
weekindex_to_weekindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"weekindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
yearindex_to_decadeindex: EagerVec::forced_import(
|
||||
&file,
|
||||
yearindex_to_decadeindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"decadeindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
yearindex_to_yearindex: EagerVec::forced_import(
|
||||
&file,
|
||||
yearindex_to_yearindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"yearindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_date_fixed: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_date_fixed: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"date_fixed",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_dateindex: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_dateindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"dateindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
txindex_to_height: EagerVec::forced_import(
|
||||
&file,
|
||||
txindex_to_height: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"height",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_timestamp_fixed: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_timestamp_fixed: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"timestamp_fixed",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_txindex_count: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_txindex_count: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"txindex_count",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
dateindex_to_height_count: EagerVec::forced_import(
|
||||
&file,
|
||||
dateindex_to_height_count: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"height_count",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
weekindex_to_dateindex_count: EagerVec::forced_import(
|
||||
&file,
|
||||
weekindex_to_dateindex_count: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"dateindex_count",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
difficultyepoch_to_height_count: EagerVec::forced_import(
|
||||
&file,
|
||||
difficultyepoch_to_height_count: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"height_count",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
monthindex_to_dateindex_count: EagerVec::forced_import(
|
||||
&file,
|
||||
monthindex_to_dateindex_count: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"dateindex_count",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
quarterindex_to_monthindex_count: EagerVec::forced_import(
|
||||
&file,
|
||||
quarterindex_to_monthindex_count: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"monthindex_count",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
semesterindex_to_monthindex_count: EagerVec::forced_import(
|
||||
&file,
|
||||
semesterindex_to_monthindex_count: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"monthindex_count",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
yearindex_to_monthindex_count: EagerVec::forced_import(
|
||||
&file,
|
||||
yearindex_to_monthindex_count: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"monthindex_count",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
decadeindex_to_yearindex_count: EagerVec::forced_import(
|
||||
&file,
|
||||
decadeindex_to_yearindex_count: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"yearindex_count",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
outputindex_to_txindex: EagerVec::forced_import(
|
||||
&file,
|
||||
outputindex_to_txindex: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"txindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
|
||||
file,
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -582,7 +482,7 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<Indexes> {
|
||||
let idxs = self.compute_(indexer, starting_indexes, exit)?;
|
||||
self.file.flush_then_punch()?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(idxs)
|
||||
}
|
||||
|
||||
@@ -596,24 +496,6 @@ impl Vecs {
|
||||
// OutputIndex
|
||||
// ---
|
||||
|
||||
self.outputindex_to_outputindex.compute_if_necessary(
|
||||
starting_indexes.outputindex,
|
||||
&indexer.vecs.outputindex_to_value,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txindex_to_output_count.compute_if_necessary(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txindex_to_input_count.compute_if_necessary(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.outputindex_to_txindex.compute_inverse_less_to_more(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_first_outputindex,
|
||||
@@ -621,109 +503,10 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.p2pk33addressindex_to_p2pk33addressindex
|
||||
.compute_if_necessary(
|
||||
starting_indexes.p2pk33addressindex,
|
||||
&indexer.vecs.p2pk33addressindex_to_p2pk33bytes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.p2pk65addressindex_to_p2pk65addressindex
|
||||
.compute_if_necessary(
|
||||
starting_indexes.p2pk65addressindex,
|
||||
&indexer.vecs.p2pk65addressindex_to_p2pk65bytes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.p2pkhaddressindex_to_p2pkhaddressindex
|
||||
.compute_if_necessary(
|
||||
starting_indexes.p2pkhaddressindex,
|
||||
&indexer.vecs.p2pkhaddressindex_to_p2pkhbytes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.p2shaddressindex_to_p2shaddressindex
|
||||
.compute_if_necessary(
|
||||
starting_indexes.p2shaddressindex,
|
||||
&indexer.vecs.p2shaddressindex_to_p2shbytes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.p2traddressindex_to_p2traddressindex
|
||||
.compute_if_necessary(
|
||||
starting_indexes.p2traddressindex,
|
||||
&indexer.vecs.p2traddressindex_to_p2trbytes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.p2wpkhaddressindex_to_p2wpkhaddressindex
|
||||
.compute_if_necessary(
|
||||
starting_indexes.p2wpkhaddressindex,
|
||||
&indexer.vecs.p2wpkhaddressindex_to_p2wpkhbytes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.p2wshaddressindex_to_p2wshaddressindex
|
||||
.compute_if_necessary(
|
||||
starting_indexes.p2wshaddressindex,
|
||||
&indexer.vecs.p2wshaddressindex_to_p2wshbytes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.emptyoutputindex_to_emptyoutputindex
|
||||
.compute_if_necessary(
|
||||
starting_indexes.emptyoutputindex,
|
||||
&indexer.vecs.emptyoutputindex_to_txindex,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.p2msoutputindex_to_p2msoutputindex
|
||||
.compute_if_necessary(
|
||||
starting_indexes.p2msoutputindex,
|
||||
&indexer.vecs.p2msoutputindex_to_txindex,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.opreturnindex_to_opreturnindex.compute_if_necessary(
|
||||
starting_indexes.opreturnindex,
|
||||
&indexer.vecs.opreturnindex_to_txindex,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.p2aaddressindex_to_p2aaddressindex
|
||||
.compute_if_necessary(
|
||||
starting_indexes.p2aaddressindex,
|
||||
&indexer.vecs.p2aaddressindex_to_p2abytes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.unknownoutputindex_to_unknownoutputindex
|
||||
.compute_if_necessary(
|
||||
starting_indexes.unknownoutputindex,
|
||||
&indexer.vecs.unknownoutputindex_to_txindex,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// ---
|
||||
// InputIndex
|
||||
// ---
|
||||
|
||||
self.inputindex_to_inputindex.compute_if_necessary(
|
||||
starting_indexes.inputindex,
|
||||
&indexer.vecs.inputindex_to_outputindex,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// ---
|
||||
// TxIndex
|
||||
// ---
|
||||
|
||||
self.txindex_to_txindex.compute_if_necessary(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.height_to_txindex_count.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![doc = "\n## Example\n\n```rust"]
|
||||
#![doc = include_str!("../examples/computer.rs")]
|
||||
#![doc = "```"]
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
@@ -9,8 +6,8 @@ use brk_error::Result;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::Version;
|
||||
use brk_vecs::{AnyCollectableVec, Computation, Exit, Format};
|
||||
use log::info;
|
||||
use vecdb::{AnyCollectableVec, Exit, Format};
|
||||
|
||||
mod blocks;
|
||||
mod cointime;
|
||||
@@ -29,6 +26,7 @@ mod utils;
|
||||
|
||||
use indexes::Indexes;
|
||||
|
||||
pub use states::PriceToAmount;
|
||||
use states::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -45,7 +43,7 @@ pub struct Computer {
|
||||
pub cointime: cointime::Vecs,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE;
|
||||
const VERSION: Version = Version::TWO;
|
||||
|
||||
impl Computer {
|
||||
/// Do NOT import multiple times or things will break !!!
|
||||
@@ -56,84 +54,43 @@ impl Computer {
|
||||
) -> Result<Self> {
|
||||
let computed_path = outputs_path.join("computed");
|
||||
|
||||
let computation = Computation::Lazy;
|
||||
let format = Format::Compressed;
|
||||
|
||||
let indexes = indexes::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
indexer,
|
||||
computation,
|
||||
format,
|
||||
)?;
|
||||
let indexes =
|
||||
indexes::Vecs::forced_import(&computed_path, VERSION + Version::ZERO, indexer)?;
|
||||
|
||||
let fetched = fetcher.map(|fetcher| {
|
||||
fetched::Vecs::forced_import(outputs_path, fetcher, VERSION + Version::ZERO).unwrap()
|
||||
});
|
||||
|
||||
let price = fetched.is_some().then(|| {
|
||||
price::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
)
|
||||
.unwrap()
|
||||
price::Vecs::forced_import(&computed_path, VERSION + Version::ZERO, &indexes).unwrap()
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
blocks: blocks::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
)?,
|
||||
mining: mining::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
)?,
|
||||
blocks: blocks::Vecs::forced_import(&computed_path, VERSION + Version::ZERO, &indexes)?,
|
||||
mining: mining::Vecs::forced_import(&computed_path, VERSION + Version::ZERO, &indexes)?,
|
||||
constants: constants::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
)?,
|
||||
market: market::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
)?,
|
||||
market: market::Vecs::forced_import(&computed_path, VERSION + Version::ZERO, &indexes)?,
|
||||
stateful: stateful::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
Format::Compressed,
|
||||
&indexes,
|
||||
price.as_ref(),
|
||||
&computed_path.join("states"),
|
||||
)?,
|
||||
transactions: transactions::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
indexer,
|
||||
&indexes,
|
||||
computation,
|
||||
format,
|
||||
price.as_ref(),
|
||||
)?,
|
||||
cointime: cointime::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
&indexes,
|
||||
price.as_ref(),
|
||||
)?,
|
||||
|
||||
+145
-419
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
use std::{path::Path, sync::Arc};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{DifficultyEpoch, HalvingEpoch, StoredF64, Version};
|
||||
use brk_vecs::{AnyCollectableVec, Computation, Exit, File, Format, PAGE_SIZE, VecIterator};
|
||||
use vecdb::{AnyCollectableVec, Database, Exit, PAGE_SIZE, VecIterator};
|
||||
|
||||
use crate::grouped::Source;
|
||||
|
||||
@@ -17,7 +17,7 @@ const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
file: Arc<File>,
|
||||
db: Database,
|
||||
|
||||
pub indexes_to_difficulty: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_difficultyepoch: ComputedVecsFromDateIndex<DifficultyEpoch>,
|
||||
@@ -25,49 +25,37 @@ pub struct Vecs {
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
parent: &Path,
|
||||
version: Version,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let file = Arc::new(File::open(&parent.join("mining"))?);
|
||||
file.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
pub fn forced_import(parent: &Path, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
let db = Database::open(&parent.join("mining"))?;
|
||||
db.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
|
||||
Ok(Self {
|
||||
indexes_to_difficulty: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"difficulty",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_difficultyepoch: ComputedVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"difficultyepoch",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_halvingepoch: ComputedVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"halvingepoch",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
|
||||
file,
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -79,7 +67,7 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_(indexer, indexes, starting_indexes, exit)?;
|
||||
self.file.flush_then_punch()?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{path::Path, sync::Arc};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
@@ -6,9 +6,9 @@ use brk_structs::{
|
||||
Cents, Close, DateIndex, DecadeIndex, DifficultyEpoch, Dollars, Height, High, Low, MonthIndex,
|
||||
OHLCDollars, OHLCSats, Open, QuarterIndex, Sats, SemesterIndex, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use brk_vecs::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, Computation, EagerVec, Exit, File,
|
||||
Format, GenericStoredVec, PAGE_SIZE, RawVec,
|
||||
use vecdb::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, Database, EagerVec, Exit,
|
||||
GenericStoredVec, PAGE_SIZE, RawVec,
|
||||
};
|
||||
|
||||
use crate::{fetched, grouped::Source};
|
||||
@@ -21,7 +21,7 @@ use super::{
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
file: Arc<File>,
|
||||
db: Database,
|
||||
|
||||
pub dateindex_to_close_in_cents: EagerVec<DateIndex, Close<Cents>>,
|
||||
pub dateindex_to_high_in_cents: EagerVec<DateIndex, High<Cents>>,
|
||||
@@ -73,295 +73,253 @@ const VERSION: Version = Version::ZERO;
|
||||
const VERSION_IN_SATS: Version = Version::ZERO;
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
parent: &Path,
|
||||
version: Version,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let file = Arc::new(File::open(&parent.join("price"))?);
|
||||
file.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
pub fn forced_import(parent: &Path, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
let db = Database::open(&parent.join("price"))?;
|
||||
db.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
|
||||
Ok(Self {
|
||||
dateindex_to_ohlc: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
dateindex_to_ohlc_in_sats: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
)?,
|
||||
dateindex_to_close_in_cents: EagerVec::forced_import(
|
||||
&file,
|
||||
dateindex_to_close_in_cents: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"close_in_cents",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
dateindex_to_high_in_cents: EagerVec::forced_import(
|
||||
&file,
|
||||
dateindex_to_high_in_cents: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"high_in_cents",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
dateindex_to_low_in_cents: EagerVec::forced_import(
|
||||
&file,
|
||||
dateindex_to_low_in_cents: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"low_in_cents",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
dateindex_to_open_in_cents: EagerVec::forced_import(
|
||||
&file,
|
||||
dateindex_to_open_in_cents: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"open_in_cents",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_ohlc: RawVec::forced_import(
|
||||
&file,
|
||||
"ohlc",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_ohlc: RawVec::forced_import(&db, "ohlc", version + VERSION + Version::ZERO)?,
|
||||
height_to_ohlc_in_sats: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
)?,
|
||||
height_to_close_in_cents: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_close_in_cents: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"close_in_cents",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_high_in_cents: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_high_in_cents: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"high_in_cents",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_low_in_cents: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_low_in_cents: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"low_in_cents",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
height_to_open_in_cents: EagerVec::forced_import(
|
||||
&file,
|
||||
height_to_open_in_cents: EagerVec::forced_import_compressed(
|
||||
&db,
|
||||
"open_in_cents",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
timeindexes_to_open: ComputedVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"open",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_first(),
|
||||
)?,
|
||||
timeindexes_to_high: ComputedVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"high",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_max(),
|
||||
)?,
|
||||
timeindexes_to_low: ComputedVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"low",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_min(),
|
||||
)?,
|
||||
timeindexes_to_close: ComputedVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"close",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
timeindexes_to_open_in_sats: ComputedVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"open_in_sats",
|
||||
Source::Compute,
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_first(),
|
||||
)?,
|
||||
timeindexes_to_high_in_sats: ComputedVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"high_in_sats",
|
||||
Source::Compute,
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_max(),
|
||||
)?,
|
||||
timeindexes_to_low_in_sats: ComputedVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"low_in_sats",
|
||||
Source::Compute,
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_min(),
|
||||
)?,
|
||||
timeindexes_to_close_in_sats: ComputedVecsFromDateIndex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"close_in_sats",
|
||||
Source::Compute,
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
chainindexes_to_open: ComputedVecsFromHeightStrict::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"open",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
VecBuilderOptions::default().add_first(),
|
||||
)?,
|
||||
chainindexes_to_high: ComputedVecsFromHeightStrict::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"high",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
VecBuilderOptions::default().add_max(),
|
||||
)?,
|
||||
chainindexes_to_low: ComputedVecsFromHeightStrict::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"low",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
VecBuilderOptions::default().add_min(),
|
||||
)?,
|
||||
chainindexes_to_close: ComputedVecsFromHeightStrict::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"close",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
chainindexes_to_open_in_sats: ComputedVecsFromHeightStrict::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"open_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
format,
|
||||
VecBuilderOptions::default().add_first(),
|
||||
)?,
|
||||
chainindexes_to_high_in_sats: ComputedVecsFromHeightStrict::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"high_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
format,
|
||||
VecBuilderOptions::default().add_max(),
|
||||
)?,
|
||||
chainindexes_to_low_in_sats: ComputedVecsFromHeightStrict::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"low_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
format,
|
||||
VecBuilderOptions::default().add_min(),
|
||||
)?,
|
||||
chainindexes_to_close_in_sats: ComputedVecsFromHeightStrict::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"close_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
format,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
weekindex_to_ohlc: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
weekindex_to_ohlc_in_sats: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
)?,
|
||||
difficultyepoch_to_ohlc: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
difficultyepoch_to_ohlc_in_sats: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
)?,
|
||||
monthindex_to_ohlc: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
monthindex_to_ohlc_in_sats: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
)?,
|
||||
quarterindex_to_ohlc: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
quarterindex_to_ohlc_in_sats: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
)?,
|
||||
semesterindex_to_ohlc: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
semesterindex_to_ohlc_in_sats: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
)?,
|
||||
yearindex_to_ohlc: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
yearindex_to_ohlc_in_sats: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
)?,
|
||||
// halvingepoch_to_ohlc: StorableVec::forced_import(file,
|
||||
// halvingepoch_to_ohlc: StorableVec::forced_import(db,
|
||||
// "halvingepoch_to_ohlc"), version + VERSION + Version::ZERO, format)?,
|
||||
decadeindex_to_ohlc: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
decadeindex_to_ohlc_in_sats: RawVec::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"ohlc_in_sats",
|
||||
version + VERSION + VERSION_IN_SATS + Version::ZERO,
|
||||
)?,
|
||||
|
||||
file,
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -374,7 +332,7 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_(indexer, indexes, starting_indexes, fetched, exit)?;
|
||||
self.file.flush_then_punch()?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::{ops::Deref, path::Path, sync::Arc};
|
||||
use std::{ops::Deref, path::Path};
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, DateIndex, Dollars, Height, StoredU64, Version};
|
||||
use brk_vecs::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, Computation, EagerVec, Exit, File,
|
||||
Format, GenericStoredVec, VecIterator,
|
||||
use vecdb::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, Database, EagerVec, Exit, Format,
|
||||
GenericStoredVec, VecIterator,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -23,9 +23,9 @@ const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
starting_height: Height,
|
||||
starting_height: Option<Height>,
|
||||
|
||||
pub state: AddressCohortState,
|
||||
pub state: Option<AddressCohortState>,
|
||||
|
||||
pub inner: common::Vecs,
|
||||
|
||||
@@ -36,14 +36,13 @@ pub struct Vecs {
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
cohort_name: Option<&str>,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: &Path,
|
||||
states_path: Option<&Path>,
|
||||
compute_relative_to_all: bool,
|
||||
) -> Result<Self> {
|
||||
let compute_dollars = price.is_some();
|
||||
@@ -51,76 +50,78 @@ impl Vecs {
|
||||
let suffix = |s: &str| cohort_name.map_or(s.to_string(), |name| format!("{name}_{s}"));
|
||||
|
||||
Ok(Self {
|
||||
starting_height: Height::ZERO,
|
||||
state: AddressCohortState::default_and_import(
|
||||
states_path,
|
||||
cohort_name.unwrap_or_default(),
|
||||
compute_dollars,
|
||||
)?,
|
||||
starting_height: None,
|
||||
state: states_path.map(|states_path| {
|
||||
AddressCohortState::new(
|
||||
states_path,
|
||||
cohort_name.unwrap_or_default(),
|
||||
compute_dollars,
|
||||
)
|
||||
}),
|
||||
height_to_address_count: EagerVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
&suffix("address_count"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
indexes_to_address_count: ComputedVecsFromHeight::forced_import(
|
||||
file,
|
||||
db,
|
||||
&suffix("address_count"),
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
inner: common::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
cohort_name,
|
||||
computation,
|
||||
format,
|
||||
version,
|
||||
indexes,
|
||||
price,
|
||||
compute_relative_to_all,
|
||||
false,
|
||||
false,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for Vecs {
|
||||
fn starting_height(&self) -> Height {
|
||||
fn min_height_vecs_len(&self) -> usize {
|
||||
[
|
||||
self.state.height().map_or(Height::MAX, |h| h.incremented()),
|
||||
self.height_to_address_count.len().into(),
|
||||
self.inner.starting_height(),
|
||||
self.height_to_address_count.len(),
|
||||
self.inner.min_height_vecs_len(),
|
||||
]
|
||||
.into_iter()
|
||||
.min()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn init(&mut self, starting_height: Height) {
|
||||
if starting_height > self.starting_height() {
|
||||
unreachable!()
|
||||
}
|
||||
fn reset_state_starting_height(&mut self) {
|
||||
self.starting_height = Some(Height::ZERO);
|
||||
}
|
||||
|
||||
self.starting_height = starting_height;
|
||||
fn import_state(&mut self, starting_height: Height) -> Result<Height> {
|
||||
let starting_height = self
|
||||
.inner
|
||||
.import_state(starting_height, &mut self.state.as_mut().unwrap().inner)?;
|
||||
|
||||
self.starting_height = Some(starting_height);
|
||||
|
||||
if let Some(prev_height) = starting_height.decremented() {
|
||||
self.state.address_count = *self
|
||||
self.state.as_mut().unwrap().address_count = *self
|
||||
.height_to_address_count
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height);
|
||||
}
|
||||
|
||||
self.inner
|
||||
.init(&mut self.starting_height, &mut self.state.inner);
|
||||
Ok(starting_height)
|
||||
}
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.height_to_address_count
|
||||
.validate_computed_version_or_reset_file(
|
||||
.validate_computed_version_or_reset(
|
||||
base_version + self.height_to_address_count.inner_version(),
|
||||
)?;
|
||||
|
||||
@@ -128,17 +129,18 @@ impl DynCohortVecs for Vecs {
|
||||
}
|
||||
|
||||
fn forced_pushed_at(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
if self.starting_height > height {
|
||||
if self.starting_height.unwrap() > height {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.height_to_address_count.forced_push_at(
|
||||
height,
|
||||
self.state.address_count.into(),
|
||||
self.state.as_ref().unwrap().address_count.into(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.inner.forced_pushed_at(height, exit, &self.state.inner)
|
||||
self.inner
|
||||
.forced_pushed_at(height, exit, &self.state.as_ref().unwrap().inner)
|
||||
}
|
||||
|
||||
fn compute_then_force_push_unrealized_states(
|
||||
@@ -155,7 +157,7 @@ impl DynCohortVecs for Vecs {
|
||||
dateindex,
|
||||
date_price,
|
||||
exit,
|
||||
&self.state.inner,
|
||||
&self.state.as_ref().unwrap().inner,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -163,7 +165,7 @@ impl DynCohortVecs for Vecs {
|
||||
self.height_to_address_count.safe_flush(exit)?;
|
||||
|
||||
self.inner
|
||||
.safe_flush_stateful_vecs(height, exit, &mut self.state.inner)
|
||||
.safe_flush_stateful_vecs(height, exit, &mut self.state.as_mut().unwrap().inner)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{path::Path, sync::Arc};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
@@ -6,8 +6,8 @@ use brk_structs::{
|
||||
AddressGroups, Bitcoin, ByAmountRange, ByGreatEqualAmount, ByLowerThanAmount, DateIndex,
|
||||
Dollars, GroupFilter, Height, Version,
|
||||
};
|
||||
use brk_vecs::{AnyIterableVec, Computation, Exit, File, Format};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use vecdb::{AnyIterableVec, Database, Exit, Format};
|
||||
|
||||
use crate::{
|
||||
Indexes, indexes, market, price,
|
||||
@@ -24,9 +24,8 @@ pub struct Vecs(AddressGroups<(GroupFilter, address_cohort::Vecs)>);
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
version: Version,
|
||||
_computation: Computation,
|
||||
format: Format,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
@@ -36,458 +35,417 @@ impl Vecs {
|
||||
AddressGroups {
|
||||
amount_range: ByAmountRange {
|
||||
_0sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_with_0sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_1sat_to_10sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_1sat_under_10sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_10sats_to_100sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_10sats_under_100sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_100sats_to_1k_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_100sats_under_1k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_1k_sats_to_10k_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_1k_sats_under_10k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_10k_sats_to_100k_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_10k_sats_under_100k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_100k_sats_to_1m_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_100k_sats_under_1m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_1m_sats_to_10m_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_1m_sats_under_10m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_10m_sats_to_1btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_10m_sats_under_1btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_1btc_to_10btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_1btc_under_10btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_10btc_to_100btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_10btc_under_100btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_100btc_to_1k_btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_100btc_under_1k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_1k_btc_to_10k_btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_1k_btc_under_10k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_10k_btc_to_100k_btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_10k_btc_under_100k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
_100k_btc_or_more: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_100k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
Some(states_path),
|
||||
true,
|
||||
)?,
|
||||
},
|
||||
lt_amount: ByLowerThanAmount {
|
||||
_10sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_10sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_100sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1k_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_1k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10k_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_10k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100k_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_100k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1m_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_1m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10m_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_10m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_1btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_10btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_100btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1k_btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_1k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10k_btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_10k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100k_btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_under_100k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
},
|
||||
ge_amount: ByGreatEqualAmount {
|
||||
_1sat: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_1sat"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_10sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_100sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1k_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_1k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10k_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_10k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100k_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_100k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1m_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_1m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10m_sats: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_10m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_1btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_10btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_100btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_100btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_1k_btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_1k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
_10k_btc: address_cohort::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
Some("addrs_above_10k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
None,
|
||||
true,
|
||||
)?,
|
||||
},
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
use brk_structs::{ByAddressType, Height};
|
||||
use brk_vecs::VecIterator;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use vecdb::VecIterator;
|
||||
|
||||
use crate::stateful::addresstype_to_height_to_addresscount::AddressTypeToHeightToAddressCount;
|
||||
use super::AddressTypeToHeightToAddressCount;
|
||||
|
||||
#[derive(Debug, Default, Deref, DerefMut)]
|
||||
pub struct AddressTypeToAddressCount(ByAddressType<u64>);
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
use brk_error::Result;
|
||||
use brk_structs::{ByAddressType, Height, StoredU64};
|
||||
use brk_vecs::{EagerVec, Exit, GenericStoredVec};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use vecdb::{EagerVec, Exit, GenericStoredVec};
|
||||
|
||||
use crate::stateful::addresstype_to_addresscount::AddressTypeToAddressCount;
|
||||
use super::AddressTypeToAddressCount;
|
||||
|
||||
#[derive(Debug, Clone, Deref, DerefMut)]
|
||||
pub struct AddressTypeToHeightToAddressCount(ByAddressType<EagerVec<Height, StoredU64>>);
|
||||
+5
-7
@@ -1,12 +1,11 @@
|
||||
use brk_error::Result;
|
||||
use brk_structs::{ByAddressType, StoredU64};
|
||||
use brk_vecs::{AnyCollectableVec, Exit};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use vecdb::{AnyCollectableVec, Exit};
|
||||
|
||||
use crate::{
|
||||
Indexes, grouped::ComputedVecsFromHeight, indexes,
|
||||
stateful::addresstype_to_height_to_addresscount::AddressTypeToHeightToAddressCount,
|
||||
};
|
||||
use crate::{Indexes, grouped::ComputedVecsFromHeight, indexes};
|
||||
|
||||
use super::AddressTypeToHeightToAddressCount;
|
||||
|
||||
#[derive(Clone, Deref, DerefMut)]
|
||||
pub struct AddressTypeToIndexesToAddressCount(ByAddressType<ComputedVecsFromHeight<StoredU64>>);
|
||||
@@ -20,7 +19,6 @@ impl From<ByAddressType<ComputedVecsFromHeight<StoredU64>>> for AddressTypeToInd
|
||||
impl AddressTypeToIndexesToAddressCount {
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
// height: Height,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
@@ -76,7 +74,7 @@ impl AddressTypeToIndexesToAddressCount {
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
//
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
self.0
|
||||
.as_typed_vec()
|
||||
@@ -0,0 +1,13 @@
|
||||
mod addresscount;
|
||||
mod height_to_addresscount;
|
||||
mod height_to_vec;
|
||||
mod indexes_to_addresscount;
|
||||
mod typeindex_tree;
|
||||
mod vec;
|
||||
|
||||
pub use addresscount::*;
|
||||
pub use height_to_addresscount::*;
|
||||
pub use height_to_vec::*;
|
||||
pub use indexes_to_addresscount::*;
|
||||
pub use typeindex_tree::*;
|
||||
pub use vec::*;
|
||||
+1
-3
@@ -1,10 +1,8 @@
|
||||
use std::{collections::BTreeMap, mem};
|
||||
|
||||
use brk_structs::TypeIndex;
|
||||
use brk_structs::{ByAddressType, TypeIndex};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use super::ByAddressType;
|
||||
|
||||
#[derive(Debug, Deref, DerefMut)]
|
||||
pub struct AddressTypeToTypeIndexTree<T>(ByAddressType<BTreeMap<TypeIndex, T>>);
|
||||
|
||||
+1
-2
@@ -1,9 +1,8 @@
|
||||
use std::mem;
|
||||
|
||||
use brk_structs::ByAddressType;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use super::ByAddressType;
|
||||
|
||||
#[derive(Debug, Deref, DerefMut)]
|
||||
pub struct AddressTypeToVec<T>(ByAddressType<Vec<T>>);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use brk_vecs::{CompressedVec, RawVec, StoredCompressed, StoredIndex, StoredRaw};
|
||||
use vecdb::{CompressedVec, RawVec, StoredCompressed, StoredIndex, StoredRaw};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RangeMap<I, T>(BTreeMap<I, T>);
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, DateIndex, Dollars, Height, Version};
|
||||
use brk_vecs::{AnyCollectableVec, AnyIterableVec, Exit};
|
||||
use vecdb::{AnyCollectableVec, AnyIterableVec, Exit};
|
||||
|
||||
use crate::{Indexes, indexes, market, price};
|
||||
|
||||
pub trait DynCohortVecs: Send + Sync {
|
||||
fn starting_height(&self) -> Height;
|
||||
fn min_height_vecs_len(&self) -> usize;
|
||||
fn reset_state_starting_height(&mut self);
|
||||
|
||||
fn init(&mut self, starting_height: Height);
|
||||
fn import_state(&mut self, starting_height: Height) -> Result<Height>;
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()>;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::{ops::Deref, path::Path, sync::Arc};
|
||||
use std::{ops::Deref, path::Path};
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::{Bitcoin, DateIndex, Dollars, Height, Version};
|
||||
use brk_vecs::{AnyCollectableVec, AnyIterableVec, Computation, Exit, File, Format};
|
||||
use vecdb::{AnyCollectableVec, AnyIterableVec, Database, Exit, Format};
|
||||
|
||||
use crate::{
|
||||
Indexes, UTXOCohortState, indexes, market, price,
|
||||
@@ -15,9 +15,9 @@ use crate::{
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
starting_height: Height,
|
||||
state_starting_height: Option<Height>,
|
||||
|
||||
pub state: UTXOCohortState,
|
||||
pub state: Option<UTXOCohortState>,
|
||||
|
||||
inner: common::Vecs,
|
||||
}
|
||||
@@ -25,62 +25,62 @@ pub struct Vecs {
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
file: &Arc<File>,
|
||||
db: &Database,
|
||||
cohort_name: Option<&str>,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: &Path,
|
||||
states_path: Option<&Path>,
|
||||
compute_relative_to_all: bool,
|
||||
ratio_extended: bool,
|
||||
compute_adjusted: bool,
|
||||
) -> Result<Self> {
|
||||
let compute_dollars = price.is_some();
|
||||
|
||||
Ok(Self {
|
||||
starting_height: Height::ZERO,
|
||||
state_starting_height: None,
|
||||
|
||||
state: UTXOCohortState::default_and_import(
|
||||
states_path,
|
||||
cohort_name.unwrap_or_default(),
|
||||
compute_dollars,
|
||||
)?,
|
||||
state: states_path.map(|states_path| {
|
||||
UTXOCohortState::new(
|
||||
states_path,
|
||||
cohort_name.unwrap_or_default(),
|
||||
compute_dollars,
|
||||
)
|
||||
}),
|
||||
|
||||
inner: common::Vecs::forced_import(
|
||||
file,
|
||||
db,
|
||||
cohort_name,
|
||||
computation,
|
||||
format,
|
||||
version,
|
||||
indexes,
|
||||
price,
|
||||
compute_relative_to_all,
|
||||
ratio_extended,
|
||||
compute_adjusted,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for Vecs {
|
||||
fn starting_height(&self) -> Height {
|
||||
[
|
||||
self.state.height().map_or(Height::MAX, |h| h.incremented()),
|
||||
self.inner.starting_height(),
|
||||
]
|
||||
.into_iter()
|
||||
.min()
|
||||
.unwrap()
|
||||
fn min_height_vecs_len(&self) -> usize {
|
||||
self.inner.min_height_vecs_len()
|
||||
}
|
||||
|
||||
fn init(&mut self, starting_height: Height) {
|
||||
if starting_height > self.starting_height() {
|
||||
unreachable!()
|
||||
}
|
||||
fn reset_state_starting_height(&mut self) {
|
||||
self.state_starting_height = Some(Height::ZERO);
|
||||
}
|
||||
|
||||
self.starting_height = starting_height;
|
||||
fn import_state(&mut self, starting_height: Height) -> Result<Height> {
|
||||
let starting_height = self
|
||||
.inner
|
||||
.import_state(starting_height, self.state.as_mut().unwrap())?;
|
||||
|
||||
self.inner.init(&mut self.starting_height, &mut self.state);
|
||||
self.state_starting_height = Some(starting_height);
|
||||
|
||||
Ok(starting_height)
|
||||
}
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
@@ -88,11 +88,12 @@ impl DynCohortVecs for Vecs {
|
||||
}
|
||||
|
||||
fn forced_pushed_at(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
if self.starting_height > height {
|
||||
if self.state_starting_height.unwrap() > height {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.inner.forced_pushed_at(height, exit, &self.state)
|
||||
self.inner
|
||||
.forced_pushed_at(height, exit, self.state.as_ref().unwrap())
|
||||
}
|
||||
|
||||
fn compute_then_force_push_unrealized_states(
|
||||
@@ -109,13 +110,13 @@ impl DynCohortVecs for Vecs {
|
||||
dateindex,
|
||||
date_price,
|
||||
exit,
|
||||
&self.state,
|
||||
self.state.as_mut().unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
self.inner
|
||||
.safe_flush_stateful_vecs(height, exit, &mut self.state)
|
||||
.safe_flush_stateful_vecs(height, exit, self.state.as_mut().unwrap())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,17 @@
|
||||
use std::ops::{Add, AddAssign, SubAssign};
|
||||
|
||||
use brk_structs::{Dollars, Timestamp};
|
||||
use serde::Serialize;
|
||||
|
||||
use super::SupplyState;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct BlockState {
|
||||
#[serde(flatten)]
|
||||
pub supply: SupplyState,
|
||||
#[serde(skip)]
|
||||
pub price: Option<Dollars>,
|
||||
#[serde(skip)]
|
||||
pub timestamp: Timestamp,
|
||||
}
|
||||
|
||||
|
||||
@@ -14,19 +14,15 @@ pub struct AddressCohortState {
|
||||
}
|
||||
|
||||
impl AddressCohortState {
|
||||
pub fn default_and_import(path: &Path, name: &str, compute_dollars: bool) -> Result<Self> {
|
||||
Ok(Self {
|
||||
pub fn new(path: &Path, name: &str, compute_dollars: bool) -> Self {
|
||||
Self {
|
||||
address_count: 0,
|
||||
inner: CohortState::default_and_import(path, name, compute_dollars)?,
|
||||
})
|
||||
inner: CohortState::new(path, name, compute_dollars),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<Height> {
|
||||
self.inner.height()
|
||||
}
|
||||
|
||||
pub fn reset_price_to_amount(&mut self) -> Result<()> {
|
||||
self.inner.reset_price_to_amount()
|
||||
pub fn reset_price_to_amount_if_needed(&mut self) -> Result<()> {
|
||||
self.inner.reset_price_to_amount_if_needed()
|
||||
}
|
||||
|
||||
pub fn reset_single_iteration_values(&mut self) {
|
||||
|
||||
@@ -8,38 +8,47 @@ use crate::{PriceToAmount, RealizedState, SupplyState, UnrealizedState};
|
||||
#[derive(Clone)]
|
||||
pub struct CohortState {
|
||||
pub supply: SupplyState,
|
||||
|
||||
pub realized: Option<RealizedState>,
|
||||
pub satblocks_destroyed: Sats,
|
||||
pub satdays_destroyed: Sats,
|
||||
|
||||
price_to_amount: PriceToAmount,
|
||||
price_to_amount: Option<PriceToAmount>,
|
||||
}
|
||||
|
||||
impl CohortState {
|
||||
pub fn default_and_import(path: &Path, name: &str, compute_dollars: bool) -> Result<Self> {
|
||||
Ok(Self {
|
||||
pub fn new(path: &Path, name: &str, compute_dollars: bool) -> Self {
|
||||
Self {
|
||||
supply: SupplyState::default(),
|
||||
realized: compute_dollars.then_some(RealizedState::NAN),
|
||||
satblocks_destroyed: Sats::ZERO,
|
||||
satdays_destroyed: Sats::ZERO,
|
||||
price_to_amount: PriceToAmount::forced_import(path, name),
|
||||
})
|
||||
price_to_amount: compute_dollars.then_some(PriceToAmount::create(path, name)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<Height> {
|
||||
self.price_to_amount.height()
|
||||
pub fn import_at_or_before(&mut self, height: Height) -> Result<Height> {
|
||||
if let Some(price_to_amount) = self.price_to_amount.as_mut() {
|
||||
price_to_amount.import_at_or_before(height)
|
||||
} else {
|
||||
Ok(height)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_price_to_amount(&mut self) -> Result<()> {
|
||||
self.price_to_amount.reset()
|
||||
pub fn reset_price_to_amount_if_needed(&mut self) -> Result<()> {
|
||||
if let Some(price_to_amount) = self.price_to_amount.as_mut() {
|
||||
price_to_amount.clean()?;
|
||||
price_to_amount.init();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn price_to_amount_first_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.price_to_amount.first_key_value()
|
||||
self.price_to_amount.as_ref().unwrap().first_key_value()
|
||||
}
|
||||
|
||||
pub fn price_to_amount_last_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.price_to_amount.last_key_value()
|
||||
self.price_to_amount.as_ref().unwrap().last_key_value()
|
||||
}
|
||||
|
||||
pub fn reset_single_iteration_values(&mut self) {
|
||||
@@ -58,7 +67,10 @@ impl CohortState {
|
||||
{
|
||||
let price = price.unwrap();
|
||||
realized.increment(supply_state, price);
|
||||
self.price_to_amount.increment(price, supply_state);
|
||||
self.price_to_amount
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.increment(price, supply_state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +86,10 @@ impl CohortState {
|
||||
&& let Some(realized) = self.realized.as_mut()
|
||||
{
|
||||
realized.increment_(realized_cap);
|
||||
self.price_to_amount.increment(realized_price, supply_state);
|
||||
self.price_to_amount
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.increment(realized_price, supply_state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +101,10 @@ impl CohortState {
|
||||
{
|
||||
let price = price.unwrap();
|
||||
realized.decrement(supply_state, price);
|
||||
self.price_to_amount.decrement(price, supply_state);
|
||||
self.price_to_amount
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.decrement(price, supply_state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +120,10 @@ impl CohortState {
|
||||
&& let Some(realized) = self.realized.as_mut()
|
||||
{
|
||||
realized.decrement_(realized_cap);
|
||||
self.price_to_amount.decrement(realized_price, supply_state);
|
||||
self.price_to_amount
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.decrement(realized_price, supply_state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,13 +154,19 @@ impl CohortState {
|
||||
if let Some((price, supply)) = price_to_amount_increment
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.increment(price, supply);
|
||||
self.price_to_amount
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.increment(price, supply);
|
||||
}
|
||||
|
||||
if let Some((price, supply)) = price_to_amount_decrement
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.decrement(price, supply);
|
||||
self.price_to_amount
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.decrement(price, supply);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,12 +223,18 @@ impl CohortState {
|
||||
if let Some((price, supply)) = price_to_amount_increment
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.increment(price, supply);
|
||||
self.price_to_amount
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.increment(price, supply);
|
||||
}
|
||||
if let Some((price, supply)) = price_to_amount_decrement
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.decrement(price, supply);
|
||||
self.price_to_amount
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.decrement(price, supply);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +245,7 @@ impl CohortState {
|
||||
height_price: Dollars,
|
||||
date_price: Option<Dollars>,
|
||||
) -> (UnrealizedState, Option<UnrealizedState>) {
|
||||
if self.price_to_amount.is_empty() {
|
||||
if self.price_to_amount.as_ref().unwrap().is_empty() {
|
||||
return (
|
||||
UnrealizedState::NAN,
|
||||
date_price.map(|_| UnrealizedState::NAN),
|
||||
@@ -255,23 +288,30 @@ impl CohortState {
|
||||
}
|
||||
};
|
||||
|
||||
self.price_to_amount.iter().for_each(|(&price, &sats)| {
|
||||
update_state(price, height_price, sats, &mut height_unrealized_state);
|
||||
self.price_to_amount
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.for_each(|(&price, &sats)| {
|
||||
update_state(price, height_price, sats, &mut height_unrealized_state);
|
||||
|
||||
if let Some(date_price) = date_price {
|
||||
update_state(
|
||||
price,
|
||||
date_price,
|
||||
sats,
|
||||
date_unrealized_state.as_mut().unwrap(),
|
||||
)
|
||||
}
|
||||
});
|
||||
if let Some(date_price) = date_price {
|
||||
update_state(
|
||||
price,
|
||||
date_price,
|
||||
sats,
|
||||
date_unrealized_state.as_mut().unwrap(),
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
(height_unrealized_state, date_unrealized_state)
|
||||
}
|
||||
|
||||
pub fn commit(&mut self, height: Height) -> Result<()> {
|
||||
self.price_to_amount.flush(height)
|
||||
if let Some(price_to_amount) = self.price_to_amount.as_mut() {
|
||||
price_to_amount.flush(height)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_structs::Height;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use super::CohortState;
|
||||
@@ -10,19 +9,11 @@ use super::CohortState;
|
||||
pub struct UTXOCohortState(CohortState);
|
||||
|
||||
impl UTXOCohortState {
|
||||
pub fn default_and_import(path: &Path, name: &str, compute_dollars: bool) -> Result<Self> {
|
||||
Ok(Self(CohortState::default_and_import(
|
||||
path,
|
||||
name,
|
||||
compute_dollars,
|
||||
)?))
|
||||
pub fn new(path: &Path, name: &str, compute_dollars: bool) -> Self {
|
||||
Self(CohortState::new(path, name, compute_dollars))
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<Height> {
|
||||
self.0.height()
|
||||
}
|
||||
|
||||
pub fn reset_price_to_amount(&mut self) -> Result<()> {
|
||||
self.0.reset_price_to_amount()
|
||||
pub fn reset_price_to_amount_if_needed(&mut self) -> Result<()> {
|
||||
self.0.reset_price_to_amount_if_needed()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs,
|
||||
io::{Cursor, Read},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use brk_error::{Error, Result};
|
||||
use brk_structs::{Dollars, Height, Sats};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use pco::standalone::{simple_decompress, simpler_compress};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zerocopy::{FromBytes, IntoBytes};
|
||||
|
||||
@@ -16,144 +16,155 @@ use crate::states::SupplyState;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PriceToAmount {
|
||||
pathbuf: PathBuf,
|
||||
height: Option<Height>,
|
||||
state: State,
|
||||
state: Option<State>,
|
||||
}
|
||||
|
||||
const STATE_AT_: &str = "state_at_";
|
||||
const STATE_TO_KEEP: usize = 10;
|
||||
|
||||
impl PriceToAmount {
|
||||
pub fn forced_import(path: &Path, name: &str) -> Self {
|
||||
Self::import(path, name).unwrap_or_else(|_| {
|
||||
// dbg!(e);
|
||||
Self {
|
||||
pathbuf: Self::path_(path, name),
|
||||
height: None,
|
||||
state: State::default(),
|
||||
}
|
||||
})
|
||||
pub fn create(path: &Path, name: &str) -> Self {
|
||||
Self {
|
||||
pathbuf: path.join(format!("{name}_price_to_amount")),
|
||||
state: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import(path: &Path, name: &str) -> Result<Self> {
|
||||
let path = Self::path_(path, name);
|
||||
fs::create_dir_all(&path)?;
|
||||
|
||||
let state = State::deserialize(&fs::read(Self::path_state_(&path))?)?;
|
||||
|
||||
Ok(Self {
|
||||
height: Height::try_from(Self::path_height_(&path).as_path()).ok(),
|
||||
pathbuf: path,
|
||||
state,
|
||||
})
|
||||
pub fn import_at_or_before(&mut self, height: Height) -> Result<Height> {
|
||||
let files = self.read_dir(None)?;
|
||||
let (&height, path) = files
|
||||
.range(..=height)
|
||||
.next_back()
|
||||
.ok_or(Error::Str("Not found"))?;
|
||||
self.state = Some(State::deserialize(&fs::read(path)?)?);
|
||||
Ok(height)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Dollars, &Sats)> {
|
||||
self.state.iter()
|
||||
self.state.as_ref().unwrap().iter()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.state.is_empty()
|
||||
self.state.as_ref().unwrap().is_empty()
|
||||
}
|
||||
|
||||
pub fn first_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.state.first_key_value()
|
||||
self.state.as_ref().unwrap().first_key_value()
|
||||
}
|
||||
|
||||
pub fn last_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.state.last_key_value()
|
||||
self.state.as_ref().unwrap().last_key_value()
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, price: Dollars, supply_state: &SupplyState) {
|
||||
*self.state.entry(price).or_default() += supply_state.value;
|
||||
*self.state.as_mut().unwrap().entry(price).or_default() += supply_state.value;
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, price: Dollars, supply_state: &SupplyState) {
|
||||
if let Some(amount) = self.state.get_mut(&price) {
|
||||
if let Some(amount) = self.state.as_mut().unwrap().get_mut(&price) {
|
||||
*amount -= supply_state.value;
|
||||
if *amount == Sats::ZERO {
|
||||
self.state.remove(&price);
|
||||
self.state.as_mut().unwrap().remove(&price);
|
||||
}
|
||||
} else {
|
||||
dbg!(&self.state, price, &self.pathbuf);
|
||||
dbg!(price, &self.pathbuf);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Result<()> {
|
||||
self.state.clear();
|
||||
self.height = None;
|
||||
fs::remove_dir_all(&self.pathbuf)?;
|
||||
pub fn init(&mut self) {
|
||||
self.state.replace(State::default());
|
||||
}
|
||||
|
||||
pub fn clean(&mut self) -> Result<()> {
|
||||
let _ = fs::remove_dir_all(&self.pathbuf);
|
||||
fs::create_dir_all(&self.pathbuf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_dir(&self, keep_only_before: Option<Height>) -> Result<BTreeMap<Height, PathBuf>> {
|
||||
Ok(fs::read_dir(&self.pathbuf)?
|
||||
.filter_map(|entry| {
|
||||
let path = entry.ok()?.path();
|
||||
let name = path.file_name()?.to_str()?;
|
||||
let height_str = name.strip_prefix(STATE_AT_).unwrap_or(name);
|
||||
if let Ok(h) = height_str.parse::<u32>().map(Height::from) {
|
||||
if keep_only_before.is_none_or(|height| h < height) {
|
||||
Some((h, path))
|
||||
} else {
|
||||
let _ = fs::remove_file(path);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<BTreeMap<Height, PathBuf>>())
|
||||
}
|
||||
|
||||
pub fn flush(&mut self, height: Height) -> Result<()> {
|
||||
self.height = Some(height);
|
||||
height.write(&self.path_height())?;
|
||||
fs::write(self.path_state(), self.state.serialize())?;
|
||||
let files = self.read_dir(Some(height))?;
|
||||
|
||||
for (_, path) in files
|
||||
.iter()
|
||||
.take(files.len().saturating_sub(STATE_TO_KEEP - 1))
|
||||
{
|
||||
fs::remove_file(path)?;
|
||||
}
|
||||
|
||||
fs::write(
|
||||
self.path_state(height),
|
||||
self.state.as_ref().unwrap().serialize()?,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<Height> {
|
||||
self.height
|
||||
fn path_state(&self, height: Height) -> PathBuf {
|
||||
Self::path_state_(&self.pathbuf, height)
|
||||
}
|
||||
|
||||
fn path_(path: &Path, name: &str) -> PathBuf {
|
||||
path.join(format!("{name}_price_to_amount"))
|
||||
}
|
||||
|
||||
fn path_state(&self) -> PathBuf {
|
||||
Self::path_state_(&self.pathbuf)
|
||||
}
|
||||
fn path_state_(path: &Path) -> PathBuf {
|
||||
path.join("state")
|
||||
}
|
||||
|
||||
fn path_height(&self) -> PathBuf {
|
||||
Self::path_height_(&self.pathbuf)
|
||||
}
|
||||
fn path_height_(path: &Path) -> PathBuf {
|
||||
path.join("height")
|
||||
fn path_state_(path: &Path, height: Height) -> PathBuf {
|
||||
path.join(u32::from(height).to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Deref, DerefMut, Serialize, Deserialize)]
|
||||
struct State(BTreeMap<Dollars, Sats>);
|
||||
|
||||
const COMPRESSION_LEVEL: usize = 4;
|
||||
|
||||
impl State {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let len = self.len();
|
||||
fn serialize(&self) -> vecdb::Result<Vec<u8>> {
|
||||
let keys: Vec<f64> = self.keys().cloned().map(f64::from).collect();
|
||||
|
||||
let mut buffer = Vec::with_capacity(8 + len * 16);
|
||||
let values: Vec<u64> = self.values().cloned().map(u64::from).collect();
|
||||
|
||||
buffer.extend(len.as_bytes());
|
||||
let compressed_keys = simpler_compress(&keys, COMPRESSION_LEVEL)?;
|
||||
let compressed_values = simpler_compress(&values, COMPRESSION_LEVEL)?;
|
||||
|
||||
self.iter().for_each(|(key, value)| {
|
||||
buffer.extend(key.as_bytes());
|
||||
buffer.extend(value.as_bytes());
|
||||
});
|
||||
let mut buffer = Vec::new();
|
||||
buffer.extend(keys.len().as_bytes());
|
||||
buffer.extend(compressed_keys.len().as_bytes());
|
||||
buffer.extend(compressed_keys);
|
||||
buffer.extend(compressed_values);
|
||||
|
||||
buffer
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
fn deserialize(data: &[u8]) -> Result<Self> {
|
||||
let mut cursor = Cursor::new(data);
|
||||
let mut buffer = [0u8; 8];
|
||||
fn deserialize(data: &[u8]) -> vecdb::Result<Self> {
|
||||
let entry_count = usize::read_from_bytes(&data[0..8])?;
|
||||
let keys_len = usize::read_from_bytes(&data[8..16])?;
|
||||
|
||||
cursor
|
||||
.read_exact(&mut buffer)
|
||||
.map_err(|_| Error::Str("Failed to read entry count"))?;
|
||||
let entry_count = usize::read_from_bytes(&buffer)?;
|
||||
let keys: Vec<f64> = simple_decompress(&data[16..16 + keys_len])?;
|
||||
let values: Vec<u64> = simple_decompress(&data[16 + keys_len..])?;
|
||||
|
||||
let mut map = BTreeMap::new();
|
||||
let map: BTreeMap<Dollars, Sats> = keys
|
||||
.into_iter()
|
||||
.zip(values)
|
||||
.map(|(k, v)| (Dollars::from(k), Sats::from(v)))
|
||||
.collect();
|
||||
|
||||
for _ in 0..entry_count {
|
||||
cursor.read_exact(&mut buffer)?;
|
||||
let key = Dollars::read_from_bytes(&buffer)?;
|
||||
|
||||
cursor.read_exact(&mut buffer)?;
|
||||
let value = Sats::read_from_bytes(&buffer)?;
|
||||
|
||||
map.insert(key, value);
|
||||
}
|
||||
assert_eq!(map.len(), entry_count);
|
||||
|
||||
Ok(Self(map))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_error::Result;
|
||||
use brk_structs::{Bitcoin, CheckedSub, Close, Date, DateIndex, Dollars, Sats, StoredF32};
|
||||
use brk_vecs::{
|
||||
use vecdb::{
|
||||
AnyIterableVec, AnyStoredVec, AnyVec, EagerVec, Exit, GenericStoredVec, StoredIndex,
|
||||
VecIterator, Version,
|
||||
};
|
||||
@@ -32,7 +32,7 @@ impl ComputeDCAStackViaLen for EagerVec<DateIndex, Sats> {
|
||||
len: usize,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset_file(
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + closes.version(),
|
||||
)?;
|
||||
|
||||
@@ -83,7 +83,7 @@ impl ComputeDCAStackViaLen for EagerVec<DateIndex, Sats> {
|
||||
from: DateIndex,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset_file(
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + closes.version(),
|
||||
)?;
|
||||
|
||||
@@ -143,7 +143,7 @@ impl ComputeDCAAveragePriceViaLen for EagerVec<DateIndex, Dollars> {
|
||||
len: usize,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset_file(
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ONE + self.inner_version() + stacks.version(),
|
||||
)?;
|
||||
|
||||
@@ -176,7 +176,7 @@ impl ComputeDCAAveragePriceViaLen for EagerVec<DateIndex, Dollars> {
|
||||
from: DateIndex,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset_file(
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + stacks.version(),
|
||||
)?;
|
||||
|
||||
@@ -218,7 +218,7 @@ where
|
||||
sats: &impl AnyIterableVec<I, Sats>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset_file(
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + sats.version(),
|
||||
)?;
|
||||
|
||||
@@ -254,7 +254,7 @@ where
|
||||
price: &impl AnyIterableVec<I, Close<Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset_file(
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + bitcoin.version(),
|
||||
)?;
|
||||
|
||||
@@ -292,7 +292,7 @@ where
|
||||
ath: &impl AnyIterableVec<I, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset_file(
|
||||
self.validate_computed_version_or_reset(
|
||||
Version::ZERO + self.inner_version() + ath.version() + close.version(),
|
||||
)?;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{path::Path, sync::Arc};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
@@ -6,10 +6,9 @@ use brk_structs::{
|
||||
CheckedSub, Feerate, HalvingEpoch, Height, InputIndex, OutputIndex, Sats, StoredBool,
|
||||
StoredU32, StoredU64, TxIndex, TxVersion, Version, Weight,
|
||||
};
|
||||
use brk_vecs::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, AnyIterableVec, Computation, ComputedVec,
|
||||
ComputedVecFrom1, ComputedVecFrom2, ComputedVecFrom3, Exit, File, Format, PAGE_SIZE,
|
||||
StoredIndex, VecIterator,
|
||||
use vecdb::{
|
||||
AnyCloneableIterableVec, AnyCollectableVec, AnyIterableVec, Database, EagerVec, Exit,
|
||||
LazyVecFrom1, LazyVecFrom2, LazyVecFrom3, PAGE_SIZE, StoredIndex, VecIterator,
|
||||
};
|
||||
|
||||
use crate::grouped::{
|
||||
@@ -23,7 +22,7 @@ const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
file: Arc<File>,
|
||||
db: Database,
|
||||
|
||||
// pub txindex_to_is_v1: LazyVec<Txindex, bool>,
|
||||
// pub txindex_to_is_v2: LazyVec<Txindex, bool>,
|
||||
@@ -34,19 +33,11 @@ pub struct Vecs {
|
||||
pub indexes_to_feerate: ComputedVecsFromTxindex<Feerate>,
|
||||
/// Value == 0 when Coinbase
|
||||
pub txindex_to_input_value:
|
||||
ComputedVecFrom3<TxIndex, Sats, TxIndex, InputIndex, TxIndex, StoredU64, InputIndex, Sats>,
|
||||
LazyVecFrom3<TxIndex, Sats, TxIndex, InputIndex, TxIndex, StoredU64, InputIndex, Sats>,
|
||||
// pub indexes_to_input_value: ComputedVecsFromTxindex<Sats>,
|
||||
pub indexes_to_opreturn_count: ComputedVecsFromHeight<StoredU64>,
|
||||
pub txindex_to_output_value: ComputedVecFrom3<
|
||||
TxIndex,
|
||||
Sats,
|
||||
TxIndex,
|
||||
OutputIndex,
|
||||
TxIndex,
|
||||
StoredU64,
|
||||
OutputIndex,
|
||||
Sats,
|
||||
>,
|
||||
pub txindex_to_output_value:
|
||||
LazyVecFrom3<TxIndex, Sats, TxIndex, OutputIndex, TxIndex, StoredU64, OutputIndex, Sats>,
|
||||
// pub indexes_to_output_value: ComputedVecsFromTxindex<Sats>,
|
||||
pub indexes_to_p2a_count: ComputedVecsFromHeight<StoredU64>,
|
||||
pub indexes_to_p2ms_count: ComputedVecsFromHeight<StoredU64>,
|
||||
@@ -67,16 +58,14 @@ pub struct Vecs {
|
||||
pub indexes_to_tx_weight: ComputedVecsFromTxindex<Weight>,
|
||||
pub indexes_to_unknownoutput_count: ComputedVecsFromHeight<StoredU64>,
|
||||
pub inputindex_to_value:
|
||||
ComputedVecFrom2<InputIndex, Sats, InputIndex, OutputIndex, OutputIndex, Sats>,
|
||||
LazyVecFrom2<InputIndex, Sats, InputIndex, OutputIndex, OutputIndex, Sats>,
|
||||
pub indexes_to_input_count: ComputedVecsFromTxindex<StoredU64>,
|
||||
pub txindex_to_is_coinbase:
|
||||
ComputedVecFrom2<TxIndex, StoredBool, TxIndex, Height, Height, TxIndex>,
|
||||
pub txindex_to_is_coinbase: LazyVecFrom2<TxIndex, StoredBool, TxIndex, Height, Height, TxIndex>,
|
||||
pub indexes_to_output_count: ComputedVecsFromTxindex<StoredU64>,
|
||||
pub txindex_to_vsize: ComputedVecFrom1<TxIndex, StoredU64, TxIndex, Weight>,
|
||||
pub txindex_to_weight:
|
||||
ComputedVecFrom2<TxIndex, Weight, TxIndex, StoredU32, TxIndex, StoredU32>,
|
||||
pub txindex_to_fee: ComputedVecFrom2<TxIndex, Sats, TxIndex, Sats, TxIndex, Sats>,
|
||||
pub txindex_to_feerate: ComputedVecFrom2<TxIndex, Feerate, TxIndex, Sats, TxIndex, StoredU64>,
|
||||
pub txindex_to_vsize: LazyVecFrom1<TxIndex, StoredU64, TxIndex, Weight>,
|
||||
pub txindex_to_weight: LazyVecFrom2<TxIndex, Weight, TxIndex, StoredU32, TxIndex, StoredU32>,
|
||||
pub txindex_to_fee: EagerVec<TxIndex, Sats>,
|
||||
pub txindex_to_feerate: EagerVec<TxIndex, Feerate>,
|
||||
pub indexes_to_exact_utxo_count: ComputedVecsFromHeight<StoredU64>,
|
||||
}
|
||||
|
||||
@@ -86,21 +75,16 @@ impl Vecs {
|
||||
version: Version,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
price: Option<&price::Vecs>,
|
||||
) -> Result<Self> {
|
||||
let file = Arc::new(File::open(&parent.join("transactions"))?);
|
||||
file.set_min_len(PAGE_SIZE * 10_000_000)?;
|
||||
let db = Database::open(&parent.join("transactions"))?;
|
||||
db.set_min_len(PAGE_SIZE * 10_000_000)?;
|
||||
|
||||
let compute_dollars = price.is_some();
|
||||
|
||||
let inputindex_to_value = ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
&file,
|
||||
let inputindex_to_value = LazyVecFrom2::init(
|
||||
"value",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.inputindex_to_outputindex.boxed_clone(),
|
||||
indexer.vecs.outputindex_to_value.boxed_clone(),
|
||||
|index: InputIndex, inputindex_to_outputindex_iter, outputindex_to_value_iter| {
|
||||
@@ -120,14 +104,11 @@ impl Vecs {
|
||||
}
|
||||
})
|
||||
},
|
||||
)?;
|
||||
);
|
||||
|
||||
let txindex_to_weight = ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
&file,
|
||||
let txindex_to_weight = LazyVecFrom2::init(
|
||||
"weight",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.txindex_to_base_size.boxed_clone(),
|
||||
indexer.vecs.txindex_to_total_size.boxed_clone(),
|
||||
|index: TxIndex, txindex_to_base_size_iter, txindex_to_total_size_iter| {
|
||||
@@ -148,14 +129,11 @@ impl Vecs {
|
||||
Weight::from(bitcoin::Weight::from_wu_usize(wu))
|
||||
})
|
||||
},
|
||||
)?;
|
||||
);
|
||||
|
||||
let txindex_to_vsize = ComputedVec::forced_import_or_init_from_1(
|
||||
computation,
|
||||
&file,
|
||||
let txindex_to_vsize = LazyVecFrom1::init(
|
||||
"vsize",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
txindex_to_weight.boxed_clone(),
|
||||
|index: TxIndex, iter| {
|
||||
let index = index.unwrap_to_usize();
|
||||
@@ -165,14 +143,11 @@ impl Vecs {
|
||||
)
|
||||
})
|
||||
},
|
||||
)?;
|
||||
);
|
||||
|
||||
let txindex_to_is_coinbase = ComputedVec::forced_import_or_init_from_2(
|
||||
computation,
|
||||
&file,
|
||||
let txindex_to_is_coinbase = LazyVecFrom2::init(
|
||||
"is_coinbase",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexes.txindex_to_height.boxed_clone(),
|
||||
indexer.vecs.height_to_first_txindex.boxed_clone(),
|
||||
|index: TxIndex, txindex_to_height_iter, height_to_first_txindex_iter| {
|
||||
@@ -188,14 +163,11 @@ impl Vecs {
|
||||
StoredBool::from(index == txindex)
|
||||
})
|
||||
},
|
||||
)?;
|
||||
);
|
||||
|
||||
let txindex_to_input_value = ComputedVec::forced_import_or_init_from_3(
|
||||
computation,
|
||||
&file,
|
||||
let txindex_to_input_value = LazyVecFrom3::init(
|
||||
"input_value",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.txindex_to_first_inputindex.boxed_clone(),
|
||||
indexes.txindex_to_input_count.boxed_clone(),
|
||||
inputindex_to_value.boxed_clone(),
|
||||
@@ -224,11 +196,11 @@ impl Vecs {
|
||||
})
|
||||
})
|
||||
},
|
||||
)?;
|
||||
);
|
||||
|
||||
// let indexes_to_input_value: ComputedVecsFromTxindex<Sats> =
|
||||
// ComputedVecsFromTxindex::forced_import(
|
||||
// file,
|
||||
// db,
|
||||
// "input_value",
|
||||
// true,
|
||||
// version + VERSION + Version::ZERO,
|
||||
@@ -240,12 +212,9 @@ impl Vecs {
|
||||
// .add_cumulative(),
|
||||
// )?;
|
||||
|
||||
let txindex_to_output_value = ComputedVec::forced_import_or_init_from_3(
|
||||
computation,
|
||||
&file,
|
||||
let txindex_to_output_value = LazyVecFrom3::init(
|
||||
"output_value",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
indexer.vecs.txindex_to_first_outputindex.boxed_clone(),
|
||||
indexes.txindex_to_output_count.boxed_clone(),
|
||||
indexer.vecs.outputindex_to_value.boxed_clone(),
|
||||
@@ -265,20 +234,20 @@ impl Vecs {
|
||||
.into_owned();
|
||||
let range = first_index..first_index + count as usize;
|
||||
range.into_iter().fold(Sats::ZERO, |total, outputindex| {
|
||||
total
|
||||
+ outputindex_to_value_iter
|
||||
.next_at(outputindex)
|
||||
.unwrap()
|
||||
.1
|
||||
.into_owned()
|
||||
let v = outputindex_to_value_iter
|
||||
.next_at(outputindex)
|
||||
.unwrap()
|
||||
.1
|
||||
.into_owned();
|
||||
total + v
|
||||
})
|
||||
})
|
||||
},
|
||||
)?;
|
||||
);
|
||||
|
||||
// let indexes_to_output_value: ComputedVecsFromTxindex<Sats> =
|
||||
// ComputedVecsFromTxindex::forced_import(
|
||||
// file,
|
||||
// db,
|
||||
// "output_value",
|
||||
// true,
|
||||
// version + VERSION + Version::ZERO,
|
||||
@@ -290,57 +259,18 @@ impl Vecs {
|
||||
// .add_cumulative(),
|
||||
// )?;
|
||||
|
||||
let txindex_to_fee = ComputedVecFrom2::forced_import_or_init_from_2(
|
||||
Computation::Eager,
|
||||
&file,
|
||||
"fee",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
txindex_to_input_value.boxed_clone(),
|
||||
txindex_to_output_value.boxed_clone(),
|
||||
|txindex: TxIndex, input_iter, output_iter| {
|
||||
let txindex = txindex.unwrap_to_usize();
|
||||
input_iter.next_at(txindex).and_then(|(_, value)| {
|
||||
let input = value.into_owned();
|
||||
if input.is_zero() {
|
||||
return Some(Sats::ZERO);
|
||||
}
|
||||
output_iter.next_at(txindex).map(|(_, value)| {
|
||||
let output = value.into_owned();
|
||||
input.checked_sub(output).unwrap()
|
||||
})
|
||||
})
|
||||
},
|
||||
)?;
|
||||
let txindex_to_fee =
|
||||
EagerVec::forced_import_compressed(&db, "fee", version + VERSION + Version::ZERO)?;
|
||||
|
||||
let txindex_to_feerate = ComputedVecFrom2::forced_import_or_init_from_2(
|
||||
Computation::Eager,
|
||||
&file,
|
||||
"feerate",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
txindex_to_fee.boxed_clone(),
|
||||
txindex_to_vsize.boxed_clone(),
|
||||
|txindex: TxIndex, fee_iter, vsize_iter| {
|
||||
let txindex = txindex.unwrap_to_usize();
|
||||
fee_iter.next_at(txindex).and_then(|(_, value)| {
|
||||
let fee = value.into_owned();
|
||||
vsize_iter.next_at(txindex).map(|(_, value)| {
|
||||
let vsize = value.into_owned();
|
||||
Feerate::from((fee, vsize))
|
||||
})
|
||||
})
|
||||
},
|
||||
)?;
|
||||
let txindex_to_feerate =
|
||||
EagerVec::forced_import_compressed(&db, "feerate", version + VERSION + Version::ZERO)?;
|
||||
|
||||
Ok(Self {
|
||||
indexes_to_tx_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"tx_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -350,12 +280,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_input_count: ComputedVecsFromTxindex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"input_count",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -365,12 +293,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_output_count: ComputedVecsFromTxindex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"output_count",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -380,43 +306,35 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_tx_v1: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"tx_v1",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_tx_v2: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"tx_v2",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_tx_v3: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"tx_v3",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
)?,
|
||||
indexes_to_fee: ComputedValueVecsFromTxindex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"fee",
|
||||
indexes,
|
||||
Source::Vec(txindex_to_fee.boxed_clone()),
|
||||
version + VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
price,
|
||||
VecBuilderOptions::default()
|
||||
.add_sum()
|
||||
@@ -426,12 +344,10 @@ impl Vecs {
|
||||
.add_average(),
|
||||
)?,
|
||||
indexes_to_feerate: ComputedVecsFromTxindex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"feerate",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_percentiles()
|
||||
@@ -439,12 +355,10 @@ impl Vecs {
|
||||
.add_average(),
|
||||
)?,
|
||||
indexes_to_tx_vsize: ComputedVecsFromTxindex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"tx_vsize",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_percentiles()
|
||||
@@ -452,12 +366,10 @@ impl Vecs {
|
||||
.add_average(),
|
||||
)?,
|
||||
indexes_to_tx_weight: ComputedVecsFromTxindex::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"tx_weight",
|
||||
Source::None,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_percentiles()
|
||||
@@ -465,12 +377,10 @@ impl Vecs {
|
||||
.add_average(),
|
||||
)?,
|
||||
indexes_to_subsidy: ComputedValueVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"subsidy",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
VecBuilderOptions::default()
|
||||
.add_percentiles()
|
||||
.add_sum()
|
||||
@@ -481,12 +391,10 @@ impl Vecs {
|
||||
indexes,
|
||||
)?,
|
||||
indexes_to_coinbase: ComputedValueVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"coinbase",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
VecBuilderOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative()
|
||||
@@ -497,23 +405,19 @@ impl Vecs {
|
||||
indexes,
|
||||
)?,
|
||||
indexes_to_unclaimed_rewards: ComputedValueVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"unclaimed_rewards",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
VecBuilderOptions::default().add_sum().add_cumulative(),
|
||||
compute_dollars,
|
||||
indexes,
|
||||
)?,
|
||||
indexes_to_p2a_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"p2a_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -523,12 +427,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_p2ms_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"p2ms_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -538,12 +440,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_p2pk33_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"p2pk33_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -553,12 +453,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_p2pk65_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"p2pk65_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -568,12 +466,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_p2pkh_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"p2pkh_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -583,12 +479,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_p2sh_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"p2sh_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -598,12 +492,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_p2tr_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"p2tr_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -613,12 +505,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_p2wpkh_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"p2wpkh_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -628,12 +518,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_p2wsh_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"p2wsh_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -643,12 +531,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_opreturn_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"opreturn_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -658,12 +544,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_unknownoutput_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"unknownoutput_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -673,12 +557,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_emptyoutput_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"emptyoutput_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default()
|
||||
.add_average()
|
||||
@@ -688,12 +570,10 @@ impl Vecs {
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_exact_utxo_count: ComputedVecsFromHeight::forced_import(
|
||||
&file,
|
||||
&db,
|
||||
"exact_utxo_count",
|
||||
Source::Compute,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
computation,
|
||||
indexes,
|
||||
VecBuilderOptions::default().add_last(),
|
||||
)?,
|
||||
@@ -708,7 +588,7 @@ impl Vecs {
|
||||
txindex_to_vsize,
|
||||
txindex_to_weight,
|
||||
|
||||
file,
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -721,7 +601,7 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_(indexer, indexes, starting_indexes, price, exit)?;
|
||||
self.file.flush_then_punch()?;
|
||||
self.db.flush_then_punch()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -792,36 +672,6 @@ impl Vecs {
|
||||
compute_indexes_to_tx_vany(&mut self.indexes_to_tx_v2, TxVersion::TWO)?;
|
||||
compute_indexes_to_tx_vany(&mut self.indexes_to_tx_v3, TxVersion::THREE)?;
|
||||
|
||||
self.txindex_to_is_coinbase.compute_if_necessary(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txindex_to_weight.compute_if_necessary(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txindex_to_vsize.compute_if_necessary(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.inputindex_to_value.compute_if_necessary(
|
||||
starting_indexes.inputindex,
|
||||
&indexer.vecs.inputindex_to_outputindex,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txindex_to_output_value.compute_if_necessary(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// self.indexes_to_output_value.compute_all(
|
||||
// indexer,
|
||||
// indexes,
|
||||
@@ -838,12 +688,6 @@ impl Vecs {
|
||||
// },
|
||||
// )?;
|
||||
|
||||
self.txindex_to_input_value.compute_if_necessary(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// self.indexes_to_input_value.compute_all(
|
||||
// indexer,
|
||||
// indexes,
|
||||
@@ -860,15 +704,29 @@ impl Vecs {
|
||||
// },
|
||||
// )?;
|
||||
|
||||
self.txindex_to_fee.compute_if_necessary(
|
||||
self.txindex_to_fee.compute_transform3(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
&self.txindex_to_input_value,
|
||||
&self.txindex_to_output_value,
|
||||
&self.txindex_to_is_coinbase,
|
||||
|(i, input, output, coinbase, ..)| {
|
||||
(
|
||||
i,
|
||||
if coinbase.is_true() {
|
||||
Sats::ZERO
|
||||
} else {
|
||||
input.checked_sub(output).unwrap()
|
||||
},
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txindex_to_feerate.compute_if_necessary(
|
||||
self.txindex_to_feerate.compute_transform2(
|
||||
starting_indexes.txindex,
|
||||
&indexer.vecs.txindex_to_txid,
|
||||
&self.txindex_to_fee,
|
||||
&self.txindex_to_vsize,
|
||||
|(txindex, fee, vsize, ..)| (txindex, Feerate::from((fee, vsize))),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -6,10 +6,11 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
brk_vecs = { workspace = true }
|
||||
vecdb = { workspace = true }
|
||||
bitcoincore-rpc = { workspace = true }
|
||||
fjall = { workspace = true }
|
||||
jiff = { workspace = true }
|
||||
|
||||
+138
-26
@@ -1,28 +1,140 @@
|
||||
# BRK Core
|
||||
# brk_error
|
||||
|
||||
<p align="left">
|
||||
<a href="https://github.com/bitcoinresearchkit/brk">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/bitcoinresearchkit/brk?style=social">
|
||||
</a>
|
||||
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
|
||||
<img src="https://img.shields.io/crates/l/brk" alt="License" />
|
||||
</a>
|
||||
<a href="https://crates.io/crates/brk_structs">
|
||||
<img src="https://img.shields.io/crates/v/brk_structs" alt="Version" />
|
||||
</a>
|
||||
<a href="https://docs.rs/brk_structs">
|
||||
<img src="https://img.shields.io/docsrs/brk_structs" alt="Documentation" />
|
||||
</a>
|
||||
<img src="https://img.shields.io/crates/size/brk_structs" alt="Size" />
|
||||
<a href="https://deps.rs/crate/brk_structs">
|
||||
<img src="https://deps.rs/crate/brk_structs/latest/status.svg" alt="Dependency status">
|
||||
</a>
|
||||
<a href="https://discord.gg/HaR3wpH3nr">
|
||||
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
|
||||
</a>
|
||||
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
|
||||
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
|
||||
</a>
|
||||
</p>
|
||||
**Centralized error handling for the Bitcoin Research Kit**
|
||||
|
||||
A list of structs that are used throughout the project as units, think of `Date`, `Height`, `Sats`, `Txindex` or anything that can be either a key and/or a value of a dataset.
|
||||
`brk_error` provides a unified error type and result system used throughout the BRK ecosystem. It consolidates error handling from multiple external dependencies and adds Bitcoin-specific error variants.
|
||||
|
||||
## What it provides
|
||||
|
||||
- **Unified Error Type**: Single `Error` enum that covers all error cases across BRK crates
|
||||
- **Convenient Result Type**: Pre-configured `Result<T, E = Error>` for consistent error handling
|
||||
- **External Error Integration**: Automatic conversions from common library errors
|
||||
- **Bitcoin-Specific Errors**: Domain-specific error variants for blockchain data processing
|
||||
|
||||
## Key Features
|
||||
|
||||
### Centralized Error Management
|
||||
- Single error type for the entire BRK ecosystem
|
||||
- Consistent error handling patterns across all crates
|
||||
- Reduced error type complexity in public APIs
|
||||
|
||||
### External Library Integration
|
||||
Automatic `From` implementations for errors from:
|
||||
- **I/O Operations**: `std::io::Error`
|
||||
- **Bitcoin Core RPC**: `bitcoincore_rpc::Error`
|
||||
- **Database Operations**: `fjall::Error`, `vecdb::Error`
|
||||
- **Serialization**: `serde_json::Error`
|
||||
- **Time Operations**: `jiff::Error`, `SystemTimeError`
|
||||
- **HTTP Requests**: `minreq::Error`
|
||||
- **Zero-Copy Operations**: `zerocopy` conversion errors
|
||||
|
||||
### Bitcoin-Specific Error Variants
|
||||
- `WrongAddressType` - Invalid address type for operation
|
||||
- `UnindexableDate` - Date before Bitcoin genesis (2009-01-03)
|
||||
- `WrongLength` - Invalid data length for Bitcoin structures
|
||||
- `QuickCacheError` - Cache operation failures
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Error Handling
|
||||
|
||||
```rust
|
||||
use brk_error::{Error, Result};
|
||||
|
||||
fn process_block() -> Result<Block> {
|
||||
let rpc_client = get_rpc_client()?; // bitcoincore_rpc::Error -> Error
|
||||
let block_data = rpc_client.get_block_info(&hash)?;
|
||||
|
||||
// Custom Bitcoin-specific validation
|
||||
if block_data.height < 0 {
|
||||
return Err(Error::Str("Invalid block height"));
|
||||
}
|
||||
|
||||
Ok(block_data)
|
||||
}
|
||||
```
|
||||
|
||||
### Working with External Libraries
|
||||
|
||||
```rust
|
||||
use brk_error::Result;
|
||||
|
||||
fn save_data(data: &[u8]) -> Result<()> {
|
||||
// I/O error automatically converted
|
||||
std::fs::write("data.bin", data)?;
|
||||
|
||||
// JSON serialization error automatically converted
|
||||
let json = serde_json::to_string(&data)?;
|
||||
|
||||
// Database error automatically converted
|
||||
database.insert("key", &json)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Bitcoin-Specific Validation
|
||||
|
||||
```rust
|
||||
use brk_error::{Error, Result};
|
||||
|
||||
fn validate_date(date: &Date) -> Result<()> {
|
||||
if *date < Date::new(2009, 1, 3) {
|
||||
return Err(Error::UnindexableDate);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_address_type(output_type: OutputType) -> Result<()> {
|
||||
if !output_type.is_address() {
|
||||
return Err(Error::WrongAddressType);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### String Errors
|
||||
|
||||
```rust
|
||||
// Static string errors (zero allocation)
|
||||
Err(Error::Str("Invalid configuration"))
|
||||
|
||||
// Dynamic string errors
|
||||
Err(Error::String(format!("Block {} not found", height)))
|
||||
```
|
||||
|
||||
## Result Type
|
||||
|
||||
The crate provides a convenient `Result` type alias:
|
||||
|
||||
```rust
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
```
|
||||
|
||||
This allows for clean function signatures throughout BRK:
|
||||
|
||||
```rust
|
||||
fn parse_block() -> Result<Block> { /* ... */ }
|
||||
fn index_transactions() -> Result<Vec<Transaction>> { /* ... */ }
|
||||
```
|
||||
|
||||
## Error Display
|
||||
|
||||
All errors implement `Display` and `std::error::Error`, providing:
|
||||
- Formatted error messages for debugging
|
||||
- Error chain support for nested errors
|
||||
- Integration with error handling libraries like `anyhow`
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `vecdb` - Vector database error types
|
||||
- `bitcoincore-rpc` - Bitcoin Core RPC client errors
|
||||
- `fjall` - Key-value store errors
|
||||
- `jiff` - Date/time operation errors
|
||||
- `minreq` - HTTP request errors
|
||||
- `serde_json` - JSON serialization errors
|
||||
- `zerocopy` - Zero-copy conversion errors
|
||||
|
||||
---
|
||||
|
||||
*This README was generated by Claude Code*
|
||||
@@ -1,3 +1,5 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
use std::{
|
||||
fmt::{self, Debug, Display},
|
||||
io, result, time,
|
||||
@@ -11,11 +13,13 @@ pub enum Error {
|
||||
BitcoinRPC(bitcoincore_rpc::Error),
|
||||
Jiff(jiff::Error),
|
||||
Fjall(fjall::Error),
|
||||
VecDB(vecdb::Error),
|
||||
SeqDB(vecdb::SeqDBError),
|
||||
Minreq(minreq::Error),
|
||||
SystemTimeError(time::SystemTimeError),
|
||||
SerdeJson(serde_json::Error),
|
||||
ZeroCopyError,
|
||||
Vecs(brk_vecs::Error),
|
||||
Vecs(vecdb::Error),
|
||||
|
||||
WrongLength,
|
||||
WrongAddressType,
|
||||
@@ -43,6 +47,18 @@ impl From<io::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<vecdb::Error> for Error {
|
||||
fn from(value: vecdb::Error) -> Self {
|
||||
Self::VecDB(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<vecdb::SeqDBError> for Error {
|
||||
fn from(value: vecdb::SeqDBError) -> Self {
|
||||
Self::SeqDB(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bitcoincore_rpc::Error> for Error {
|
||||
fn from(value: bitcoincore_rpc::Error) -> Self {
|
||||
Self::BitcoinRPC(value)
|
||||
@@ -55,12 +71,6 @@ impl From<minreq::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<brk_vecs::Error> for Error {
|
||||
fn from(value: brk_vecs::Error) -> Self {
|
||||
Self::Vecs(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<jiff::Error> for Error {
|
||||
fn from(value: jiff::Error) -> Self {
|
||||
Self::Jiff(value)
|
||||
@@ -91,6 +101,8 @@ impl fmt::Display for Error {
|
||||
Error::IO(error) => Display::fmt(&error, f),
|
||||
Error::Minreq(error) => Display::fmt(&error, f),
|
||||
Error::SerdeJson(error) => Display::fmt(&error, f),
|
||||
Error::VecDB(error) => Display::fmt(&error, f),
|
||||
Error::SeqDB(error) => Display::fmt(&error, f),
|
||||
Error::Vecs(error) => Display::fmt(&error, f),
|
||||
Error::BitcoinRPC(error) => Display::fmt(&error, f),
|
||||
Error::SystemTimeError(error) => Display::fmt(&error, f),
|
||||
|
||||
@@ -6,6 +6,7 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
|
||||
+184
-26
@@ -1,28 +1,186 @@
|
||||
# BRK Fetcher
|
||||
# brk_fetcher
|
||||
|
||||
<p align="left">
|
||||
<a href="https://github.com/bitcoinresearchkit/brk">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/bitcoinresearchkit/brk?style=social">
|
||||
</a>
|
||||
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
|
||||
<img src="https://img.shields.io/crates/l/brk" alt="License" />
|
||||
</a>
|
||||
<a href="https://crates.io/crates/brk_fetcher">
|
||||
<img src="https://img.shields.io/crates/v/brk_fetcher" alt="Version" />
|
||||
</a>
|
||||
<a href="https://docs.rs/brk_fetcher">
|
||||
<img src="https://img.shields.io/docsrs/brk_fetcher" alt="Documentation" />
|
||||
</a>
|
||||
<img src="https://img.shields.io/crates/size/brk_fetcher" alt="Size" />
|
||||
<a href="https://deps.rs/crate/brk_fetcher">
|
||||
<img src="https://deps.rs/crate/brk_fetcher/latest/status.svg" alt="Dependency status">
|
||||
</a>
|
||||
<a href="https://discord.gg/HaR3wpH3nr">
|
||||
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
|
||||
</a>
|
||||
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
|
||||
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
|
||||
</a>
|
||||
</p>
|
||||
**Bitcoin price data fetcher with multi-source fallback and retry logic**
|
||||
|
||||
A crate that can fetch the Bitcoin price, either by date or height, from Binance, Kraken and the main instance of BRK.
|
||||
`brk_fetcher` provides reliable Bitcoin price data retrieval from multiple sources including Binance, Kraken, and BRK instances. It offers both date-based and block height-based price queries with automatic fallback and retry mechanisms for robust data collection.
|
||||
|
||||
## What it provides
|
||||
|
||||
- **Multi-source fallback**: Automatic fallback between Kraken → Binance → BRK
|
||||
- **Flexible querying**: Fetch prices by date or block height with timestamps
|
||||
- **Retry logic**: Built-in retry mechanism with exponential backoff
|
||||
- **Multiple timeframes**: 1-minute and 1-day interval support
|
||||
- **HAR file import**: Import historical Binance chart data from browser
|
||||
|
||||
## Key Features
|
||||
|
||||
### Data Sources
|
||||
- **Kraken API**: Primary source for OHLC data (1-day and 1-minute intervals)
|
||||
- **Binance API**: Secondary source with additional historical data
|
||||
- **BRK instance**: Fallback source for previously cached price data
|
||||
- **HAR import**: Manual historical data import from browser sessions
|
||||
|
||||
### Query Methods
|
||||
- **Date-based queries**: Get OHLC data for specific calendar dates
|
||||
- **Height-based queries**: Get OHLC data for specific block heights with timestamps
|
||||
- **Automatic aggregation**: Combines minute-level data for block intervals
|
||||
|
||||
### Reliability Features
|
||||
- **Automatic fallback**: Tries sources in order until successful
|
||||
- **Retry mechanism**: Up to 12 hours of retries with 60-second intervals
|
||||
- **Cache clearing**: Automatic cache refresh on failures
|
||||
- **Error handling**: Graceful degradation with detailed error messages
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Setup
|
||||
|
||||
```rust
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_structs::{Date, Height, Timestamp};
|
||||
|
||||
// Initialize fetcher with exchange APIs enabled
|
||||
let mut fetcher = Fetcher::import(true, None)?;
|
||||
|
||||
// Initialize without exchange APIs (BRK-only mode)
|
||||
let mut fetcher = Fetcher::import(false, None)?;
|
||||
|
||||
// Initialize with HAR file for historical Binance data
|
||||
let har_path = Path::new("./binance.har");
|
||||
let mut fetcher = Fetcher::import(true, Some(har_path))?;
|
||||
```
|
||||
|
||||
### Date-based Price Queries
|
||||
|
||||
```rust
|
||||
use brk_structs::Date;
|
||||
|
||||
// Fetch OHLC data for a specific date
|
||||
let date = Date::new(2024, 12, 25);
|
||||
let ohlc = fetcher.get_date(date)?;
|
||||
|
||||
println!("Bitcoin price on {}: ${:.2}", date, ohlc.close.dollars());
|
||||
println!("Daily high: ${:.2}", ohlc.high.dollars());
|
||||
println!("Daily low: ${:.2}", ohlc.low.dollars());
|
||||
```
|
||||
|
||||
### Block Height-based Price Queries
|
||||
|
||||
```rust
|
||||
use brk_structs::{Height, Timestamp};
|
||||
|
||||
// Fetch price at specific block height
|
||||
let height = Height::new(900_000);
|
||||
let timestamp = Timestamp::from_block_height(height);
|
||||
let previous_timestamp = Some(Timestamp::from_block_height(Height::new(899_999)));
|
||||
|
||||
let ohlc = fetcher.get_height(height, timestamp, previous_timestamp)?;
|
||||
println!("Bitcoin price at block {}: ${:.2}", height, ohlc.close.dollars());
|
||||
```
|
||||
|
||||
### Working with OHLC Data
|
||||
|
||||
```rust
|
||||
use brk_structs::OHLCCents;
|
||||
|
||||
// OHLC data is returned in cents for precision
|
||||
let ohlc: OHLCCents = fetcher.get_date(date)?;
|
||||
|
||||
// Convert to dollars for display
|
||||
println!("Open: ${:.2}", ohlc.open.dollars());
|
||||
println!("High: ${:.2}", ohlc.high.dollars());
|
||||
println!("Low: ${:.2}", ohlc.low.dollars());
|
||||
println!("Close: ${:.2}", ohlc.close.dollars());
|
||||
|
||||
// Access raw cent values
|
||||
println!("Close in cents: {}", ohlc.close.0);
|
||||
```
|
||||
|
||||
### Using Individual Sources
|
||||
|
||||
```rust
|
||||
use brk_fetcher::{Binance, Kraken, BRK};
|
||||
|
||||
// Use specific exchanges directly
|
||||
let binance = Binance::init(None);
|
||||
let kraken = Kraken::default();
|
||||
let brk = BRK::default();
|
||||
|
||||
// Fetch from specific source
|
||||
let binance_data = binance.get_from_1d(&date)?;
|
||||
let kraken_data = kraken.get_from_1mn(timestamp, previous_timestamp)?;
|
||||
let brk_data = brk.get_from_height(height)?;
|
||||
```
|
||||
|
||||
### Error Handling and Retries
|
||||
|
||||
```rust
|
||||
// The fetcher automatically retries on failures
|
||||
match fetcher.get_date(date) {
|
||||
Ok(ohlc) => println!("Successfully fetched: ${:.2}", ohlc.close.dollars()),
|
||||
Err(e) => {
|
||||
// After all retries and sources exhausted
|
||||
eprintln!("Failed to fetch price data: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear cache to force fresh data
|
||||
fetcher.clear();
|
||||
```
|
||||
|
||||
## Data Sources and Limitations
|
||||
|
||||
### Kraken API
|
||||
- **1-day data**: Historical daily OHLC data
|
||||
- **1-minute data**: Limited to last ~10 hours
|
||||
- **Rate limits**: Subject to Kraken API restrictions
|
||||
|
||||
### Binance API
|
||||
- **1-day data**: Historical daily OHLC data
|
||||
- **1-minute data**: Limited to last ~16 hours
|
||||
- **HAR import**: Can extend historical coverage via browser data
|
||||
|
||||
### BRK Instance
|
||||
- **Cached data**: Previously fetched price data
|
||||
- **Offline capability**: Works without internet when data is cached
|
||||
- **Height-based**: Optimized for block height queries
|
||||
|
||||
## HAR File Import
|
||||
|
||||
For historical data beyond API limits:
|
||||
|
||||
1. Visit [Binance BTC/USDT chart](https://www.binance.com/en/trade/BTC_USDT?type=spot)
|
||||
2. Set chart to 1-minute interval
|
||||
3. Open browser dev tools, go to Network tab
|
||||
4. Filter by 'uiKlines'
|
||||
5. Scroll chart to desired historical period
|
||||
6. Export network requests as HAR file
|
||||
7. Initialize fetcher with HAR path
|
||||
|
||||
## Fallback Strategy
|
||||
|
||||
The fetcher tries sources in this order:
|
||||
1. **Kraken** - Primary source for most queries
|
||||
2. **Binance** - Secondary source with extended coverage
|
||||
3. **BRK** - Fallback for cached/computed prices
|
||||
|
||||
If all sources fail, it retries up to 12 hours with 60-second intervals.
|
||||
|
||||
## Performance and Reliability
|
||||
|
||||
- **Automatic retries**: Up to 720 attempts (12 hours) with 60-second delays
|
||||
- **Cache management**: Clears cache on failures to force fresh data
|
||||
- **Error logging**: Detailed failure reporting with recovery instructions
|
||||
- **Graceful degradation**: Falls back through sources until successful
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `brk_structs` - Bitcoin-aware type system (Date, Height, OHLC types)
|
||||
- `brk_error` - Unified error handling
|
||||
- `minreq` - HTTP client for API requests
|
||||
- `serde_json` - JSON parsing for API responses
|
||||
- `log` - Logging for retry and error reporting
|
||||
|
||||
---
|
||||
|
||||
*This README was generated by Claude Code*
|
||||
@@ -3,13 +3,13 @@ use brk_fetcher::{BRK, Binance, Fetcher, Kraken};
|
||||
use brk_structs::{Date, Height};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
brk_logger::init(None);
|
||||
brk_logger::init(None)?;
|
||||
|
||||
let mut brk = BRK::default();
|
||||
dbg!(brk.get_from_height(Height::new(900_000))?);
|
||||
dbg!(brk.get_from_date(Date::new(2025, 6, 7))?);
|
||||
|
||||
let mut fetcher = Fetcher::import(None)?;
|
||||
let mut fetcher = Fetcher::import(true, None)?;
|
||||
|
||||
Binance::fetch_1d().map(|b| {
|
||||
dbg!(b.last_key_value());
|
||||
|
||||
@@ -11,7 +11,7 @@ use brk_structs::{Cents, OHLCCents, Timestamp};
|
||||
use log::info;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{Close, Date, Dollars, Fetcher, High, Low, Open, retry};
|
||||
use crate::{Close, Date, Dollars, Fetcher, High, Low, Open, default_retry};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Binance {
|
||||
@@ -68,17 +68,13 @@ impl Binance {
|
||||
pub fn fetch_1mn() -> Result<BTreeMap<Timestamp, OHLCCents>> {
|
||||
info!("Fetching 1mn prices from Binance...");
|
||||
|
||||
retry(
|
||||
|_| {
|
||||
Self::json_to_timestamp_to_ohlc(
|
||||
&minreq::get(Self::url("interval=1m&limit=1000"))
|
||||
.send()?
|
||||
.json()?,
|
||||
)
|
||||
},
|
||||
30,
|
||||
10,
|
||||
)
|
||||
default_retry(|_| {
|
||||
Self::json_to_timestamp_to_ohlc(
|
||||
&minreq::get(Self::url("interval=1m&limit=1000"))
|
||||
.send()?
|
||||
.json()?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_from_1d(&mut self, date: &Date) -> Result<OHLCCents> {
|
||||
@@ -97,11 +93,9 @@ impl Binance {
|
||||
pub fn fetch_1d() -> Result<BTreeMap<Date, OHLCCents>> {
|
||||
info!("Fetching daily prices from Binance...");
|
||||
|
||||
retry(
|
||||
|_| Self::json_to_date_to_ohlc(&minreq::get(Self::url("interval=1d")).send()?.json()?),
|
||||
30,
|
||||
10,
|
||||
)
|
||||
default_retry(|_| {
|
||||
Self::json_to_date_to_ohlc(&minreq::get(Self::url("interval=1d")).send()?.json()?)
|
||||
})
|
||||
}
|
||||
|
||||
fn read_har(&self) -> Result<BTreeMap<Timestamp, OHLCCents>> {
|
||||
|
||||
@@ -5,7 +5,7 @@ use brk_structs::{Cents, CheckedSub, Date, DateIndex, Height, OHLCCents};
|
||||
use log::info;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{Close, Dollars, High, Low, Open, retry};
|
||||
use crate::{Close, Dollars, High, Low, Open, default_retry};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
@@ -15,7 +15,6 @@ pub struct BRK {
|
||||
}
|
||||
|
||||
const API_URL: &str = "https://bitcoinresearchkit.org/api/vecs";
|
||||
const RETRIES: usize = 10;
|
||||
const CHUNK_SIZE: usize = 10_000;
|
||||
|
||||
impl BRK {
|
||||
@@ -45,25 +44,21 @@ impl BRK {
|
||||
fn fetch_height_prices(height: Height) -> Result<Vec<OHLCCents>> {
|
||||
info!("Fetching BRK height {height} prices...");
|
||||
|
||||
retry(
|
||||
|_| {
|
||||
let url = format!(
|
||||
"{API_URL}/height-to-ohlc?from={}&to={}",
|
||||
height,
|
||||
height + CHUNK_SIZE
|
||||
);
|
||||
default_retry(|_| {
|
||||
let url = format!(
|
||||
"{API_URL}/height-to-ohlc?from={}&to={}",
|
||||
height,
|
||||
height + CHUNK_SIZE
|
||||
);
|
||||
|
||||
let body: Value = minreq::get(url).send()?.json()?;
|
||||
let body: Value = minreq::get(url).send()?.json()?;
|
||||
|
||||
body.as_array()
|
||||
.ok_or(Error::Str("Expect to be an array"))?
|
||||
.iter()
|
||||
.map(Self::value_to_ohlc)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
},
|
||||
30,
|
||||
RETRIES,
|
||||
)
|
||||
body.as_array()
|
||||
.ok_or(Error::Str("Expect to be an array"))?
|
||||
.iter()
|
||||
.map(Self::value_to_ohlc)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_from_date(&mut self, date: Date) -> Result<OHLCCents> {
|
||||
@@ -94,25 +89,21 @@ impl BRK {
|
||||
fn fetch_date_prices(dateindex: DateIndex) -> Result<Vec<OHLCCents>> {
|
||||
info!("Fetching BRK dateindex {dateindex} prices...");
|
||||
|
||||
retry(
|
||||
|_| {
|
||||
let url = format!(
|
||||
"{API_URL}/dateindex-to-ohlc?from={}&to={}",
|
||||
dateindex,
|
||||
dateindex + CHUNK_SIZE
|
||||
);
|
||||
default_retry(|_| {
|
||||
let url = format!(
|
||||
"{API_URL}/dateindex-to-ohlc?from={}&to={}",
|
||||
dateindex,
|
||||
dateindex + CHUNK_SIZE
|
||||
);
|
||||
|
||||
let body: Value = minreq::get(url).send()?.json()?;
|
||||
let body: Value = minreq::get(url).send()?.json()?;
|
||||
|
||||
body.as_array()
|
||||
.ok_or(Error::Str("Expect to be an array"))?
|
||||
.iter()
|
||||
.map(Self::value_to_ohlc)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
},
|
||||
30,
|
||||
RETRIES,
|
||||
)
|
||||
body.as_array()
|
||||
.ok_or(Error::Str("Expect to be an array"))?
|
||||
.iter()
|
||||
.map(Self::value_to_ohlc)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
}
|
||||
|
||||
fn value_to_ohlc(value: &Value) -> Result<OHLCCents> {
|
||||
|
||||
@@ -5,7 +5,7 @@ use brk_structs::{Cents, Close, Date, Dollars, High, Low, OHLCCents, Open, Times
|
||||
use log::info;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{Fetcher, retry};
|
||||
use crate::{Fetcher, default_retry};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Kraken {
|
||||
@@ -35,11 +35,9 @@ impl Kraken {
|
||||
pub fn fetch_1mn() -> Result<BTreeMap<Timestamp, OHLCCents>> {
|
||||
info!("Fetching 1mn prices from Kraken...");
|
||||
|
||||
retry(
|
||||
|_| Self::json_to_timestamp_to_ohlc(&minreq::get(Self::url(1)).send()?.json()?),
|
||||
30,
|
||||
10,
|
||||
)
|
||||
default_retry(|_| {
|
||||
Self::json_to_timestamp_to_ohlc(&minreq::get(Self::url(1)).send()?.json()?)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_from_1d(&mut self, date: &Date) -> Result<OHLCCents> {
|
||||
@@ -57,11 +55,7 @@ impl Kraken {
|
||||
pub fn fetch_1d() -> Result<BTreeMap<Date, OHLCCents>> {
|
||||
info!("Fetching daily prices from Kraken...");
|
||||
|
||||
retry(
|
||||
|_| Self::json_to_date_to_ohlc(&minreq::get(Self::url(1440)).send()?.json()?),
|
||||
30,
|
||||
10,
|
||||
)
|
||||
default_retry(|_| Self::json_to_date_to_ohlc(&minreq::get(Self::url(1440)).send()?.json()?))
|
||||
}
|
||||
|
||||
fn json_to_timestamp_to_ohlc(json: &Value) -> Result<BTreeMap<Timestamp, OHLCCents>> {
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![doc = "\n## Example\n\n```rust"]
|
||||
#![doc = include_str!("../examples/main.rs")]
|
||||
#![doc = "```"]
|
||||
|
||||
use std::{collections::BTreeMap, path::Path, thread::sleep, time::Duration};
|
||||
|
||||
@@ -23,16 +20,16 @@ const TRIES: usize = 12 * 60;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Fetcher {
|
||||
binance: Binance,
|
||||
kraken: Kraken,
|
||||
binance: Option<Binance>,
|
||||
kraken: Option<Kraken>,
|
||||
brk: BRK,
|
||||
}
|
||||
|
||||
impl Fetcher {
|
||||
pub fn import(hars_path: Option<&Path>) -> Result<Self> {
|
||||
pub fn import(exchanges: bool, hars_path: Option<&Path>) -> Result<Self> {
|
||||
Ok(Self {
|
||||
binance: Binance::init(hars_path),
|
||||
kraken: Kraken::default(),
|
||||
binance: exchanges.then(|| Binance::init(hars_path)),
|
||||
kraken: exchanges.then(Kraken::default),
|
||||
brk: BRK::default(),
|
||||
})
|
||||
}
|
||||
@@ -43,10 +40,17 @@ impl Fetcher {
|
||||
|
||||
fn get_date_(&mut self, date: Date, tries: usize) -> Result<OHLCCents> {
|
||||
self.kraken
|
||||
.get_from_1d(&date)
|
||||
.as_mut()
|
||||
.map_or(Err(Error::Str("Kraken off")), |kraken| {
|
||||
kraken.get_from_1d(&date)
|
||||
})
|
||||
.or_else(|_| {
|
||||
// eprintln!("{e}");
|
||||
self.binance.get_from_1d(&date)
|
||||
self.binance
|
||||
.as_mut()
|
||||
.map_or(Err(Error::Str("Binance off")), |binance| {
|
||||
binance.get_from_1d(&date)
|
||||
})
|
||||
})
|
||||
.or_else(|_| {
|
||||
// eprintln!("{e}");
|
||||
@@ -93,11 +97,17 @@ impl Fetcher {
|
||||
|
||||
let ohlc = self
|
||||
.kraken
|
||||
.get_from_1mn(timestamp, previous_timestamp)
|
||||
.as_mut()
|
||||
.map_or(Err(Error::Str("Kraken off")), |kraken| {
|
||||
kraken.get_from_1mn(timestamp, previous_timestamp)
|
||||
})
|
||||
.unwrap_or_else(|_report| {
|
||||
// eprintln!("{_report}");
|
||||
self.binance
|
||||
.get_from_1mn(timestamp, previous_timestamp)
|
||||
.as_mut()
|
||||
.map_or(Err(Error::Str("Binance off")), |binance| {
|
||||
binance.get_from_1mn(timestamp, previous_timestamp)
|
||||
})
|
||||
.unwrap_or_else(|_report| {
|
||||
// // eprintln!("{_report}");
|
||||
self.brk.get_from_height(height).unwrap_or_else(|_report| {
|
||||
@@ -187,8 +197,12 @@ How to fix this:
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.kraken.clear();
|
||||
self.binance.clear();
|
||||
if let Some(kraken) = self.kraken.as_mut() {
|
||||
kraken.clear()
|
||||
}
|
||||
if let Some(binance) = self.binance.as_mut() {
|
||||
binance.clear()
|
||||
}
|
||||
self.brk.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,14 @@ use std::{fmt::Debug, thread::sleep, time::Duration};
|
||||
use brk_error::Result;
|
||||
use log::info;
|
||||
|
||||
pub fn retry<T>(function: impl Fn(usize) -> Result<T>, sleep_in_s: u64, retries: usize) -> Result<T>
|
||||
pub fn default_retry<T>(function: impl Fn(usize) -> Result<T>) -> Result<T>
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
retry(function, 5, 6)
|
||||
}
|
||||
|
||||
fn retry<T>(function: impl Fn(usize) -> Result<T>, sleep_in_s: u64, retries: usize) -> Result<T>
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
@@ -16,7 +17,7 @@ brk_error = { workspace = true }
|
||||
brk_logger = { workspace = true }
|
||||
brk_parser = { workspace = true }
|
||||
brk_store = { workspace = true }
|
||||
brk_vecs = { workspace = true }
|
||||
vecdb = { workspace = true }
|
||||
fjall = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rayon = { workspace = true }
|
||||
|
||||
+173
-52
@@ -1,69 +1,190 @@
|
||||
# BRK Indexer
|
||||
# brk_indexer
|
||||
|
||||
<p align="left">
|
||||
<a href="https://github.com/bitcoinresearchkit/brk">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/bitcoinresearchkit/brk?style=social">
|
||||
</a>
|
||||
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
|
||||
<img src="https://img.shields.io/crates/l/brk" alt="License" />
|
||||
</a>
|
||||
<a href="https://crates.io/crates/brk_indexer">
|
||||
<img src="https://img.shields.io/crates/v/brk_indexer" alt="Version" />
|
||||
</a>
|
||||
<a href="https://docs.rs/brk_indexer">
|
||||
<img src="https://img.shields.io/docsrs/brk_indexer" alt="Documentation" />
|
||||
</a>
|
||||
<img src="https://img.shields.io/crates/size/brk_indexer" alt="Size" />
|
||||
<a href="https://deps.rs/crate/brk_indexer">
|
||||
<img src="https://deps.rs/crate/brk_indexer/latest/status.svg" alt="Dependency status">
|
||||
</a>
|
||||
<a href="https://discord.gg/HaR3wpH3nr">
|
||||
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
|
||||
</a>
|
||||
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
|
||||
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
|
||||
</a>
|
||||
</p>
|
||||
**High-performance Bitcoin blockchain indexer with dual storage architecture**
|
||||
|
||||
A [Bitcoin Core](https://bitcoincore.org/en/about/) node indexer which iterates over the chain (via `../brk_parser`) and creates a database of the vecs (`brk_vec`) and key/value stores ([`fjall`](https://crates.io/crates/fjall)) that can be used in your Rust code.
|
||||
`brk_indexer` processes raw Bitcoin Core block data and creates efficient storage structures using both vectors (time-series) and key-value stores (lookups). It serves as the foundation of BRK's data pipeline, organizing all blockchain data into optimized formats for fast retrieval and analysis.
|
||||
|
||||
The crate only stores the bare minimum to be self sufficient and not have to use an RPC client (except for scripts which are not stored). If you need more data, checkout `../computer` which uses the outputs from the indexer to compute a whole range of datasets.
|
||||
## What it provides
|
||||
|
||||
Vecs are used sparingly instead of stores for multiple reasons:
|
||||
- **Dual Storage Architecture**: Vectors for time-series data, key-value stores for lookups
|
||||
- **Memory Efficiency**: ~5-6GB peak RAM usage during full blockchain indexing
|
||||
- **Incremental Processing**: Resume from last indexed height with rollback protection
|
||||
- **Data Integrity**: Collision detection and validation during indexing
|
||||
- **All Bitcoin Data Types**: Complete support for blocks, transactions, inputs, outputs, and addresses
|
||||
|
||||
- Only stores the relevant data since the key is an index
|
||||
- Saved as uncompressed bytes and thus can be parsed manually (with any programming language) without relying on a server or library
|
||||
- Easy to work with and predictable
|
||||
## Key Features
|
||||
|
||||
### Storage Strategy
|
||||
|
||||
**Vector Storage (time-series data):**
|
||||
- Block metadata (height, timestamp, hash, difficulty, size)
|
||||
- Transaction data (version, locktime, RBF flag, indices)
|
||||
- Input/Output mappings and values
|
||||
- Address bytes for all output types
|
||||
- Efficient for range queries and analytics
|
||||
|
||||
**Key-Value Storage (lookups):**
|
||||
- Block hash prefixes → heights
|
||||
- Transaction ID prefixes → transaction indices
|
||||
- Address byte hashes → type indices
|
||||
- Fast point queries by hash or address
|
||||
|
||||
### Performance Features
|
||||
- **Parallel Processing**: Concurrent transaction and output processing using Rayon
|
||||
- **Batch Operations**: Periodic commits every 1,000 blocks for optimal I/O
|
||||
- **Memory Efficiency**: Optimized data structures minimize RAM usage
|
||||
- **Incremental Updates**: Handles blockchain reorganizations automatically
|
||||
|
||||
### Address Type Support
|
||||
Complete support for all Bitcoin address types:
|
||||
- P2PK (65-byte and 33-byte), P2PKH, P2SH
|
||||
- P2WPKH, P2WSH, P2TR, P2A
|
||||
- P2MS (multisig), OpReturn, Empty, Unknown
|
||||
|
||||
## Usage
|
||||
|
||||
Storage wise, the expected overhead should be around 30% of the chain itself.
|
||||
### Basic Indexing
|
||||
|
||||
Peaks at 5 GB of RAM
|
||||
```rust
|
||||
use brk_indexer::Indexer;
|
||||
use brk_parser::Parser;
|
||||
use bitcoincore_rpc::{Auth, Client};
|
||||
use vecdb::Exit;
|
||||
|
||||
## Outputs
|
||||
// Setup Bitcoin Core RPC connection
|
||||
let rpc = Box::leak(Box::new(Client::new(
|
||||
"http://localhost:8332",
|
||||
Auth::CookieFile(Path::new("~/.bitcoin/.cookie")),
|
||||
)?));
|
||||
|
||||
Vecs: `src/storage/vecs/mod.rs`
|
||||
// Create parser for Bitcoin Core block files
|
||||
let parser = Parser::new(
|
||||
Path::new("~/.bitcoin/blocks").to_path_buf(),
|
||||
Path::new("./brk_data").to_path_buf(),
|
||||
rpc
|
||||
);
|
||||
|
||||
Stores: `src/storage/stores/mod.rs`
|
||||
// Create indexer with forced import (resets if needed)
|
||||
let mut indexer = Indexer::forced_import(Path::new("./brk_data"))?;
|
||||
|
||||
## Benchmark
|
||||
// Setup graceful shutdown handler
|
||||
let exit = Exit::new();
|
||||
exit.set_ctrlc_handler();
|
||||
|
||||
### `v0.0.21`
|
||||
// Index the blockchain
|
||||
let indexes = indexer.index(&parser, rpc, &exit, true)?;
|
||||
println!("Indexed up to height: {}", indexes.height);
|
||||
```
|
||||
|
||||
- machine: `MBP M3 Pro (36GB RAM)`
|
||||
- mode: `raw`
|
||||
- from: `0`
|
||||
- to: `892_098`
|
||||
- time: `7 hours 10 min 22s`
|
||||
- peak memory: `6.1GB`
|
||||
- disk usage: `270 GB`
|
||||
- overhead: `36%` (`270 GB / 741 GB`)
|
||||
### Continuous Indexing
|
||||
|
||||
### `v0.0.31`
|
||||
```rust
|
||||
use std::time::{Duration, Instant};
|
||||
use std::thread::sleep;
|
||||
|
||||
- machine: `MBP M3 Pro (36GB RAM)`
|
||||
- mode: `raw`
|
||||
- disk usage: `208 GB`
|
||||
- overhead: `28%` (`208 GB / 744 GB`)
|
||||
- peak memory: `5.7GB`
|
||||
// Continuous indexing loop for real-time updates
|
||||
loop {
|
||||
let start_time = Instant::now();
|
||||
|
||||
// Index new blocks
|
||||
let indexes = indexer.index(&parser, rpc, &exit, true)?;
|
||||
|
||||
println!("Indexed to height {} in {:?}",
|
||||
indexes.height, start_time.elapsed());
|
||||
|
||||
// Check for exit signal
|
||||
if exit.is_signaled() {
|
||||
println!("Graceful shutdown requested");
|
||||
break;
|
||||
}
|
||||
|
||||
// Wait before next update cycle
|
||||
sleep(Duration::from_secs(5 * 60));
|
||||
}
|
||||
```
|
||||
|
||||
### Accessing Indexed Data
|
||||
|
||||
```rust
|
||||
// Access the underlying storage structures
|
||||
let vecs = &indexer.vecs;
|
||||
let stores = &indexer.stores;
|
||||
|
||||
// Get block hash at specific height
|
||||
let block_hash = vecs.height_to_blockhash.get(Height::new(800_000))?;
|
||||
|
||||
// Look up transaction by prefix
|
||||
let tx_prefix = TxidPrefix::from(&txid);
|
||||
let tx_index = stores.txidprefix_to_txindex.get(&tx_prefix)?;
|
||||
|
||||
// Get address data
|
||||
let address_hash = AddressBytesHash::from(&address_bytes);
|
||||
let type_index = stores.addressbyteshash_to_anyaddressindex.get(&address_hash)?;
|
||||
```
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
**Benchmarked on MacBook Pro M3 Pro (36GB RAM):**
|
||||
- **Full blockchain sync** (to ~892k blocks): 7-8 hours
|
||||
- **Peak memory usage**: 5-6GB
|
||||
- **Storage overhead**: ~27% of Bitcoin Core block size
|
||||
- **Incremental updates**: Very fast, efficient resume from last height
|
||||
|
||||
## Data Organization
|
||||
|
||||
The indexer creates this storage structure:
|
||||
```
|
||||
brk_data/
|
||||
├── indexed/
|
||||
│ ├── vecs/ # Vector storage
|
||||
│ │ ├── height_to_* # Height-indexed data
|
||||
│ │ ├── txindex_to_* # Transaction-indexed data
|
||||
│ │ └── outputindex_to_* # Output-indexed data
|
||||
│ └── stores/ # Key-value stores
|
||||
│ ├── hash_lookups/ # Block/TX hash mappings
|
||||
│ └── address_maps/ # Address type mappings
|
||||
└── metadata/ # Versioning and state
|
||||
```
|
||||
|
||||
## Indexes Tracking
|
||||
|
||||
The indexer maintains current indices during processing:
|
||||
|
||||
```rust
|
||||
pub struct Indexes {
|
||||
pub height: Height, // Current block height
|
||||
pub txindex: TxIndex, // Current transaction index
|
||||
pub inputindex: InputIndex, // Current input index
|
||||
pub outputindex: OutputIndex, // Current output index
|
||||
pub p2pkhaddressindex: P2PKHAddressIndex, // P2PKH address index
|
||||
// ... indices for all address types
|
||||
}
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Bitcoin Core node** with RPC enabled
|
||||
- **Block file access** to `~/.bitcoin/blocks/`
|
||||
- **Storage space**: Minimum 500GB (scales with blockchain growth)
|
||||
- **Memory**: 8GB+ RAM recommended
|
||||
- **CPU**: Multi-core recommended for parallel processing
|
||||
|
||||
## Rollback and Recovery
|
||||
|
||||
- **Automatic rollback** on interruption or blockchain reorgs
|
||||
- **State persistence** for efficient restart
|
||||
- **Version management** for storage format compatibility
|
||||
- **Graceful shutdown** with Ctrl+C handling
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `brk_parser` - Bitcoin block parsing and sequential access
|
||||
- `brk_store` - Key-value storage wrapper (fjall-based)
|
||||
- `vecdb` - Vector database for time-series storage
|
||||
- `bitcoin` - Bitcoin protocol types and parsing
|
||||
- `rayon` - Parallel processing framework
|
||||
- `bitcoincore_rpc` - Bitcoin Core RPC client
|
||||
|
||||
---
|
||||
|
||||
*This README was generated by Claude Code*
|
||||
@@ -8,10 +8,10 @@ use std::{
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_parser::Parser;
|
||||
use brk_vecs::Exit;
|
||||
use vecdb::Exit;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
brk_logger::init(Some(Path::new(".log")));
|
||||
brk_logger::init(Some(Path::new(".log")))?;
|
||||
|
||||
let bitcoin_dir = Path::new(&std::env::var("HOME").unwrap())
|
||||
.join("Library")
|
||||
|
||||
@@ -7,7 +7,7 @@ use brk_structs::{
|
||||
P2PKHAddressIndex, P2SHAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex,
|
||||
TxIndex, TypeIndex, UnknownOutputIndex,
|
||||
};
|
||||
use brk_vecs::{
|
||||
use vecdb::{
|
||||
AnyIterableVec, AnyStoredIterableVec, GenericStoredVec, StoredIndex, StoredRaw, VecIterator,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![doc = "\n## Example\n\n```rust"]
|
||||
#![doc = include_str!("../examples/indexer.rs")]
|
||||
#![doc = "```"]
|
||||
|
||||
use std::{collections::BTreeMap, path::Path, str::FromStr, sync::Arc, thread, time::Instant};
|
||||
use std::{collections::BTreeMap, path::Path, str::FromStr, thread, time::Instant};
|
||||
|
||||
use bitcoin::{Transaction, TxIn, TxOut};
|
||||
use brk_error::{Error, Result};
|
||||
@@ -15,9 +12,9 @@ use brk_structs::{
|
||||
OutputType, Sats, StoredBool, Timestamp, TxIndex, Txid, TxidPrefix, TypeIndex,
|
||||
TypeIndexWithOutputindex, Unit, Version, Vin, Vout,
|
||||
};
|
||||
use brk_vecs::{AnyVec, Exit, File, GenericStoredVec, PAGE_SIZE, Reader, VecIterator};
|
||||
use log::{error, info};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyVec, Database, Exit, GenericStoredVec, PAGE_SIZE, Reader, VecIterator};
|
||||
mod indexes;
|
||||
mod stores;
|
||||
mod vecs;
|
||||
@@ -27,23 +24,23 @@ pub use stores::*;
|
||||
pub use vecs::*;
|
||||
|
||||
const SNAPSHOT_BLOCK_RANGE: usize = 1_000;
|
||||
const COLLISIONS_CHECKED_UP_TO: Height = Height::new(908_700);
|
||||
const COLLISIONS_CHECKED_UP_TO: Height = Height::new(909_150);
|
||||
const VERSION: Version = Version::ONE;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Indexer {
|
||||
pub file: Arc<File>,
|
||||
pub db: Database,
|
||||
pub vecs: Vecs,
|
||||
pub stores: Stores,
|
||||
}
|
||||
|
||||
impl Indexer {
|
||||
pub fn forced_import(outputs_dir: &Path) -> Result<Self> {
|
||||
let file = Arc::new(File::open(&outputs_dir.join("indexed/vecs"))?);
|
||||
let db = Database::open(&outputs_dir.join("indexed/vecs"))?;
|
||||
|
||||
let vecs = Vecs::forced_import(&file, VERSION + Version::ZERO)?;
|
||||
let vecs = Vecs::forced_import(&db, VERSION + Version::ZERO)?;
|
||||
|
||||
file.set_min_len(PAGE_SIZE * 50_000_000)?;
|
||||
db.set_min_len(PAGE_SIZE * 50_000_000)?;
|
||||
|
||||
Ok(Self {
|
||||
vecs,
|
||||
@@ -51,7 +48,7 @@ impl Indexer {
|
||||
&outputs_dir.join("indexed/stores"),
|
||||
VERSION + Version::ZERO,
|
||||
)?,
|
||||
file,
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,10 +59,10 @@ impl Indexer {
|
||||
exit: &Exit,
|
||||
check_collisions: bool,
|
||||
) -> Result<Indexes> {
|
||||
let file = self.file.clone();
|
||||
let db = self.db.clone();
|
||||
|
||||
// dbg!(self.file.regions().id_to_index());
|
||||
// dbg!(self.file.layout());
|
||||
// dbg!(self.db.regions().id_to_index());
|
||||
// dbg!(self.db.layout());
|
||||
|
||||
let starting_indexes = Indexes::try_from((&mut self.vecs, &self.stores, rpc))
|
||||
.unwrap_or_else(|_report| Indexes::default());
|
||||
@@ -111,8 +108,8 @@ impl Indexer {
|
||||
vecs.flush(height)?;
|
||||
info!("Flushed vecs in {}s", i.elapsed().as_secs());
|
||||
let i = Instant::now();
|
||||
file.flush()?;
|
||||
info!("Flushed file in {}s", i.elapsed().as_secs());
|
||||
db.flush()?;
|
||||
info!("Flushed db in {}s", i.elapsed().as_secs());
|
||||
Ok(())
|
||||
};
|
||||
|
||||
@@ -785,22 +782,23 @@ impl Indexer {
|
||||
},
|
||||
)?;
|
||||
|
||||
txindex_to_first_outputindex_reader_opt.take();
|
||||
p2pk65addressindex_to_p2pk65bytes_reader_opt.take();
|
||||
p2pk33addressindex_to_p2pk33bytes_reader_opt.take();
|
||||
p2pkhaddressindex_to_p2pkhbytes_reader_opt.take();
|
||||
p2shaddressindex_to_p2shbytes_reader_opt.take();
|
||||
p2wpkhaddressindex_to_p2wpkhbytes_reader_opt.take();
|
||||
p2wshaddressindex_to_p2wshbytes_reader_opt.take();
|
||||
p2traddressindex_to_p2trbytes_reader_opt.take();
|
||||
p2aaddressindex_to_p2abytes_reader_opt.take();
|
||||
|
||||
if should_export(idxs.height, true) {
|
||||
txindex_to_first_outputindex_reader_opt.take();
|
||||
p2pk65addressindex_to_p2pk65bytes_reader_opt.take();
|
||||
p2pk33addressindex_to_p2pk33bytes_reader_opt.take();
|
||||
p2pkhaddressindex_to_p2pkhbytes_reader_opt.take();
|
||||
p2shaddressindex_to_p2shbytes_reader_opt.take();
|
||||
p2wpkhaddressindex_to_p2wpkhbytes_reader_opt.take();
|
||||
p2wshaddressindex_to_p2wshbytes_reader_opt.take();
|
||||
p2traddressindex_to_p2trbytes_reader_opt.take();
|
||||
p2aaddressindex_to_p2abytes_reader_opt.take();
|
||||
export(stores, vecs, idxs.height, exit)?;
|
||||
}
|
||||
|
||||
let i = Instant::now();
|
||||
file.punch_holes()?;
|
||||
info!("Punched holes in file in {}s", i.elapsed().as_secs());
|
||||
db.punch_holes()?;
|
||||
info!("Punched holes in db in {}s", i.elapsed().as_secs());
|
||||
|
||||
Ok(starting_indexes)
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ use brk_structs::{
|
||||
AddressBytes, AddressBytesHash, BlockHashPrefix, ByAddressType, Height, OutputIndex,
|
||||
OutputType, TxIndex, TxidPrefix, TypeIndex, TypeIndexWithOutputindex, Unit, Version,
|
||||
};
|
||||
use brk_vecs::VecIterator;
|
||||
use fjall::{PersistMode, TransactionalKeyspace};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::VecIterator;
|
||||
|
||||
use crate::Indexes;
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_structs::{
|
||||
AddressBytes, BlockHash, EmptyOutputIndex, Height, InputIndex, OpReturnIndex, OutputIndex,
|
||||
@@ -9,10 +7,10 @@ use brk_structs::{
|
||||
RawLockTime, Sats, StoredBool, StoredF64, StoredU32, StoredU64, Timestamp, TxIndex, TxVersion,
|
||||
Txid, TypeIndex, UnknownOutputIndex, Version, Weight,
|
||||
};
|
||||
use brk_vecs::{
|
||||
AnyCollectableVec, AnyStoredVec, CompressedVec, File, GenericStoredVec, RawVec, Stamp,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{
|
||||
AnyCollectableVec, AnyStoredVec, CompressedVec, Database, GenericStoredVec, RawVec, Stamp,
|
||||
};
|
||||
|
||||
use crate::Indexes;
|
||||
|
||||
@@ -69,225 +67,221 @@ pub struct Vecs {
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(file: &Arc<File>, version: Version) -> Result<Self> {
|
||||
pub fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
emptyoutputindex_to_txindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"txindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_blockhash: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"blockhash",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_difficulty: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"difficulty",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_emptyoutputindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_emptyoutputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_inputindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_inputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_opreturnindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_opreturnindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_outputindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_outputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_p2aaddressindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_p2aaddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_p2msoutputindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_p2msoutputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_p2pk33addressindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_p2pk33addressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_p2pk65addressindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_p2pk65addressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_p2pkhaddressindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_p2pkhaddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_p2shaddressindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_p2shaddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_p2traddressindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_p2traddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_p2wpkhaddressindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_p2wpkhaddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_p2wshaddressindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_p2wshaddressindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_txindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_txindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_first_unknownoutputindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_unknownoutputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_timestamp: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"timestamp",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_total_size: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"total_size",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
height_to_weight: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"weight",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
inputindex_to_outputindex: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"outputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
opreturnindex_to_txindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"txindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
outputindex_to_outputtype: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"outputtype",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
outputindex_to_typeindex: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"typeindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
outputindex_to_value: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"value",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
p2aaddressindex_to_p2abytes: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"p2abytes",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
p2msoutputindex_to_txindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"txindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
p2pk33addressindex_to_p2pk33bytes: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"p2pk33bytes",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
p2pk65addressindex_to_p2pk65bytes: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"p2pk65bytes",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
p2pkhaddressindex_to_p2pkhbytes: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"p2pkhbytes",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
p2shaddressindex_to_p2shbytes: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"p2shbytes",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
p2traddressindex_to_p2trbytes: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"p2trbytes",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
p2wpkhaddressindex_to_p2wpkhbytes: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"p2wpkhbytes",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
p2wshaddressindex_to_p2wshbytes: RawVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"p2wshbytes",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
txindex_to_base_size: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"base_size",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
txindex_to_first_inputindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_inputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
txindex_to_first_outputindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"first_outputindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
txindex_to_is_explicitly_rbf: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"is_explicitly_rbf",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
txindex_to_rawlocktime: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"rawlocktime",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
txindex_to_total_size: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"total_size",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
txindex_to_txid: RawVec::forced_import(
|
||||
file,
|
||||
"txid",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
txindex_to_txid: RawVec::forced_import(db, "txid", version + VERSION + Version::ZERO)?,
|
||||
txindex_to_txversion: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"txversion",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
unknownoutputindex_to_txindex: CompressedVec::forced_import(
|
||||
file,
|
||||
db,
|
||||
"txindex",
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
|
||||
@@ -6,6 +6,7 @@ edition.workspace = true
|
||||
version.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
@@ -13,7 +14,7 @@ brk_computer = { workspace = true }
|
||||
brk_error = { workspace = true }
|
||||
brk_indexer = { workspace = true }
|
||||
brk_structs = { workspace = true }
|
||||
brk_vecs = { workspace = true }
|
||||
vecdb = { workspace = true }
|
||||
derive_deref = { workspace = true }
|
||||
schemars = "1.0.4"
|
||||
serde = { workspace = true }
|
||||
|
||||
+222
-29
@@ -1,34 +1,227 @@
|
||||
# BRK Interface
|
||||
# brk_interface
|
||||
|
||||
<p align="left">
|
||||
<a href="https://github.com/bitcoinresearchkit/brk">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/bitcoinresearchkit/brk?style=social">
|
||||
</a>
|
||||
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
|
||||
<img src="https://img.shields.io/crates/l/brk" alt="License" />
|
||||
</a>
|
||||
<a href="https://crates.io/crates/brk_interface">
|
||||
<img src="https://img.shields.io/crates/v/brk_interface" alt="Version" />
|
||||
</a>
|
||||
<a href="https://docs.rs/brk_interface">
|
||||
<img src="https://img.shields.io/docsrs/brk_interface" alt="Documentation" />
|
||||
</a>
|
||||
<img src="https://img.shields.io/crates/size/brk_interface" alt="Size" />
|
||||
<a href="https://deps.rs/crate/brk_interface">
|
||||
<img src="https://deps.rs/crate/brk_interface/latest/status.svg" alt="Dependency status">
|
||||
</a>
|
||||
<a href="https://discord.gg/HaR3wpH3nr">
|
||||
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
|
||||
</a>
|
||||
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
|
||||
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
|
||||
</a>
|
||||
</p>
|
||||
**Unified data query and formatting interface for Bitcoin datasets**
|
||||
|
||||
A crate that searches for datasets from either `brk_indexer` or `brk_computer` according to given parameters.
|
||||
`brk_interface` provides a clean, unified API for accessing Bitcoin datasets from both indexer and computer components. It serves as the primary data access layer powering BRK's web API and MCP endpoints, offering flexible querying, pagination, and multiple output formats.
|
||||
|
||||
It's possible to search for one or multiple dataset if they have the same index and specify range with both the `from` and `to` being optional and supporting negative values.
|
||||
## What it provides
|
||||
|
||||
The output will depend on the format choson which can be Markdown, Json, CSV or TSV and might vary if there is a one or mutiple datasets, and if one dataset one or multiple values.
|
||||
- **Unified Data Access**: Single interface to query both indexed blockchain data and computed analytics
|
||||
- **Multiple Output Formats**: JSON, CSV, TSV, and Markdown table formatting
|
||||
- **Flexible Pagination**: Range queries with positive/negative indexing and automatic pagination
|
||||
- **Multi-dataset Queries**: Retrieve multiple datasets with the same time index in one call
|
||||
- **Dynamic Search**: Intelligent ID matching with automatic fallbacks and normalization
|
||||
|
||||
In the future, it will support more features similar to a real query engine like in a Postgres databases and presets to fetch data grouped by address, transaction or blockhash/height.
|
||||
## Key Features
|
||||
|
||||
### Query Interface
|
||||
- **25 Time Indices**: From granular (Height, DateIndex) to aggregate (YearIndex, DecadeIndex)
|
||||
- **Bitcoin-Specific Indices**: Address types (P2PKH, P2SH, P2TR), output types, epochs
|
||||
- **Multi-dataset support**: Query multiple related datasets simultaneously
|
||||
- **Intelligent ID resolution**: Flexible matching with automatic fallbacks
|
||||
|
||||
### Pagination and Ranges
|
||||
- **Signed indexing**: Negative indices count from end (`-1` = latest, `-10` = last 10)
|
||||
- **Range queries**: `from`/`to` parameters with optional `count` limit
|
||||
- **Efficient pagination**: 1,000 items per page with proper start/end calculation
|
||||
|
||||
### Output Formatting
|
||||
- **JSON**: Single values, arrays, or matrices with automatic structure detection
|
||||
- **CSV/TSV**: Delimiter-separated values with headers for spreadsheet use
|
||||
- **Markdown**: Tables formatted for documentation and display
|
||||
- **Schema Support**: JSON Schema generation for API documentation
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Query Setup
|
||||
|
||||
```rust
|
||||
use brk_interface::{Interface, Params, ParamsOpt, Index, Format};
|
||||
use brk_indexer::Indexer;
|
||||
use brk_computer::Computer;
|
||||
|
||||
// Load data sources
|
||||
let indexer = Indexer::forced_import("./brk_data")?;
|
||||
let computer = Computer::forced_import("./brk_data", &indexer, None)?;
|
||||
|
||||
// Create unified interface
|
||||
let interface = Interface::build(&indexer, &computer);
|
||||
```
|
||||
|
||||
### Single Dataset Queries
|
||||
|
||||
```rust
|
||||
// Get latest block data
|
||||
let params = Params {
|
||||
index: Index::Height,
|
||||
ids: vec!["date", "timestamp", "difficulty"].into(),
|
||||
rest: ParamsOpt::default()
|
||||
.set_from(-1) // Latest block
|
||||
.set_format(Format::JSON),
|
||||
};
|
||||
|
||||
let result = interface.search_and_format(params)?;
|
||||
println!("{}", result);
|
||||
```
|
||||
|
||||
### Range Queries
|
||||
|
||||
```rust
|
||||
// Get price data for last 30 days
|
||||
let params = Params {
|
||||
index: Index::DateIndex,
|
||||
ids: vec!["price_usd", "price_usd_high", "price_usd_low"].into(),
|
||||
rest: ParamsOpt::default()
|
||||
.set_from(-30) // Last 30 days
|
||||
.set_count(30)
|
||||
.set_format(Format::CSV),
|
||||
};
|
||||
|
||||
let csv_data = interface.search_and_format(params)?;
|
||||
```
|
||||
|
||||
### Multiple Datasets
|
||||
|
||||
```rust
|
||||
// Get comprehensive block statistics
|
||||
let params = Params {
|
||||
index: Index::Height,
|
||||
ids: vec!["size", "weight", "tx_count", "fee_total"].into(),
|
||||
rest: ParamsOpt::default()
|
||||
.set_from(800_000) // Starting from block 800,000
|
||||
.set_to(800_100) // Up to block 800,100
|
||||
.set_format(Format::TSV),
|
||||
};
|
||||
|
||||
let tsv_data = interface.search_and_format(params)?;
|
||||
```
|
||||
|
||||
### Flexible ID Specification
|
||||
|
||||
```rust
|
||||
// Different ways to specify dataset IDs
|
||||
let params = Params {
|
||||
index: Index::DateIndex,
|
||||
ids: vec!["price_usd,volume_usd,market_cap"].into(), // Comma-separated
|
||||
// OR: ids: vec!["price_usd volume_usd market_cap"].into(), // Space-separated
|
||||
// OR: ids: vec!["price_usd", "volume_usd", "market_cap"].into(), // Array
|
||||
rest: ParamsOpt::default()
|
||||
.set_format(Format::JSON),
|
||||
};
|
||||
```
|
||||
|
||||
### Advanced Queries with Pagination
|
||||
|
||||
```rust
|
||||
// Query with custom pagination
|
||||
let params = Params {
|
||||
index: Index::MonthIndex,
|
||||
ids: vec!["supply_total", "supply_active"].into(),
|
||||
rest: ParamsOpt::default()
|
||||
.set_from(0) // From beginning
|
||||
.set_count(50) // Max 50 results
|
||||
.set_format(Format::Markdown),
|
||||
};
|
||||
|
||||
let markdown_table = interface.search_and_format(params)?;
|
||||
```
|
||||
|
||||
## API Methods
|
||||
|
||||
### Core Query Methods
|
||||
|
||||
```rust
|
||||
// Combined search and format
|
||||
let result = interface.search_and_format(params)?;
|
||||
|
||||
// Separate search and format
|
||||
let vecs = interface.search(params)?;
|
||||
let formatted = interface.format(vecs, params.rest.format.unwrap_or_default())?;
|
||||
|
||||
// Get metadata
|
||||
let current_height = interface.get_height();
|
||||
let available_datasets = interface.get_vecids(None)?; // Paginated
|
||||
let available_indices = interface.get_indexes();
|
||||
```
|
||||
|
||||
### Working with Results
|
||||
|
||||
```rust
|
||||
use brk_interface::{Value, Output};
|
||||
|
||||
// Handle different output types
|
||||
match interface.search_and_format(params)? {
|
||||
Output::Json(Value::Single(val)) => println!("Single value: {}", val),
|
||||
Output::Json(Value::List(arr)) => println!("Array with {} items", arr.len()),
|
||||
Output::Json(Value::Matrix(matrix)) => println!("Matrix: {}x{}", matrix.len(), matrix[0].len()),
|
||||
Output::Delimited(csv_data) => println!("CSV/TSV data:\n{}", csv_data),
|
||||
Output::Markdown(table) => println!("Markdown table:\n{}", table),
|
||||
}
|
||||
```
|
||||
|
||||
## Available Indices
|
||||
|
||||
### Time-based Indices
|
||||
- `Height` - Block height (0, 1, 2, ...)
|
||||
- `DateIndex` - Days since Bitcoin genesis
|
||||
- `WeekIndex`, `MonthIndex`, `QuarterIndex`, `YearIndex`, `DecadeIndex`
|
||||
- `HalvingEpoch`, `DifficultyEpoch` - Bitcoin-specific epochs
|
||||
|
||||
### Bitcoin-specific Indices
|
||||
- Address types: `P2PKHAddressIndex`, `P2SHAddressIndex`, `P2WPKHAddressIndex`, etc.
|
||||
- Output types: `OutputType` classifications
|
||||
- Transaction types: `TxIndex`, `InputIndex`, `OutputIndex`
|
||||
|
||||
## Parameter Reference
|
||||
|
||||
```rust
|
||||
pub struct Params {
|
||||
pub index: Index, // Time dimension for query
|
||||
pub ids: MaybeIds, // Dataset identifiers
|
||||
pub rest: ParamsOpt, // Optional parameters
|
||||
}
|
||||
|
||||
pub struct ParamsOpt {
|
||||
pub from: Option<i64>, // Starting index (negative = from end)
|
||||
pub to: Option<i64>, // Ending index (exclusive)
|
||||
pub count: Option<usize>, // Maximum results
|
||||
pub format: Option<Format>, // Output format
|
||||
}
|
||||
```
|
||||
|
||||
## Output Formats
|
||||
|
||||
- **JSON**: Structured data (single values, arrays, matrices)
|
||||
- **CSV**: Comma-separated values with headers
|
||||
- **TSV**: Tab-separated values with headers
|
||||
- **Markdown**: Formatted tables for documentation
|
||||
|
||||
## Performance Features
|
||||
|
||||
- **Zero-copy operations**: Uses references and lifetimes to avoid cloning
|
||||
- **Memory mapping**: Leverages vecdb's memory-mapped storage
|
||||
- **Lazy evaluation**: Only processes data when formatting is requested
|
||||
- **Efficient pagination**: Smart bounds calculation and range queries
|
||||
|
||||
## Schema Support
|
||||
|
||||
The interface provides JSON Schema support for API documentation:
|
||||
|
||||
```rust
|
||||
use schemars::schema_for;
|
||||
|
||||
let schema = schema_for!(Params);
|
||||
// Use schema for API documentation
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `brk_indexer` - Indexed blockchain data source
|
||||
- `brk_computer` - Computed analytics data source
|
||||
- `vecdb` - Vector database with collection traits
|
||||
- `serde` - Serialization/deserialization support
|
||||
- `schemars` - JSON Schema generation
|
||||
|
||||
---
|
||||
|
||||
*This README was generated by Claude Code*
|
||||
@@ -1,7 +1,4 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![doc = "\n## Example\n\n```rust"]
|
||||
#![doc = include_str!("../examples/main.rs")]
|
||||
#![doc = "```"]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@@ -9,8 +6,8 @@ use brk_computer::Computer;
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_structs::Height;
|
||||
use brk_vecs::{AnyCollectableVec, AnyStoredVec};
|
||||
use tabled::settings::Style;
|
||||
use vecdb::{AnyCollectableVec, AnyStoredVec};
|
||||
|
||||
mod deser;
|
||||
mod format;
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
||||
|
||||
use brk_computer::Computer;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vecs::AnyCollectableVec;
|
||||
use vecdb::AnyCollectableVec;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::pagination::{PaginatedIndexParam, PaginationParam};
|
||||
|
||||
@@ -6,6 +6,7 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
|
||||
+189
-27
@@ -1,30 +1,192 @@
|
||||
# BRK Logger
|
||||
# brk_logger
|
||||
|
||||
<p align="left">
|
||||
<a href="https://github.com/bitcoinresearchkit/brk">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/bitcoinresearchkit/brk?style=social">
|
||||
</a>
|
||||
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
|
||||
<img src="https://img.shields.io/crates/l/brk" alt="License" />
|
||||
</a>
|
||||
<a href="https://crates.io/crates/brk_logger">
|
||||
<img src="https://img.shields.io/crates/v/brk_logger" alt="Version" />
|
||||
</a>
|
||||
<a href="https://docs.rs/brk_logger">
|
||||
<img src="https://img.shields.io/docsrs/brk_logger" alt="Documentation" />
|
||||
</a>
|
||||
<img src="https://img.shields.io/crates/size/brk_logger" alt="Size" />
|
||||
<a href="https://deps.rs/crate/brk_logger">
|
||||
<img src="https://deps.rs/crate/brk_logger/latest/status.svg" alt="Dependency status">
|
||||
</a>
|
||||
<a href="https://discord.gg/HaR3wpH3nr">
|
||||
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
|
||||
</a>
|
||||
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
|
||||
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
|
||||
</a>
|
||||
</p>
|
||||
**Logging utilities with colored console output and file logging**
|
||||
|
||||
A simple crate built on top of [`env_logger`](https://crates.io/crates/env_logger) to display logs from the [`log`](https://crates.io/crates/log) crate in a colorful and clean format.
|
||||
`brk_logger` provides a thin wrapper around `env_logger` with BRK-specific defaults, colored console output, and optional file logging. It's designed to provide clear, readable logs for Bitcoin data processing operations.
|
||||
|
||||
It can also save logs into a file if desired.
|
||||
## What it provides
|
||||
|
||||
- **Colored Console Output**: Level-based color coding for easy visual parsing
|
||||
- **File Logging**: Optional log file output with automatic rotation
|
||||
- **Sensible Defaults**: Pre-configured log levels for Bitcoin and dependency crates
|
||||
- **Timestamp Formatting**: Human-readable timestamps with system timezone
|
||||
|
||||
## Key Features
|
||||
|
||||
### Console Logging
|
||||
- **Color-coded levels**: Error (red), Warn (yellow), Info (green), Debug (blue), Trace (cyan)
|
||||
- **Formatted timestamps**: `YYYY-MM-DD HH:MM:SS` format with dimmed styling
|
||||
- **Clean output**: Minimal formatting focused on readability
|
||||
|
||||
### File Logging
|
||||
- **Optional file output**: Writes to specified file path
|
||||
- **Automatic cleanup**: Removes existing log file on initialization
|
||||
- **Append mode**: New log entries appended to file
|
||||
- **Plain text format**: No colors in file output for better compatibility
|
||||
|
||||
### Dependency Filtering
|
||||
Pre-configured to suppress noisy logs from common dependencies:
|
||||
- `bitcoin=off` - Bitcoin protocol library
|
||||
- `bitcoincore-rpc=off` - RPC client
|
||||
- `fjall=off` - Key-value store
|
||||
- `lsm_tree=off` - LSM tree implementation
|
||||
- `rolldown=off` - Bundler
|
||||
- `tracing=off` - Tracing framework
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Setup (Console Only)
|
||||
|
||||
```rust
|
||||
use brk_logger;
|
||||
|
||||
// Initialize with console output only
|
||||
brk_logger::init(None)?;
|
||||
|
||||
// Now use standard logging macros
|
||||
log::info!("BRK starting up");
|
||||
log::warn!("Bitcoin Core not fully synced");
|
||||
log::error!("Failed to connect to RPC");
|
||||
```
|
||||
|
||||
### With File Logging
|
||||
|
||||
```rust
|
||||
use std::path::Path;
|
||||
|
||||
// Initialize with both console and file output
|
||||
let log_path = Path::new("~/.brk/brk.log");
|
||||
brk_logger::init(Some(log_path))?;
|
||||
|
||||
log::info!("Logs will appear in console and file");
|
||||
```
|
||||
|
||||
### Environment Variable Control
|
||||
|
||||
```bash
|
||||
# Set log level via environment variable
|
||||
export RUST_LOG=debug
|
||||
export RUST_LOG=info,brk_parser=debug # Override for specific crates
|
||||
|
||||
# Run with custom log level
|
||||
RUST_LOG=trace brk
|
||||
```
|
||||
|
||||
### Using Color Utilities
|
||||
|
||||
The crate re-exports `OwoColorize` for consistent coloring:
|
||||
|
||||
```rust
|
||||
use brk_logger::OwoColorize;
|
||||
|
||||
println!("Success: {}", "Operation completed".green());
|
||||
println!("Warning: {}", "Low disk space".yellow());
|
||||
println!("Error: {}", "Connection failed".red());
|
||||
println!("Info: {}", "Processing block 800000".bright_black());
|
||||
```
|
||||
|
||||
## Log Format
|
||||
|
||||
### Console Output
|
||||
```
|
||||
2024-12-25 10:30:15 - info Starting BRK indexer
|
||||
2024-12-25 10:30:16 - warn Bitcoin Core still syncing (99.8% complete)
|
||||
2024-12-25 10:30:45 - info Indexed block 900000 (1.2M transactions)
|
||||
2024-12-25 10:30:46 - error Connection to RPC failed, retrying...
|
||||
```
|
||||
|
||||
### File Output
|
||||
```
|
||||
2024-12-25 10:30:15 - info Starting BRK indexer
|
||||
2024-12-25 10:30:16 - warn Bitcoin Core still syncing (99.8% complete)
|
||||
2024-12-25 10:30:45 - info Indexed block 900000 (1.2M transactions)
|
||||
2024-12-25 10:30:46 - error Connection to RPC failed, retrying...
|
||||
```
|
||||
|
||||
## Default Log Levels
|
||||
|
||||
The logger uses these default settings:
|
||||
|
||||
- **Default level**: `info` - Shows important operational information
|
||||
- **Suppressed crates**: Dependencies that produce excessive output are set to `off`
|
||||
- **Override capability**: Can be overridden via `RUST_LOG` environment variable
|
||||
|
||||
### Common Log Level Settings
|
||||
|
||||
```bash
|
||||
# Minimal output (errors and warnings only)
|
||||
RUST_LOG=warn
|
||||
|
||||
# Standard output (recommended)
|
||||
RUST_LOG=info
|
||||
|
||||
# Verbose output (for debugging)
|
||||
RUST_LOG=debug
|
||||
|
||||
# Maximum output (for development)
|
||||
RUST_LOG=trace
|
||||
|
||||
# Mixed levels (info by default, debug for specific crates)
|
||||
RUST_LOG=info,brk_indexer=debug,brk_computer=trace
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### In BRK CLI
|
||||
|
||||
```rust
|
||||
use brk_logger;
|
||||
use log::info;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Initialize logging early in main
|
||||
brk_logger::init(Some(Path::new("~/.brk/brk.log")))?;
|
||||
|
||||
info!("BRK CLI starting");
|
||||
|
||||
// ... rest of application
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### In Custom Applications
|
||||
|
||||
```rust
|
||||
use brk_logger::{self, OwoColorize};
|
||||
use log::{info, warn, error};
|
||||
|
||||
fn setup_logging() -> std::io::Result<()> {
|
||||
// Console only for development
|
||||
brk_logger::init(None)?;
|
||||
|
||||
info!("Application initialized");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_data() {
|
||||
info!("Processing Bitcoin data...");
|
||||
|
||||
// Use color utilities for progress
|
||||
println!("Progress: {}", "50%".green());
|
||||
|
||||
warn!("Large memory usage detected");
|
||||
error!("Critical error: {}", "Database connection lost".red());
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- **Minimal overhead**: Lightweight wrapper around `env_logger`
|
||||
- **Lazy evaluation**: Log messages only formatted when level is enabled
|
||||
- **File I/O**: Asynchronous file writing doesn't block main thread
|
||||
- **Memory usage**: No buffering, logs written immediately
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `env_logger` - Core logging implementation
|
||||
- `owo_colors` - Terminal color support
|
||||
- `jiff` - Modern date/time handling for timestamps
|
||||
|
||||
---
|
||||
|
||||
*This README was generated by Claude Code*
|
||||
@@ -1,10 +1,14 @@
|
||||
use std::io;
|
||||
|
||||
use log::{debug, error, info, trace};
|
||||
|
||||
fn main() {
|
||||
brk_logger::init(None);
|
||||
fn main() -> io::Result<()> {
|
||||
brk_logger::init(None)?;
|
||||
|
||||
info!("info");
|
||||
debug!("debug");
|
||||
error!("error");
|
||||
trace!("trace");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![doc = "\n## Example\n\n```rust"]
|
||||
#![doc = include_str!("../examples/main.rs")]
|
||||
#![doc = "```"]
|
||||
|
||||
use std::{
|
||||
fmt::Display,
|
||||
fs::{self, OpenOptions},
|
||||
io::Write,
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
@@ -15,7 +12,7 @@ use jiff::{Timestamp, tz};
|
||||
pub use owo_colors::OwoColorize;
|
||||
|
||||
#[inline]
|
||||
pub fn init(path: Option<&Path>) {
|
||||
pub fn init(path: Option<&Path>) -> io::Result<()> {
|
||||
let file = path.map(|path| {
|
||||
let _ = fs::remove_file(path);
|
||||
OpenOptions::new()
|
||||
@@ -26,7 +23,7 @@ pub fn init(path: Option<&Path>) {
|
||||
});
|
||||
|
||||
Builder::from_env(Env::default().default_filter_or(
|
||||
"info,bitcoin=off,bitcoincore-rpc=off,fjall=off,lsm_tree=off,rolldown=off,brk_rolldown=off,rmcp=off,rmcp=off,tracing=off",
|
||||
"info,bitcoin=off,bitcoincore-rpc=off,fjall=off,lsm_tree=off,rolldown=off,brk_rolldown=off,rmcp=off,brk_rmcp=off,tracing=off",
|
||||
))
|
||||
.format(move |buf, record| {
|
||||
let date_time = Timestamp::now()
|
||||
@@ -71,6 +68,8 @@ pub fn init(path: Option<&Path>) {
|
||||
)
|
||||
})
|
||||
.init();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(
|
||||
|
||||
@@ -6,13 +6,14 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
axum = { workspace = true }
|
||||
brk_interface = { workspace = true }
|
||||
log = { workspace = true }
|
||||
brk_rmcp = { version = "0.3.0", features = [
|
||||
brk_rmcp = { version = "0.6.0", features = [
|
||||
"transport-worker",
|
||||
"transport-streamable-http-server",
|
||||
] }
|
||||
|
||||
+198
-26
@@ -1,49 +1,221 @@
|
||||
# BRK MCP
|
||||
# brk_mcp
|
||||
|
||||
A Model Context Protocol (MCP) which gives LLMs access to all available tools in BRK
|
||||
**Model Context Protocol (MCP) bridge for LLM integration with BRK**
|
||||
|
||||
## URLs
|
||||
`brk_mcp` provides a Model Context Protocol server that enables Large Language Models (LLMs) to access Bitcoin blockchain data through BRK's interface layer. It implements the MCP specification to expose BRK's analytics capabilities as tools that LLMs can call.
|
||||
|
||||
- https://eu1.bitcoinresearchkit.org/mcp
|
||||
- https://eu2.bitcoinresearchkit.org/mcp
|
||||
## What it provides
|
||||
|
||||
## Usage
|
||||
- **MCP Server Implementation**: Standards-compliant Model Context Protocol server
|
||||
- **Bitcoin Data Tools**: LLM-accessible tools for querying blockchain analytics
|
||||
- **Type-Safe Parameters**: Structured tool parameters with validation
|
||||
- **Multi-Format Output**: JSON responses for LLM consumption
|
||||
- **BRK Integration**: Direct access to all indexed and computed datasets
|
||||
|
||||
To connect to the MCP use any of the previous URL, no token or auth is needed.
|
||||
## Available MCP Tools
|
||||
|
||||
This implementation has only been tested with Claude and the [MCP inspector](https://modelcontextprotocol.io/docs/tools/inspector).
|
||||
Based on the actual implementation, the following tools are available:
|
||||
|
||||
Please be aware that the technology is evolving very rapidly, thus having issues is probably expected. If you, you can join the discord see if there is a solution.
|
||||
### Metadata Tools
|
||||
|
||||
### Claude
|
||||
#### `get_index_count`
|
||||
Get the count of all existing indexes.
|
||||
|
||||
#### Step 1
|
||||
**Parameters:** None
|
||||
**Returns:** Number of available time indices
|
||||
|
||||
First we need to connect BRK to Claude. To do that we need to go to the "Connect apps" menu from the home screen of Claude desktop.
|
||||
#### `get_vecid_count`
|
||||
Get the count of all existing vector IDs.
|
||||
|
||||

|
||||
**Parameters:** None
|
||||
**Returns:** Number of available dataset identifiers
|
||||
|
||||
#### Step 2
|
||||
#### `get_vec_count`
|
||||
Get the count of all existing vectors (sum of supported indexes for each vector ID).
|
||||
|
||||
Then simply go to "Add integration".
|
||||
**Parameters:** None
|
||||
**Returns:** Total number of vector/index combinations
|
||||
|
||||

|
||||
#### `get_indexes`
|
||||
Get the list of all existing indexes.
|
||||
|
||||
#### Step 3
|
||||
**Parameters:** None
|
||||
**Returns:** Array of available index names (height, date, week, month, etc.)
|
||||
|
||||
Claude's MCP client is (for now?) session based thus using a URL pointing to a load balancer will not work.
|
||||
#### `get_accepted_indexes`
|
||||
Get an object with all existing indexes as keys and their accepted variants as values.
|
||||
|
||||
Use one of the following URL instead:
|
||||
**Parameters:** None
|
||||
**Returns:** Object mapping indexes to their accepted variant names
|
||||
|
||||
- https://eu1.bitcoinresearchkit.org/mcp
|
||||
- https://eu2.bitcoinresearchkit.org/mcp
|
||||
### Discovery Tools
|
||||
|
||||

|
||||
#### `get_vecids`
|
||||
Get a paginated list of all existing vector IDs.
|
||||
|
||||
#### Step 4
|
||||
**Parameters:**
|
||||
- `page` (optional number): Page number (default: 0, up to 1,000 results per page)
|
||||
|
||||
Verify that it has access to BRK's tools.
|
||||
**Returns:** Array of dataset identifiers
|
||||
|
||||
Optionally and highly recommended, giving it unsupervised access gives a more fluid experience and prevents possible issues and errors.
|
||||
#### `get_index_to_vecids`
|
||||
Get a paginated list of all vector IDs which support a given index.
|
||||
|
||||

|
||||
**Parameters:**
|
||||
- `index` (string): Index name to query
|
||||
- `page` (optional number): Page number (default: 0)
|
||||
|
||||
**Returns:** Array of vector IDs that support the specified index
|
||||
|
||||
#### `get_vecid_to_indexes`
|
||||
Get a list of all indexes supported by a given vector ID.
|
||||
|
||||
**Parameters:**
|
||||
- `id` (string): Vector ID to query
|
||||
|
||||
**Returns:** Array of indexes supported by the vector ID (empty if ID doesn't exist)
|
||||
|
||||
### Data Query Tool
|
||||
|
||||
#### `get_vecs`
|
||||
Get one or multiple vectors depending on given parameters.
|
||||
|
||||
**Parameters:**
|
||||
- `index` (string): Time dimension (height, date, week, month, etc.)
|
||||
- `ids` (string): Dataset identifiers (comma or space separated)
|
||||
- `from` (optional i64): Start index (negative = from end)
|
||||
- `to` (optional i64): End index (exclusive)
|
||||
- `count` (optional usize): Maximum results
|
||||
- `format` (optional string): Output format (json, csv, tsv, md)
|
||||
|
||||
**Response format depends on parameters:**
|
||||
- **Single value**: One vector, one result (e.g., `from=-1`)
|
||||
- **Array**: One vector, multiple results (e.g., `from=-100&count=100`)
|
||||
- **Matrix**: Multiple vectors (always matrix, even for single results)
|
||||
|
||||
### System Tool
|
||||
|
||||
#### `get_version`
|
||||
Get the running version of the Bitcoin Research Kit.
|
||||
|
||||
**Parameters:** None
|
||||
**Returns:** Version string (e.g., "v0.0.88")
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Discovery Workflow
|
||||
|
||||
```
|
||||
1. Call get_indexes to see available time dimensions
|
||||
2. Call get_vecids to see available datasets (paginated)
|
||||
3. Call get_index_to_vecids to find datasets for specific timeframes
|
||||
4. Call get_vecs to query actual data
|
||||
```
|
||||
|
||||
### Basic Data Query
|
||||
|
||||
```json
|
||||
// Get latest Bitcoin price
|
||||
{
|
||||
"tool": "get_vecs",
|
||||
"parameters": {
|
||||
"index": "date",
|
||||
"ids": "close",
|
||||
"from": -1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-Dataset Query
|
||||
|
||||
```json
|
||||
// Get last 30 days of OHLC data
|
||||
{
|
||||
"tool": "get_vecs",
|
||||
"parameters": {
|
||||
"index": "date",
|
||||
"ids": "open,high,low,close",
|
||||
"from": -30,
|
||||
"format": "csv"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Metadata Exploration
|
||||
|
||||
```json
|
||||
// Discover what's available
|
||||
{
|
||||
"tool": "get_accepted_indexes"
|
||||
}
|
||||
|
||||
// Find datasets for weekly analysis
|
||||
{
|
||||
"tool": "get_index_to_vecids",
|
||||
"parameters": {
|
||||
"index": "week"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Server Configuration
|
||||
|
||||
The MCP server is stateless and integrates with BRK's HTTP server:
|
||||
|
||||
```rust
|
||||
// In brk_server routes
|
||||
router.add_mcp_routes(interface, true) // Enable MCP at /mcp endpoint
|
||||
```
|
||||
|
||||
**Server Info:**
|
||||
- Protocol version: Latest MCP specification
|
||||
- Capabilities: Tools enabled
|
||||
- Instructions: Provides context about Bitcoin data access
|
||||
- Stateless mode: Compatible with load balancers
|
||||
|
||||
## Error Handling
|
||||
|
||||
The MCP server provides structured error responses following MCP specification:
|
||||
- Invalid parameters result in proper MCP error responses
|
||||
- Tool failures are handled gracefully
|
||||
- Parameter validation ensures type safety
|
||||
|
||||
## Integration
|
||||
|
||||
### With BRK Server
|
||||
MCP is exposed at the `/mcp` endpoint when enabled:
|
||||
```
|
||||
POST /mcp
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "get_vecs",
|
||||
"arguments": {
|
||||
"index": "height",
|
||||
"ids": "timestamp,size",
|
||||
"from": -1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### LLM Instructions
|
||||
The server provides context to LLMs:
|
||||
- Explains Bitcoin data terminology (vectors, indexes, vecids)
|
||||
- Clarifies that vectors/vecs/arrays/datasets are interchangeable terms
|
||||
- Describes indexes as timeframes and vecids as dataset names
|
||||
- Encourages exploration before web browsing
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `brk_interface` - Data access and formatting layer
|
||||
- `brk_rmcp` - Rust MCP implementation
|
||||
- `axum` - HTTP router integration
|
||||
- `log` - Request logging
|
||||
|
||||
---
|
||||
|
||||
*This README was generated by Claude Code*
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
// #![doc = "\n## Example\n\n```rust"]
|
||||
// #![doc = include_str!("../examples/main.rs")]
|
||||
// #![doc = "```"]
|
||||
|
||||
use brk_interface::{IdParam, Interface, PaginatedIndexParam, PaginationParam, Params};
|
||||
use brk_rmcp::{
|
||||
|
||||
@@ -26,7 +26,7 @@ where
|
||||
move || Ok(MCP::new(interface)),
|
||||
LocalSessionManager::default().into(),
|
||||
StreamableHttpServerConfig {
|
||||
// stateful_mode: false, // breaks Claude
|
||||
stateful_mode: false,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
Generated
-475
@@ -1,475 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "base58ck"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f"
|
||||
dependencies = [
|
||||
"bitcoin-internals",
|
||||
"bitcoin_hashes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "bech32"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d"
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin"
|
||||
version = "0.32.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026"
|
||||
dependencies = [
|
||||
"base58ck",
|
||||
"bech32",
|
||||
"bitcoin-internals",
|
||||
"bitcoin-io",
|
||||
"bitcoin-units",
|
||||
"bitcoin_hashes",
|
||||
"hex-conservative",
|
||||
"hex_lit",
|
||||
"secp256k1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin-internals"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin-io"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56"
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin-units"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2"
|
||||
dependencies = [
|
||||
"bitcoin-internals",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin_hashes"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
|
||||
dependencies = [
|
||||
"bitcoin-io",
|
||||
"hex-conservative",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitcoincore-rpc"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee"
|
||||
dependencies = [
|
||||
"bitcoincore-rpc-json",
|
||||
"jsonrpc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitcoincore-rpc-json"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "biter"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"bitcoincore-rpc",
|
||||
"crossbeam",
|
||||
"derived-deref",
|
||||
"rayon",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "derived-deref"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "805ef2023ccd65425743a91ecd11fc020979a0b01921db3104fb606d18a7b43e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex-conservative"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex_lit"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"minreq",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minreq"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "763d142cdff44aaadd9268bebddb156ef6c65a0e13486bb81673cf2d8739f9b0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1"
|
||||
version = "0.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113"
|
||||
dependencies = [
|
||||
"bitcoin_hashes",
|
||||
"rand",
|
||||
"secp256k1-sys",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1-sys"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@@ -8,6 +8,7 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user