Compare commits
187 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ec64f8d048 | |||
| ed288a9dba | |||
| 27da0a4102 | |||
| 3c01ba1a76 | |||
| 252c8833ae | |||
| f45fb6efe6 | |||
| 8cc1f8d691 | |||
| bff22b5182 | |||
| d31d47eb32 | |||
| 5fe984c39d | |||
| 7f07b0daa7 | |||
| 6d35c26b3f | |||
| be4e693a27 | |||
| 5810276156 | |||
| d10ac3f87b | |||
| 9810bc09e9 | |||
| a0a13eb2a8 | |||
| 6e996797b8 | |||
| 663092b501 | |||
| 8ea13544de | |||
| e73daa6214 | |||
| cf92c60a01 | |||
| b7f51b03bc | |||
| 903e69ff77 | |||
| c4167ddaad | |||
| 50bfdb0d68 | |||
| a6cb09ff1c | |||
| e4c9f23476 | |||
| 44e5415d43 | |||
| 1c653693ed | |||
| 39c470ad7a | |||
| 1103e538a5 | |||
| c0cd4cba6f | |||
| b91120e8d4 | |||
| 005774a4c2 | |||
| 16bbfebfba | |||
| 15505cd82d | |||
| 016d80e002 | |||
| 0f3c267a48 | |||
| 589bb02411 | |||
| c0f4ece17b | |||
| c3ae3cb768 | |||
| c9e0f9d985 | |||
| e3431c2fa3 | |||
| 5979b9771e | |||
| aa61832fb2 | |||
| 2ac6e982b1 | |||
| 3204ddcf07 | |||
| c87b1c133c | |||
| 9b275ecdae | |||
| d6fd7de361 | |||
| 49d66a133e | |||
| c559f26d0e | |||
| bbe9f1bad2 | |||
| 7e1fb6472d | |||
| 0ff8d20573 | |||
| 9c1f9448dc | |||
| 43a6081dd6 | |||
| 985e961876 | |||
| 098f6de047 | |||
| 1b0f90fd68 | |||
| 12252f407b | |||
| 3b6e3f47ab | |||
| 6a9ac9b025 | |||
| ae6aa4088b | |||
| c08f431180 | |||
| 123c1f56e9 | |||
| 35ac65a864 | |||
| e9f362cc87 | |||
| 65685c23e1 | |||
| 2f74748cea | |||
| f477bd66f3 | |||
| d7d77ae8f0 | |||
| 31110a740d | |||
| b64d8b1d7f | |||
| c46006aacc | |||
| 92f81b1493 | |||
| 70213cfc8f | |||
| 8a82bf5c50 | |||
| 37405384a2 | |||
| 54ea6cc53b | |||
| 339c00d815 | |||
| ea6b4dcde2 | |||
| 2b84623d1e | |||
| c8b3afa56b | |||
| 1348f3c24c | |||
| 62208ce3e1 | |||
| 813b2481de | |||
| 27b924ba61 | |||
| b40170b8ce | |||
| 8bfa9d2734 | |||
| c7cf76d4a8 | |||
| dfd2969b3e | |||
| 0e1866fe1d | |||
| b9ae46b913 | |||
| 06e7284055 | |||
| 93289e8fca | |||
| 130d5057d4 | |||
| be492d5084 | |||
| e0bf1d736f | |||
| 5a6b71cbeb | |||
| e6934cd5e2 | |||
| b5aada0792 | |||
| 165ea83ac3 | |||
| 440a82dee4 | |||
| 9c2d3e5e26 | |||
| 6fb6abcbe5 | |||
| dc449dafd1 | |||
| ecdaeebbfb | |||
| fa958b59bd | |||
| fb3d8521cd | |||
| 608c401cf3 | |||
| 1c3da90a24 | |||
| 34567f3375 | |||
| 51bcbeb48f | |||
| cc0f9c42df | |||
| a11bf5523b | |||
| 1921c3d901 | |||
| d568469e8b | |||
| 20d5c7e8d5 | |||
| 9f289ed9de | |||
| 93ee5e480b | |||
| 98a312701f | |||
| cbcf603b63 | |||
| f976f672cf | |||
| cfc3081e8a | |||
| 99818924ee | |||
| 9bbf3a027f | |||
| 93e01902e3 | |||
| 34919aba05 | |||
| a8ee4cf57f | |||
| b39548b4c6 | |||
| 4217c22ff6 | |||
| 4ab10670c9 | |||
| 2883f88de6 | |||
| e002a61a19 | |||
| 5893376279 | |||
| 411c5e4c4d | |||
| c2a77072d2 | |||
| c8a25934a6 | |||
| 7b38355cd4 | |||
| ddc54e0b98 | |||
| 8a7003782b | |||
| 8e6464dacb | |||
| 92b1dc0afb | |||
| 7562f51e07 | |||
| 09bba99e68 | |||
| 9d674cd49b | |||
| 88a0c9ea03 | |||
| 5014e0ce3e | |||
| b7a1ee9ebc | |||
| 292ceddd66 | |||
| 4b52b80000 | |||
| 9f20664c6e | |||
| 851a6aac0e | |||
| 1f1e73c47a | |||
| 112f61ca18 | |||
| 96eeacbe2b | |||
| 3f62da879c | |||
| aa30feb875 | |||
| 9ba3c2b7c5 | |||
| 320c708e10 | |||
| efa7294f59 | |||
| ae0e092935 | |||
| c77aecbfce | |||
| 700352ec45 | |||
| 664b125ce2 | |||
| 5f4b1c9e32 | |||
| d11d3f19bd | |||
| f34f4f2738 | |||
| 15db7c2310 | |||
| f9257ed04d | |||
| 15e6ef8488 | |||
| 9ae0a57f22 | |||
| 1e38c21f8e | |||
| bdc3c19163 | |||
| d55478da54 | |||
| 82bcc55645 | |||
| 07618ebe43 | |||
| 1492834d1e | |||
| 5ab6197356 | |||
| 0a789fe551 | |||
| caa8ff23ed | |||
| ee30d1d36d | |||
| 0d9415db9d | |||
| 8020e1126f | |||
| 3439422057 |
@@ -1,2 +0,0 @@
|
||||
[build]
|
||||
rustflags = ["-C", "target-cpu=native"]
|
||||
@@ -3,36 +3,18 @@
|
||||
|
||||
# Builds
|
||||
target
|
||||
dist
|
||||
vecid-to-indexes.js
|
||||
|
||||
# Copies
|
||||
*\ copy*
|
||||
|
||||
# Ignored
|
||||
/_*
|
||||
_*
|
||||
|
||||
# Editors
|
||||
.vscode
|
||||
.zed
|
||||
|
||||
# Flamegraph
|
||||
flamegraph/
|
||||
flamegraph.svg
|
||||
|
||||
# Benchmarks
|
||||
benches
|
||||
|
||||
# Snapshots
|
||||
snapshots*/
|
||||
|
||||
# Docker
|
||||
docker/kibo
|
||||
|
||||
# Types
|
||||
paths.d.ts
|
||||
|
||||
# Outputs
|
||||
_outputs
|
||||
|
||||
|
||||
# Logs
|
||||
.log
|
||||
|
||||
@@ -1,44 +1,17 @@
|
||||
<!--
|
||||
# v0.X.Y | WIP
|
||||

|
||||

|
||||
-->
|
||||
|
||||
# v0.6.0 | WIP | A new beginning
|
||||
# v0.X.0 | WIP | A new beginning
|
||||
|
||||
## Global
|
||||

|
||||
|
||||
- Completely redesign the back-end
|
||||
Full rewrite
|
||||
|
||||
- Merged parser and server crates into a single project (and thus executable), so now both will run at the same time with a single `cargo run -r` [#7392982](https://github.com/kibo-money/kibo/commit/7392982824c2db94bcd57251fd41986117c29a23)
|
||||
- Added `--no-server` and `--no-parser` to disable each if needed
|
||||
- Improved executable parameters
|
||||
- Started using `log` and `env_logger` crates instead of custom code [#7392982](https://github.com/kibo-money/kibo/commit/7392982824c2db94bcd57251fd41986117c29a23)
|
||||
- Improved logs
|
||||
- Fixed input being unfocused right after being focused in Brave browser [#9a9ae61](https://github.com/kibo-money/kibo/commit/9a9ae614d07b54c08b7e9c0e2aefe3b52fdb93c5)
|
||||
# [kibo-v0.5.0](https://github.com/bitcoinresearchkit/brk/tree/eea56d394bf92c62c81da8b78b8c47ea730683f5) | [873199](https://mempool.space/block/0000000000000000000270925aa6a565be92e13164565a3f7994ca1966e48050) - 2024/12/04
|
||||
|
||||
- Reworked server's API code [#6ab0f46]( https://github.com/kibo-money/kibo/commit/6ab0f463119a902a1b7ca9691b54f61543bb8f2f)
|
||||
- New route format: `/api/date-to-realized-price` is now `/api/realized-price?kind=date`
|
||||
- Added status and timing to logs
|
||||
- Updated website packages
|
||||
- Added API support for datasets by timestamp (by merging any dataset by height with the height to timestamp dataset and so it still uses heights as chunk ids) [#ca00f3f](https://github.com/kibo-money/kibo/commit/ca00f3f71526f0c5c16021024fec7e5c6e47221c)
|
||||
- `/api/realized-price?kind=t`
|
||||
- `/api/realized-price?kind=timestamp&chunk=860000`
|
||||
- Created separate crate for indexing called `bindex`
|
||||
- Created a crate a storage engine specialized in storing datasets that have indexes as keys and thus can be represented by an array/vec called `storable-vec`
|
||||
- Removed the need for the `-txindex=1` parameter when starting your Bitcoin Core node as kibo has its own indexes now
|
||||
|
||||
## Git
|
||||
|
||||
Added git tags for each version though Markdown won't display formatted on Github so left the default text
|
||||
|
||||
## Deprecated
|
||||
|
||||
Moved Sanakirja database wrapper to its own crate (`snkrj`) and added a robust auto defragmentation to improve disk usage without the need for user's intervention.
|
||||
Since it's not used anymore it will moved out of the repository relatively soon.
|
||||
|
||||
# [kibo-v0.5.0](https://github.com/kibo-money/kibo/tree/eea56d394bf92c62c81da8b78b8c47ea730683f5) | [873199](https://mempool.space/block/0000000000000000000270925aa6a565be92e13164565a3f7994ca1966e48050) - 2024/12/04
|
||||
|
||||

|
||||

|
||||
|
||||
## Datasets
|
||||
|
||||
@@ -103,9 +76,9 @@ Since it's not used anymore it will moved out of the repository relatively soon.
|
||||
|
||||
- Moved back to this repo
|
||||
|
||||
# [kibo-v0.4.0](https://github.com/kibo-money/kibo/tree/a64c544815d9ef785e2fc1323582f774f16b9200) | [861950](https://mempool.space/block/00000000000000000000530d0e30ccf7deeace122dcc99f2668a06c6dad83629) - 2024/09/19
|
||||
# [kibo-v0.4.0](https://github.com/bitcoinresearchkit/brk/tree/a64c544815d9ef785e2fc1323582f774f16b9200) | [861950](https://mempool.space/block/00000000000000000000530d0e30ccf7deeace122dcc99f2668a06c6dad83629) - 2024/09/19
|
||||
|
||||

|
||||

|
||||
|
||||
## Brand
|
||||
|
||||
@@ -140,9 +113,9 @@ Since it's not used anymore it will moved out of the repository relatively soon.
|
||||
- Added serving of the website
|
||||
- Improved `Cache-Control` behavior
|
||||
|
||||
# [kibo-v0.3.0](https://github.com/kibo-money/kibo/tree/b68b016091c45b071218fba01bac5b76e8eaf18c) | [853930](https://mempool.space/block/00000000000000000002eb5e9a7950ca2d5d98bd1ed28fc9098aa630d417985d) - 2024/07/26
|
||||
# [satonomics-v0.3.0](https://github.com/bitcoinresearchkit/brk/tree/b68b016091c45b071218fba01bac5b76e8eaf18c) | [853930](https://mempool.space/block/00000000000000000002eb5e9a7950ca2d5d98bd1ed28fc9098aa630d417985d) - 2024/07/26
|
||||
|
||||

|
||||

|
||||
|
||||
## Parser
|
||||
|
||||
@@ -219,9 +192,9 @@ Since it's not used anymore it will moved out of the repository relatively soon.
|
||||
- Only run with a watcher if `cargo watch` is available
|
||||
- Removed id_to_path file in favor for only `paths.d.ts` in `app/src/types`
|
||||
|
||||
# [kibo-v0.2.0](https://github.com/kibo-money/kibo/tree/248187889283597c5dbb806292297453c25e97b8) | [851286](https://mempool.space/block/0000000000000000000281ca7f1bf8c50702bfca168c7af1bdc67c977c1ac8ed) - 2024/07/08
|
||||
# [satonomics-v0.2.0](https://github.com/bitcoinresearchkit/brk/tree/248187889283597c5dbb806292297453c25e97b8) | [851286](https://mempool.space/block/0000000000000000000281ca7f1bf8c50702bfca168c7af1bdc67c977c1ac8ed) - 2024/07/08
|
||||
|
||||

|
||||

|
||||
|
||||
## App
|
||||
|
||||
@@ -255,9 +228,9 @@ Since it's not used anymore it will moved out of the repository relatively soon.
|
||||
|
||||
- Fixed ulimit only being run in Mac OS instead of whenever the program is detected
|
||||
|
||||
# [kibo-v0.1.1](https://github.com/kibo-money/kibo/tree/e55b5195a9de9aea306903c94ed63cb1720fda5f) | [849240](https://mempool.space/block/000000000000000000002b8653988655071c07bb5f7181c038f9326bc86db741) - 2024/06/24
|
||||
# [satonomics-v0.1.1](https://github.com/bitcoinresearchkit/brk/tree/e55b5195a9de9aea306903c94ed63cb1720fda5f) | [849240](https://mempool.space/block/000000000000000000002b8653988655071c07bb5f7181c038f9326bc86db741) - 2024/06/24
|
||||
|
||||

|
||||

|
||||
|
||||
## Parser
|
||||
|
||||
@@ -305,10 +278,10 @@ Since it's not used anymore it will moved out of the repository relatively soon.
|
||||
|
||||
- Deleted old price datasets and their backups
|
||||
|
||||
# [kibo-v0.1.0](https://github.com/kibo-money/kibo/tree/a1a576d088c8f83ed32d48753a7611f70a964574) | [848642](https://mempool.space/block/000000000000000000020be5761d70751252219a9557f55e91ecdfb86c4e026a) - 2024/06/19
|
||||
# [satonomics-v0.1.0](https://github.com/bitcoinresearchkit/brk/tree/a1a576d088c8f83ed32d48753a7611f70a964574) | [848642](https://mempool.space/block/000000000000000000020be5761d70751252219a9557f55e91ecdfb86c4e026a) - 2024/06/19
|
||||
|
||||

|
||||

|
||||
|
||||
# kibo-v0.0.1 | [835444](https://mempool.space/block/000000000000000000009f93907a0dd83c080d5585cc7ec82c076d45f6d7c872) - 2024/03/20
|
||||
# satonomics-v0.0.1 | [835444](https://mempool.space/block/000000000000000000009f93907a0dd83c080d5585cc7ec82c076d45f6d7c872) - 2024/03/20
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -4,7 +4,8 @@ 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.28"
|
||||
package.version = "0.0.73"
|
||||
package.homepage = "https://bitcoinresearchkit.org"
|
||||
package.repository = "https://github.com/bitcoinresearchkit/brk"
|
||||
|
||||
[profile.release]
|
||||
@@ -12,37 +13,54 @@ lto = "fat"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[profile.profiling]
|
||||
inherits = "release"
|
||||
debug = true
|
||||
|
||||
[profile.dist]
|
||||
inherits = "release"
|
||||
|
||||
[workspace.dependencies]
|
||||
axum = "0.8.3"
|
||||
bitcoin = { version = "0.32.5", features = ["serde"] }
|
||||
arc-swap = "1.7.1"
|
||||
axum = "0.8.4"
|
||||
bincode = { version = "2.0.1", features = ["serde"] }
|
||||
bitcoin = { version = "0.32.6", features = ["serde"] }
|
||||
bitcoincore-rpc = "0.19.0"
|
||||
brk_cli = { version = "0", path = "crates/brk_cli" }
|
||||
brk_computer = { version = "0", path = "crates/brk_computer" }
|
||||
brk_core = { version = "0", path = "crates/brk_core" }
|
||||
brk_exit = { version = "0", path = "crates/brk_exit" }
|
||||
brk_fetcher = { version = "0", path = "crates/brk_fetcher" }
|
||||
brk_indexer = { version = "0", path = "crates/brk_indexer" }
|
||||
brk_logger = { version = "0", path = "crates/brk_logger" }
|
||||
brk_parser = { version = "0", path = "crates/brk_parser" }
|
||||
brk_query = { version = "0", path = "crates/brk_query" }
|
||||
brk_server = { version = "0", path = "crates/brk_server" }
|
||||
brk_vec = { version = "0", path = "crates/brk_vec" }
|
||||
byteview = "0.6.1"
|
||||
clap = { version = "4.5.36", features = ["derive", "string"] }
|
||||
color-eyre = "0.6.3"
|
||||
brk_bundler = { version = "0.0.73", path = "crates/brk_bundler" }
|
||||
brk_cli = { version = "0.0.73", path = "crates/brk_cli" }
|
||||
brk_computer = { version = "0.0.73", path = "crates/brk_computer" }
|
||||
brk_core = { version = "0.0.73", path = "crates/brk_core" }
|
||||
brk_exit = { version = "0.0.73", path = "crates/brk_exit" }
|
||||
brk_fetcher = { version = "0.0.73", path = "crates/brk_fetcher" }
|
||||
brk_indexer = { version = "0.0.73", path = "crates/brk_indexer" }
|
||||
brk_interface = { version = "0.0.73", path = "crates/brk_interface" }
|
||||
brk_logger = { version = "0.0.73", path = "crates/brk_logger" }
|
||||
brk_mcp = { version = "0.0.73", path = "crates/brk_mcp" }
|
||||
brk_parser = { version = "0.0.73", path = "crates/brk_parser" }
|
||||
brk_rmcp = { version = "0.2.1", features = ["transport-streamable-http-server", "transport-worker"]}
|
||||
# brk_rmcp = { path = "../rust-sdk/crates/rmcp", features = ["transport-streamable-http-server", "transport-worker"]}
|
||||
brk_server = { version = "0.0.73", path = "crates/brk_server" }
|
||||
brk_store = { version = "0.0.73", path = "crates/brk_store" }
|
||||
brk_vec = { version = "0.0.73", path = "crates/brk_vec" }
|
||||
byteview = "=0.6.1"
|
||||
clap = { version = "4.5.40", features = ["string"] }
|
||||
clap_derive = "4.5.40"
|
||||
color-eyre = "0.6.5"
|
||||
derive_deref = "1.1.1"
|
||||
fjall = "2.8.0"
|
||||
jiff = "0.2.8"
|
||||
fjall = "2.11.1"
|
||||
jiff = "0.2.15"
|
||||
log = { version = "0.4.27" }
|
||||
minreq = { version = "2.13.4", features = ["https", "serde_json"] }
|
||||
minreq = { version = "2.14.0", features = ["https", "serde_json"] }
|
||||
rayon = "1.10.0"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
schemars = "1.0.4"
|
||||
serde = { version = "1.0.219" }
|
||||
serde_bytes = "0.11.17"
|
||||
serde_derive = "1.0.219"
|
||||
serde_json = { version = "1.0.140", features = ["float_roundtrip"] }
|
||||
tabled = "0.18.0"
|
||||
zerocopy = { version = "0.8.24", features = ["derive"] }
|
||||
tabled = "0.20.0"
|
||||
tokio = { version = "1.46.1", features = ["rt-multi-thread"] }
|
||||
zerocopy = { version = "0.8.26" }
|
||||
zerocopy-derive = "0.8.26"
|
||||
|
||||
[workspace.metadata.release]
|
||||
shared-version = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 bitcoinresearchkit, kibo.money
|
||||
Copyright (c) 2025 bitcoinresearchkit, kibo.money, satonomics
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
<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://kibo.money">
|
||||
<img alt="kibo.money" src="https://img.shields.io/badge/showcase-kib%C5%8D.money-orange">
|
||||
</a>
|
||||
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
|
||||
<img src="https://img.shields.io/crates/l/brk" alt="License" />
|
||||
</a>
|
||||
@@ -29,33 +26,31 @@
|
||||
<a href="https://bsky.app/profile/bitcoinresearchkit.org">
|
||||
<img src="https://img.shields.io/badge/bluesky-blue?link=https%3A%2F%2Fbsky.app%2Fprofile%2Fbitcoinresearchkit.org" alt="Bluesky" />
|
||||
</a>
|
||||
<a href="https://x.com/0xbrk">
|
||||
<a href="https://x.com/brkdotorg">
|
||||
<img src="https://img.shields.io/badge/x.com-black" alt="X" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
> **WARNING**
|
||||
>
|
||||
> This project is still a work in progress and while it's much better in many ways than its previous version ([kibo v0.5](https://github.com/kibo-money/kibo)), it doesn't yet include all of those datasets. If you're interested in having everything right now, please use the latter until feature parity is achieved.
|
||||
>
|
||||
> The explorer part (mempool.space/electrs) is also not viable just yet.
|
||||
>
|
||||
> Stay tuned and please be patient, it's a lot of work !
|
||||
The Bitcoin Research Kit is a high-performance toolchain designed to parse, index, compute, serve and visualize data from a Bitcoin node, enabling users to gain deeper insights into the Bitcoin network.
|
||||
|
||||
The Bitcoin Research Kit is a high-performance toolchain designed to parse, index, compute, serve and visualize data from a Bitcoin Core node, enabling users to gain deeper insights into the Bitcoin network.
|
||||
|
||||
In other words it's an alternative to [Glassnode](https://glassnode.com), [mempool.space](https://mempool.space/) and [electrs](https://github.com/romanz/electrs) all in one package with a particular focus on simplicity and the self-hosting experience.
|
||||
In other words it's an alternative to [Glassnode](https://glassnode.com), [mempool.space](https://mempool.space/) (soon) and [electrs](https://github.com/romanz/electrs) (soon) all in one package with a particular focus on simplicity and ease of use.
|
||||
|
||||
The toolkit can be used in various ways to accommodate as many needs as possible:
|
||||
|
||||
- **[Website](https://kibo.money)** \
|
||||
Everyone is welcome to visit [kibo.money](https://kibo.money) which is the official showcase of the suite's capabilities and served by default when running BRK. \
|
||||
Researchers and developers are free to use the API which endpoints documentation can be found [here](https://github.com/bitcoinresearchkit/brk/tree/main/crates/brk_server#endpoints). \
|
||||
As a token of gratitude to the community and to stimulate curiosity, both the website and the API are entirely free, allowing anyone to use them.
|
||||
- **[Website](https://bitcoinresearchkit.org)** \
|
||||
Everyone is welcome to visit the official instance and showcase of the suite's capabilities. \
|
||||
It has a wide range of functionalities including charts, tables and simulations which you can visit for free and without the need for an account. \
|
||||
Also available at: [kibo.money](https://kibo.money) // [satonomics.xyz](https://satonomics.xyz)
|
||||
- **[API](https://github.com/bitcoinresearchkit/brk/tree/main/crates/brk_server#brk-server)** \
|
||||
Researchers and developers are free to use BRK's public API with  dataset variants at their disposal. \
|
||||
Just like the website, it's entirely free, with no authentication or rate-limiting.
|
||||
- **[AI](https://github.com/bitcoinresearchkit/brk/blob/main/crates/brk_mcp/README.md#brk-mcp)** \
|
||||
LLMs have to possibility to connect to BRK's backend through a [MCP](https://modelcontextprotocol.io/introduction). \
|
||||
It will give them access to the same tools as the API, with no restrictions, and allow you to have your very own data analysts. \
|
||||
One-shot output examples: [Document](https://claude.ai/public/artifacts/71194d29-f965-417c-ba09-fdf0e4ecb1d5) // [Dashboard](https://claude.ai/public/artifacts/beef143f-399a-4ed4-b8bf-c986b776de42) // [Dashboard 2](https://claude.ai/public/artifacts/5430ae49-bb3d-4fc1-ab24-f1e33deb40dc)
|
||||
- **[CLI](https://crates.io/crates/brk_cli)** \
|
||||
Node runners are strongly encouraged to try out and self-host their own instance. \
|
||||
A lot of effort has gone into making this as easy as possible. \
|
||||
For more information visit: [`brk_cli`](https://crates.io/crates/brk_cli)
|
||||
Node runners are strongly encouraged to try out and self-host their own instance using BRK's command line interface. \
|
||||
The CLI has multiple cogs available for users to tweak to adapt to all situations with even the possibility for web developers to create their own custom website which could later on be added as an alternative front-end.
|
||||
- **[Crates](https://crates.io/crates/brk)** \
|
||||
Rust developers have access to a wide range crates, each built upon one another with its own specific purpose, enabling independent use and offering great flexibility.
|
||||
PRs are welcome, especially if their goal is to introduce additional datasets.
|
||||
@@ -67,37 +62,41 @@ In contrast, existing alternatives tend to be either [very costly](https://studi
|
||||
## Crates
|
||||
|
||||
- [`brk`](https://crates.io/crates/brk): Wrapper around all other `brk-*` crates
|
||||
- [`brk_cli`](https://crates.io/crates/brk_cli): A standalone command line interface to interact with the Bitcoin Research Kit
|
||||
- [`brk_cli`](https://crates.io/crates/brk_cli): A command line interface to run a Bitcoin Research Kit instance
|
||||
- [`brk_computer`](https://crates.io/crates/brk_computer): A Bitcoin dataset computer, built on top of brk_indexer
|
||||
- [`brk_core`](https://crates.io/crates/brk_core): The Core (Structs and Errors) of the Bitcoin Research Kit
|
||||
- [`brk_exit`](https://crates.io/crates/brk_exit): An exit blocker built on top of ctrlc
|
||||
- [`brk_fetcher`](https://crates.io/crates/brk_fetcher): A Bitcoin price fetcher
|
||||
- [`brk_indexer`](https://crates.io/crates/brk_indexer): A Bitcoin Core indexer built on top of brk_parser
|
||||
- [`brk_logger`](https://crates.io/crates/brk_logger): A clean logger used in the Bitcoin Research Kit.
|
||||
- [`brk_logger`](https://crates.io/crates/brk_logger): A clean logger used in the Bitcoin Research Kit
|
||||
- [`brk_mcp`](https://crates.io/crates/brk_mcp): A Model Context Protocol (MCP) which gives LLMs access to all available tools in BRK
|
||||
- [`brk_parser`](https://crates.io/crates/brk_parser): A very fast Bitcoin Core block parser and iterator built on top of bitcoin-rust
|
||||
- [`brk_query`](https://crates.io/crates/brk_query): A library that finds requested datasets.
|
||||
- [`brk_interface`](https://crates.io/crates/brk_interface): An interface to BRK's engine
|
||||
- [`brk_server`](https://crates.io/crates/brk_server): A server that serves Bitcoin data and swappable front-ends, built on top of `brk_indexer`, `brk_fetcher` and `brk_computer`
|
||||
- [`brk_store`](https://crates.io/crates/brk_store): A thin wrapper around [`fjall`](https://crates.io/crates/fjall)
|
||||
- [`brk_vec`](https://crates.io/crates/brk_vec): A push-only, truncable, compressable, saveable Vec
|
||||
- [`brk_bundler`](https://crates.io/crates/brk_bundler): A thin wrapper around [`rolldown`](https://rolldown.rs/)
|
||||
|
||||
## 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 [kibo.money](https://kibo.money) public instance.
|
||||
|
||||
## Hosting as a service
|
||||
|
||||
*Soon™*
|
||||
|
||||
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
|
||||
- Direct communication for feature requests and support
|
||||
- Updates delivered at your convenience
|
||||
- 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`
|
||||
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
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
# TODO
|
||||
|
||||
- __crates__
|
||||
- _cli_
|
||||
- check disk space on first launch
|
||||
- add custom path support for config.toml
|
||||
- maybe add bitcoind download and launch support
|
||||
- via: https://github.com/rust-bitcoin/corepc/blob/master/node
|
||||
- test read/write speed, add warning if too low (<2gb/s)
|
||||
- pull latest version and notify is out of date
|
||||
- _computer_
|
||||
- **add rollback of states (in stateful)**
|
||||
- add support for per index computation
|
||||
- fix feerate which is always ZERO due to coinbase transaction
|
||||
- before computing multiple sources check their length, panic if not equal
|
||||
- add oracle price dataset (https://utxo.live/oracle/UTXOracle.py)
|
||||
- add address counts relative to all datasets
|
||||
- make decade, quarter, year datasets `computed` instead of `eager`
|
||||
- add 6 months (semester) interval datasets to builder
|
||||
- add revived/sent supply datasets
|
||||
- add `in-sats` version of all price datasets (average and co)
|
||||
- add `p2pk` group (sum of `p2pk33` and `p2pk65`)
|
||||
- add more date ranges (3-6 months and more)
|
||||
- add puell multiple dataset
|
||||
- add pi cycle dataset
|
||||
- add ema of price
|
||||
- add 7d and 30d ema to sell side risk ratio and sopr
|
||||
- add all possible charts from:
|
||||
- https://mainnet.observer
|
||||
- https://glassnode.com
|
||||
- https://checkonchain.com
|
||||
- https://researchbitcoin.net/exciting-update-coming-to-the-bitcoin-lab/
|
||||
- https://mempool.space/research
|
||||
- _indexer_
|
||||
- parse only the needed block number
|
||||
- maybe using https://developer.bitcoin.org/reference/rpc/getblockhash.html
|
||||
- _interface_
|
||||
- create pagination enum
|
||||
- from to
|
||||
- from option<count>
|
||||
- to option<count>
|
||||
- page + option<per page> default 1000 max 1000
|
||||
- from/to/count params don’t cap all combinations
|
||||
- example: from -10,000 count 10, won’t work if underlying vec isn’t 10k or more long
|
||||
- _parser_
|
||||
- save `vec` file instead of `json`
|
||||
- support lock file, process in read only if already opened in write mode
|
||||
- if less than X (10 maybe ?) get block using rpc instead of parsing the block files
|
||||
- _server_
|
||||
- api
|
||||
- add extensions support (.json .csv …)
|
||||
- if format instead of extension then don't download file
|
||||
- _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
|
||||
- add contribution section where help is needed
|
||||
- documentation/mcp/datasets/different front ends
|
||||
- add faq
|
||||
- __websites__
|
||||
- _default_
|
||||
- explorer
|
||||
- blocks
|
||||
- transactions
|
||||
- addresses
|
||||
- miners
|
||||
- maybe xpubs
|
||||
- charts
|
||||
- improve some names and colors
|
||||
- remove `sum` series when it's a duplicate of the `base` (in subsidy for example)
|
||||
- selected unit sometimes changes when going back end forth
|
||||
- add support for custom charts
|
||||
- price scale format depends on unit, hide digits for sats for example (if/when possible)
|
||||
- table
|
||||
- pagination
|
||||
- exports (.json, .csv,…)
|
||||
- search
|
||||
- datasets add legend, and keywords ?
|
||||
- height/address/txid
|
||||
- api
|
||||
- add api page with interactivity
|
||||
- global
|
||||
- **fix navigation/history**
|
||||
- move share button to footer ?
|
||||
- Use `ichart.createPane()` in wrapper
|
||||
- improve behavior when local storage is unavailable
|
||||
- by having a global state
|
||||
- __global__
|
||||
- check `TODO`s in codebase
|
||||
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 263 KiB |
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 386 KiB |
|
After Width: | Height: | Size: 496 KiB |
|
After Width: | Height: | Size: 564 KiB |
|
After Width: | Height: | Size: 592 KiB |
|
After Width: | Height: | Size: 453 KiB |
|
After Width: | Height: | Size: 526 KiB |
@@ -0,0 +1,2 @@
|
||||
profile.json.gz
|
||||
flamegraph.svg
|
||||
@@ -3,35 +3,43 @@ name = "brk"
|
||||
description.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[features]
|
||||
full = [
|
||||
"bundler",
|
||||
"core",
|
||||
"computer",
|
||||
"exit",
|
||||
"fetcher",
|
||||
"indexer",
|
||||
"logger",
|
||||
"mcp",
|
||||
"parser",
|
||||
"query",
|
||||
"interface",
|
||||
"server",
|
||||
"store",
|
||||
"vec",
|
||||
]
|
||||
bundler = ["brk_bundler"]
|
||||
core = ["brk_core"]
|
||||
computer = ["brk_computer"]
|
||||
exit = ["brk_exit"]
|
||||
fetcher = ["brk_fetcher"]
|
||||
indexer = ["brk_indexer"]
|
||||
logger = ["brk_logger"]
|
||||
mcp = ["brk_mcp"]
|
||||
parser = ["brk_parser"]
|
||||
query = ["brk_query"]
|
||||
interface = ["brk_interface"]
|
||||
server = ["brk_server"]
|
||||
store = ["brk_store"]
|
||||
vec = ["brk_vec"]
|
||||
|
||||
[dependencies]
|
||||
brk_bundler = { workspace = true, optional = true }
|
||||
brk_cli = { workspace = true }
|
||||
brk_core = { workspace = true, optional = true }
|
||||
brk_computer = { workspace = true, optional = true }
|
||||
@@ -39,9 +47,11 @@ brk_exit = { workspace = true, optional = true }
|
||||
brk_fetcher = { workspace = true, optional = true }
|
||||
brk_indexer = { workspace = true, optional = true }
|
||||
brk_logger = { workspace = true, optional = true }
|
||||
brk_mcp = { workspace = true, optional = true }
|
||||
brk_parser = { workspace = true, optional = true }
|
||||
brk_query = { workspace = true, optional = true }
|
||||
brk_interface = { workspace = true, optional = true }
|
||||
brk_server = { workspace = true, optional = true }
|
||||
brk_store = { workspace = true, optional = true }
|
||||
brk_vec = { workspace = true, optional = true }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
fn main() {}
|
||||
@@ -0,0 +1,2 @@
|
||||
cargo build --profile profiling
|
||||
flamegraph -- ../../target/profiling/brk
|
||||
@@ -0,0 +1,2 @@
|
||||
cargo build --profile profiling
|
||||
samply record ../../target/profiling/brk
|
||||
@@ -1,5 +1,12 @@
|
||||
#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
|
||||
|
||||
#[cfg(feature = "bundler")]
|
||||
#[doc(inline)]
|
||||
pub use brk_bundler as bundler;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use brk_cli as cli;
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
#[doc(inline)]
|
||||
pub use brk_core as core;
|
||||
@@ -24,18 +31,26 @@ pub use brk_indexer as indexer;
|
||||
#[doc(inline)]
|
||||
pub use brk_logger as logger;
|
||||
|
||||
#[cfg(feature = "mcp")]
|
||||
#[doc(inline)]
|
||||
pub use brk_mcp as mcp;
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
#[doc(inline)]
|
||||
pub use brk_parser as parser;
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
#[cfg(feature = "interface")]
|
||||
#[doc(inline)]
|
||||
pub use brk_query as query;
|
||||
pub use brk_interface as interface;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
#[doc(inline)]
|
||||
pub use brk_server as server;
|
||||
|
||||
#[cfg(feature = "store")]
|
||||
#[doc(inline)]
|
||||
pub use brk_store as store;
|
||||
|
||||
#[cfg(feature = "vec")]
|
||||
#[doc(inline)]
|
||||
pub use brk_vec as vec;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "brk_bundler"
|
||||
description = "A thin wrapper around rolldown"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
log = { workspace = true }
|
||||
notify = "8.1.0"
|
||||
brk_rolldown = "0.1.0"
|
||||
# brk_rolldown = { path = "../../../rolldown/crates/rolldown"}
|
||||
sugar_path = "1.2.0"
|
||||
tokio = { workspace = true }
|
||||
@@ -0,0 +1,145 @@
|
||||
use std::{fs, io, path::Path, sync::Arc};
|
||||
|
||||
use brk_rolldown::{Bundler, BundlerOptions, RawMinifyOptions, SourceMapType};
|
||||
use log::error;
|
||||
use notify::{EventKind, RecursiveMode, Watcher};
|
||||
use sugar_path::SugarPath;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub async fn bundle(websites_path: &Path, source_folder: &str, watch: bool) -> io::Result<()> {
|
||||
let source_path = websites_path.join(source_folder);
|
||||
let dist_path = websites_path.join("dist");
|
||||
|
||||
let _ = fs::remove_dir_all(&dist_path);
|
||||
copy_dir_all(&source_path, &dist_path)?;
|
||||
|
||||
let source_scripts = format!("./{source_folder}/scripts");
|
||||
let source_entry = format!("{source_scripts}/entry.js");
|
||||
|
||||
let absolute_websites_path = websites_path.absolutize();
|
||||
|
||||
let mut bundler = Bundler::new(BundlerOptions {
|
||||
input: Some(vec![source_entry.into()]),
|
||||
dir: Some("./dist/scripts".to_string()),
|
||||
cwd: Some(absolute_websites_path),
|
||||
minify: Some(RawMinifyOptions::Bool(true)),
|
||||
sourcemap: Some(SourceMapType::File),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
bundler.write().await.unwrap();
|
||||
|
||||
let absolute_source_index_path = source_path.join("index.html").absolutize();
|
||||
let absolute_source_index_path_clone = absolute_source_index_path.clone();
|
||||
let absolute_source_path = source_path.absolutize();
|
||||
let absolute_source_path_clone = absolute_source_path.clone();
|
||||
let absolute_source_scripts_path = websites_path.join(source_scripts).absolutize();
|
||||
let absolute_source_sw_path = source_path.join("service-worker.js").absolutize();
|
||||
let absolute_source_sw_path_clone = absolute_source_sw_path.clone();
|
||||
|
||||
let absolute_dist_entry_path = dist_path.join("scripts/entry.js").absolutize();
|
||||
let absolute_dist_index_path = dist_path.join("index.html").absolutize();
|
||||
let absolute_dist_path = dist_path.absolutize();
|
||||
let absolute_dist_path_clone = absolute_dist_path.clone();
|
||||
let absolute_dist_sw_path = dist_path.join("service-worker.js").absolutize();
|
||||
|
||||
let write_index = move || {
|
||||
let mut contents = fs::read_to_string(&absolute_source_index_path).unwrap();
|
||||
|
||||
if let Ok(entry) = fs::read_to_string(absolute_dist_path_clone.join("scripts/entry.js")) {
|
||||
if let Some(start) = entry.find("main") {
|
||||
if let Some(end) = entry.find(".js") {
|
||||
let main_hashed = &entry[start..end];
|
||||
contents =
|
||||
contents.replace("/scripts/main.js", &format!("/scripts/{main_hashed}.js"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = fs::write(&absolute_dist_index_path, contents);
|
||||
};
|
||||
|
||||
let write_sw = move || {
|
||||
let contents = fs::read_to_string(&absolute_source_sw_path)
|
||||
.unwrap()
|
||||
.replace("__VERSION__", &format!("v{VERSION}"));
|
||||
let _ = fs::write(&absolute_dist_sw_path, contents);
|
||||
};
|
||||
|
||||
write_index();
|
||||
write_sw();
|
||||
|
||||
if !watch {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
let write_index_clone = write_index.clone();
|
||||
|
||||
let mut entry_watcher = notify::recommended_watcher(
|
||||
move |res: Result<notify::Event, notify::Error>| match res {
|
||||
Ok(_) => write_index_clone(),
|
||||
Err(e) => error!("watch error: {e:?}"),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
entry_watcher
|
||||
.watch(&absolute_dist_entry_path, RecursiveMode::Recursive)
|
||||
.unwrap();
|
||||
|
||||
let mut source_watcher = notify::recommended_watcher(
|
||||
move |res: Result<notify::Event, notify::Error>| match res {
|
||||
Ok(event) => match event.kind {
|
||||
EventKind::Create(_) => event.paths,
|
||||
EventKind::Modify(_) => event.paths,
|
||||
_ => vec![],
|
||||
}
|
||||
.into_iter()
|
||||
.filter(|path| path.starts_with(&absolute_source_path))
|
||||
.filter(|path| !path.starts_with(&absolute_source_scripts_path))
|
||||
.for_each(|source_path| {
|
||||
let suffix = source_path.strip_prefix(&absolute_source_path).unwrap();
|
||||
let dist_path = absolute_dist_path.join(suffix);
|
||||
|
||||
if source_path == absolute_source_index_path_clone {
|
||||
write_index();
|
||||
} else if source_path == absolute_source_sw_path_clone {
|
||||
write_sw();
|
||||
} else {
|
||||
let _ = fs::copy(&source_path, &dist_path);
|
||||
}
|
||||
}),
|
||||
Err(e) => error!("watch error: {e:?}"),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
source_watcher
|
||||
.watch(&absolute_source_path_clone, RecursiveMode::Recursive)
|
||||
.unwrap();
|
||||
|
||||
let watcher =
|
||||
brk_rolldown::Watcher::new(vec![Arc::new(Mutex::new(bundler))], None).unwrap();
|
||||
|
||||
watcher.start().await;
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
fs::create_dir_all(&dst)?;
|
||||
for entry in fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let ty = entry.file_type()?;
|
||||
if ty.is_dir() {
|
||||
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
} else {
|
||||
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
[package]
|
||||
name = "brk_cli"
|
||||
description = "A command line interface to interact with the full Bitcoin Research Kit"
|
||||
description = "A command line interface to run a Bitcoin Research Kit instance"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bitcoincore-rpc = { workspace = true }
|
||||
brk_computer = { workspace = true }
|
||||
brk_core = { workspace = true }
|
||||
brk_exit = { workspace = true }
|
||||
@@ -14,14 +16,15 @@ brk_fetcher = { workspace = true }
|
||||
brk_indexer = { workspace = true }
|
||||
brk_logger = { workspace = true }
|
||||
brk_parser = { workspace = true }
|
||||
brk_query = { workspace = true }
|
||||
brk_server = { workspace = true }
|
||||
clap = { workspace = true, features = ["string"] }
|
||||
brk_vec = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
clap_derive = { workspace = true }
|
||||
color-eyre = { workspace = true }
|
||||
log = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tabled = { workspace = true }
|
||||
toml = "0.8.20"
|
||||
tokio = { workspace = true }
|
||||
toml = "0.9.0"
|
||||
|
||||
[[bin]]
|
||||
name = "brk"
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
# 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://kibo.money">
|
||||
<img alt="kibo.money" src="https://img.shields.io/badge/showcase-kib%C5%8D.money-orange">
|
||||
</a>
|
||||
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
|
||||
<img src="https://img.shields.io/crates/l/brk" alt="License" />
|
||||
</a>
|
||||
@@ -29,14 +26,16 @@
|
||||
<a href="https://bsky.app/profile/bitcoinresearchkit.org">
|
||||
<img src="https://img.shields.io/badge/bluesky-blue?link=https%3A%2F%2Fbsky.app%2Fprofile%2Fbitcoinresearchkit.org" alt="Bluesky" />
|
||||
</a>
|
||||
<a href="https://x.com/0xbrk">
|
||||
<a href="https://x.com/brkdotorg">
|
||||
<img src="https://img.shields.io/badge/x.com-black" alt="X" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
A command line interface to interact with the full Bitcoin Research Kit. It's built on top of every other create and gives the possility to use BRK using the terminal instead of Rust.
|
||||
A command line interface to run a Bitcoin Research Kit instance.
|
||||
|
||||
It has 2 commandes for now (other than `help` and `version`) which are `run` and `query`. The former is used to run the processing (indexer + computer) and/or the server. The latter uses `brk_query` as its backend just like to server to be able to get datasets via the terminal instead of the API. Both commands are very costumizable by having all the parameters of their Rust counterparts ([`run`](https://github.com/bitcoinresearchkit/brk/blob/main/crates/brk_cli/src/run.rs#L91-L147), [`query`](https://github.com/bitcoinresearchkit/brk/blob/main/crates/brk_query/src/params.rs)).
|
||||
It's very customizable with all parameters from the underlying tools (crates) used inside.
|
||||
|
||||
Run `brk -h` for more information.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -57,13 +56,15 @@ To be determined
|
||||
- [Bitcoin](https://bitcoin.org/en/full-node)
|
||||
- [Rust](https://www.rust-lang.org/tools/install)
|
||||
- Unix based operating system (Mac OS or Linux)
|
||||
- Ubuntu users need to install `open-ssl` via `sudo apt install libssl-dev pkg-config`
|
||||
|
||||
> [!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 on the releases page ([link](https://github.com/bitcoinresearchkit/brk/releases/latest)).
|
||||
You can find a pre-built binary for your operating system in the [releases page](https://github.com/bitcoinresearchkit/brk/releases/latest).
|
||||
|
||||
### Cargo
|
||||
|
||||
@@ -85,10 +86,11 @@ cargo run -r
|
||||
|
||||
## Usage
|
||||
|
||||
Run `brk -h` to view each available command and their respective description.
|
||||
Run `brk -h` to view each available parameter and their respective description.
|
||||
|
||||
`-h` works also for commands, which mean that `brk run -h` will explain all the parameters of `brk run` for example.
|
||||
> [!TIP]
|
||||
> Every parameter set will be saved at `~/.brk/config.toml`, which allows you to simply run `brk` next time.
|
||||
|
||||
Every parameter set for `brk run` will be saved at `~/.brk/config.toml`, which will allow you to simply run `brk run` next time.
|
||||
## Tunnel
|
||||
|
||||
Then the easiest to let others access your server is to use `cloudflared` which will also cache requests. For more information go to: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use bitcoincore_rpc::{self, Auth, Client};
|
||||
use brk_core::{default_bitcoin_path, default_brk_path, default_on_error, dot_brk_path};
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_server::Website;
|
||||
use brk_vec::{Computation, Format};
|
||||
use clap::Parser;
|
||||
use clap_derive::Parser;
|
||||
use color_eyre::eyre::eyre;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::services::Services;
|
||||
|
||||
#[derive(Parser, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||
#[command(version, about)]
|
||||
pub struct Config {
|
||||
/// Bitcoin main directory path, defaults: ~/.bitcoin, ~/Library/Application\ Support/Bitcoin, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "PATH")]
|
||||
bitcoindir: Option<String>,
|
||||
|
||||
/// Bitcoin blocks directory path, default: --bitcoindir/blocks, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "PATH")]
|
||||
blocksdir: Option<String>,
|
||||
|
||||
/// Bitcoin Research Kit outputs directory path, default: ~/.brk, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "PATH")]
|
||||
brkdir: Option<String>,
|
||||
|
||||
/// Activated services, default: all, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short, long)]
|
||||
services: Option<Services>,
|
||||
|
||||
/// Computation of computed datasets, `lazy` computes data whenever requested without saving it, `eager` computes the data once and saves it to disk, default: `lazy`, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short, long)]
|
||||
computation: Option<Computation>,
|
||||
|
||||
/// Format of computed datasets, `compressed` to save disk space (experimental), `raw` to prioritize speed, default: `raw`, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short, long)]
|
||||
format: Option<Format>,
|
||||
|
||||
/// Activate fetching prices from exchanges APIs and the computation of all 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
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short, long)]
|
||||
website: Option<Website>,
|
||||
|
||||
/// Bitcoin RPC ip, default: localhost, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "IP")]
|
||||
rpcconnect: Option<String>,
|
||||
|
||||
/// Bitcoin RPC port, default: 8332, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "PORT")]
|
||||
rpcport: Option<u16>,
|
||||
|
||||
/// Bitcoin RPC cookie file, default: --bitcoindir/.cookie, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "PATH")]
|
||||
rpccookiefile: Option<String>,
|
||||
|
||||
/// Bitcoin RPC username, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(long, value_name = "USERNAME")]
|
||||
rpcuser: Option<String>,
|
||||
|
||||
/// Bitcoin RPC password, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[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")]
|
||||
check_collisions: Option<bool>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn import() -> color_eyre::Result<Self> {
|
||||
let config_args = Some(Config::parse());
|
||||
|
||||
let path = dot_brk_path();
|
||||
|
||||
let _ = fs::create_dir_all(&path);
|
||||
|
||||
let path = path.join("config.toml");
|
||||
|
||||
let mut config_saved = Self::read(&path);
|
||||
|
||||
if let Some(mut config_args) = config_args {
|
||||
if let Some(bitcoindir) = config_args.bitcoindir.take() {
|
||||
config_saved.bitcoindir = Some(bitcoindir);
|
||||
}
|
||||
|
||||
if let Some(blocksdir) = config_args.blocksdir.take() {
|
||||
config_saved.blocksdir = Some(blocksdir);
|
||||
}
|
||||
|
||||
if let Some(brkdir) = config_args.brkdir.take() {
|
||||
config_saved.brkdir = Some(brkdir);
|
||||
}
|
||||
|
||||
if let Some(services) = config_args.services.take() {
|
||||
config_saved.services = Some(services);
|
||||
}
|
||||
|
||||
if let Some(computation) = config_args.computation.take() {
|
||||
config_saved.computation = Some(computation);
|
||||
}
|
||||
|
||||
if let Some(fetch) = config_args.fetch.take() {
|
||||
config_saved.fetch = Some(fetch);
|
||||
}
|
||||
|
||||
if let Some(format) = config_args.format.take() {
|
||||
config_saved.format = Some(format);
|
||||
}
|
||||
|
||||
if let Some(website) = config_args.website.take() {
|
||||
config_saved.website = Some(website);
|
||||
}
|
||||
|
||||
if let Some(rpcconnect) = config_args.rpcconnect.take() {
|
||||
config_saved.rpcconnect = Some(rpcconnect);
|
||||
}
|
||||
|
||||
if let Some(rpcport) = config_args.rpcport.take() {
|
||||
config_saved.rpcport = Some(rpcport);
|
||||
}
|
||||
|
||||
if let Some(rpccookiefile) = config_args.rpccookiefile.take() {
|
||||
config_saved.rpccookiefile = Some(rpccookiefile);
|
||||
}
|
||||
|
||||
if let Some(rpcuser) = config_args.rpcuser.take() {
|
||||
config_saved.rpcuser = Some(rpcuser);
|
||||
}
|
||||
|
||||
if let Some(rpcpassword) = config_args.rpcpassword.take() {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
let config = config_saved;
|
||||
|
||||
config.check();
|
||||
|
||||
config.write(&path)?;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn check(&self) {
|
||||
if !self.bitcoindir().is_dir() {
|
||||
println!("{:?} isn't a valid directory", self.bitcoindir());
|
||||
println!("Please use the --bitcoindir parameter to set a valid path.");
|
||||
println!("Run the program with '-h' for help.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if !self.blocksdir().is_dir() {
|
||||
println!("{:?} isn't a valid directory", self.blocksdir());
|
||||
println!("Please use the --blocksdir parameter to set a valid path.");
|
||||
println!("Run the program with '-h' for help.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if !self.brkdir().is_dir() {
|
||||
println!("{:?} isn't a valid directory", self.brkdir());
|
||||
println!("Please use the --brkdir parameter to set a valid path.");
|
||||
println!("Run the program with '-h' for help.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
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."
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn read(path: &Path) -> Self {
|
||||
fs::read_to_string(path).map_or_else(
|
||||
|_| Config::default(),
|
||||
|contents| toml::from_str(&contents).unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn write(&self, path: &Path) -> std::io::Result<()> {
|
||||
fs::write(path, toml::to_string(self).unwrap())
|
||||
}
|
||||
|
||||
pub fn rpc(&self) -> color_eyre::Result<&'static Client> {
|
||||
Ok(Box::leak(Box::new(Client::new(
|
||||
&format!(
|
||||
"http://{}:{}",
|
||||
self.rpcconnect().unwrap_or(&"localhost".to_string()),
|
||||
self.rpcport().unwrap_or(8332)
|
||||
),
|
||||
self.rpc_auth().unwrap(),
|
||||
)?)))
|
||||
}
|
||||
|
||||
fn rpc_auth(&self) -> color_eyre::Result<Auth> {
|
||||
let cookie = self.path_cookiefile();
|
||||
|
||||
if cookie.is_file() {
|
||||
Ok(Auth::CookieFile(cookie))
|
||||
} else if self.rpcuser.is_some() && self.rpcpassword.is_some() {
|
||||
Ok(Auth::UserPass(
|
||||
self.rpcuser.clone().unwrap(),
|
||||
self.rpcpassword.clone().unwrap(),
|
||||
))
|
||||
} else {
|
||||
Err(eyre!("Failed to find correct auth"))
|
||||
}
|
||||
}
|
||||
|
||||
fn rpcconnect(&self) -> Option<&String> {
|
||||
self.rpcconnect.as_ref()
|
||||
}
|
||||
|
||||
fn rpcport(&self) -> Option<u16> {
|
||||
self.rpcport
|
||||
}
|
||||
|
||||
pub fn delay(&self) -> Option<u64> {
|
||||
self.delay
|
||||
}
|
||||
|
||||
pub fn bitcoindir(&self) -> PathBuf {
|
||||
self.bitcoindir
|
||||
.as_ref()
|
||||
.map_or_else(default_bitcoin_path, |s| Self::fix_user_path(s.as_ref()))
|
||||
}
|
||||
|
||||
pub fn blocksdir(&self) -> PathBuf {
|
||||
self.blocksdir.as_ref().map_or_else(
|
||||
|| self.bitcoindir().join("blocks"),
|
||||
|blocksdir| Self::fix_user_path(blocksdir.as_str()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn brkdir(&self) -> PathBuf {
|
||||
self.brkdir
|
||||
.as_ref()
|
||||
.map_or_else(default_brk_path, |s| Self::fix_user_path(s.as_ref()))
|
||||
}
|
||||
|
||||
pub fn outputsdir(&self) -> PathBuf {
|
||||
self.brkdir().join("outputs")
|
||||
}
|
||||
|
||||
pub fn harsdir(&self) -> PathBuf {
|
||||
self.outputsdir().join("hars")
|
||||
}
|
||||
|
||||
pub fn process(&self) -> bool {
|
||||
self.services
|
||||
.is_none_or(|m| m == Services::All || m == Services::Processor)
|
||||
}
|
||||
|
||||
pub fn serve(&self) -> bool {
|
||||
self.services
|
||||
.is_none_or(|m| m == Services::All || m == Services::Server)
|
||||
}
|
||||
|
||||
fn path_cookiefile(&self) -> PathBuf {
|
||||
self.rpccookiefile.as_ref().map_or_else(
|
||||
|| self.bitcoindir().join(".cookie"),
|
||||
|p| Self::fix_user_path(p.as_str()),
|
||||
)
|
||||
}
|
||||
|
||||
fn fix_user_path(path: &str) -> PathBuf {
|
||||
let fix = move |pattern: &str| {
|
||||
if path.starts_with(pattern) {
|
||||
let path = &path
|
||||
.replace(&format!("{pattern}/"), "")
|
||||
.replace(pattern, "");
|
||||
|
||||
let home = std::env::var("HOME").unwrap();
|
||||
|
||||
Some(Path::new(&home).join(path))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
fix("~").unwrap_or_else(|| fix("$HOME").unwrap_or_else(|| PathBuf::from(&path)))
|
||||
}
|
||||
|
||||
pub fn website(&self) -> Website {
|
||||
self.website.unwrap_or(Website::Default)
|
||||
}
|
||||
|
||||
pub fn fetch(&self) -> bool {
|
||||
self.fetch.is_none_or(|b| b)
|
||||
}
|
||||
|
||||
pub fn fetcher(&self) -> Option<Fetcher> {
|
||||
self.fetch()
|
||||
.then(|| Fetcher::import(Some(self.harsdir().as_path())).unwrap())
|
||||
}
|
||||
|
||||
pub fn computation(&self) -> Computation {
|
||||
self.computation.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn format(&self) -> Format {
|
||||
self.format.unwrap_or_default()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,12 @@
|
||||
use std::fs;
|
||||
use std::{fs, thread};
|
||||
|
||||
use brk_core::{dot_brk_log_path, dot_brk_path};
|
||||
use brk_query::Params as QueryArgs;
|
||||
use clap::{Parser, Subcommand};
|
||||
use query::query;
|
||||
use run::{RunConfig, run};
|
||||
|
||||
mod query;
|
||||
mod config;
|
||||
mod run;
|
||||
mod services;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version, about)]
|
||||
#[command(propagate_version = true)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
enum Commands {
|
||||
/// Run the indexer, computer and server
|
||||
Run(RunConfig),
|
||||
/// Query generated datasets via the `run` command in a similar fashion as the server's API
|
||||
Query(QueryArgs),
|
||||
}
|
||||
use run::*;
|
||||
|
||||
pub fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
@@ -32,10 +15,9 @@ pub fn main() -> color_eyre::Result<()> {
|
||||
|
||||
brk_logger::init(Some(&dot_brk_log_path()));
|
||||
|
||||
let cli = Cli::parse();
|
||||
|
||||
match cli.command {
|
||||
Commands::Run(args) => run(args),
|
||||
Commands::Query(args) => query(args),
|
||||
}
|
||||
thread::Builder::new()
|
||||
.stack_size(256 * 1024 * 1024)
|
||||
.spawn(run)?
|
||||
.join()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
use brk_computer::Computer;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_query::{Index, Output, Params as QueryParams, Query, Tabled, Value};
|
||||
use tabled::settings::Style;
|
||||
|
||||
use crate::run::RunConfig;
|
||||
|
||||
pub fn query(params: QueryParams) -> color_eyre::Result<()> {
|
||||
let config = RunConfig::import(None)?;
|
||||
|
||||
let compressed = config.compressed();
|
||||
|
||||
let mut indexer = Indexer::new(config.indexeddir(), compressed, config.check_collisions())?;
|
||||
indexer.import_vecs()?;
|
||||
|
||||
let mut computer = Computer::new(config.computeddir(), config.fetcher(), compressed);
|
||||
computer.import_vecs()?;
|
||||
|
||||
let query = Query::build(&indexer, &computer);
|
||||
|
||||
let index = Index::try_from(params.index.as_str())?;
|
||||
|
||||
let ids = params.values.iter().map(|s| s.as_str()).collect::<Vec<_>>();
|
||||
|
||||
let res = query.search_and_format(index, &ids, params.from, params.to, params.format)?;
|
||||
|
||||
if params.format.is_some() {
|
||||
println!("{}", res);
|
||||
} else {
|
||||
println!(
|
||||
"{}",
|
||||
match res {
|
||||
Output::Json(v) => match v {
|
||||
Value::Single(v) => v.to_string().replace("\"", ""),
|
||||
v => {
|
||||
let v = match v {
|
||||
Value::Single(_) => unreachable!("Already processed"),
|
||||
Value::List(v) => vec![v],
|
||||
Value::Matrix(v) => v,
|
||||
};
|
||||
let mut table =
|
||||
v.to_table(ids.iter().map(|id| id.to_string()).collect::<Vec<_>>());
|
||||
table.with(Style::psql());
|
||||
table.to_string()
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,24 +1,16 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
thread::sleep,
|
||||
time::Duration,
|
||||
};
|
||||
use std::{thread::sleep, time::Duration};
|
||||
|
||||
use bitcoincore_rpc::{self, RpcApi};
|
||||
use brk_computer::Computer;
|
||||
use brk_core::{default_bitcoin_path, default_brk_path, dot_brk_path};
|
||||
use brk_exit::Exit;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_parser::rpc::{self, Auth, Client, RpcApi};
|
||||
use brk_server::{Server, Website, tokio};
|
||||
use clap::{Parser, ValueEnum};
|
||||
use color_eyre::eyre::eyre;
|
||||
use brk_server::Server;
|
||||
use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
||||
let config = RunConfig::import(Some(config))?;
|
||||
use crate::config::Config;
|
||||
|
||||
pub fn run() -> color_eyre::Result<()> {
|
||||
let config = Config::import()?;
|
||||
|
||||
let rpc = config.rpc()?;
|
||||
|
||||
@@ -26,15 +18,33 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
||||
|
||||
let parser = brk_parser::Parser::new(config.blocksdir(), rpc);
|
||||
|
||||
let compressed = config.compressed();
|
||||
let format = config.format();
|
||||
|
||||
let mut indexer = Indexer::new(config.indexeddir(), compressed, config.check_collisions())?;
|
||||
indexer.import_stores()?;
|
||||
indexer.import_vecs()?;
|
||||
let mut indexer = Indexer::forced_import(&config.outputsdir())?;
|
||||
|
||||
let mut computer = Computer::new(config.computeddir(), config.fetcher(), compressed);
|
||||
computer.import_stores()?;
|
||||
computer.import_vecs()?;
|
||||
let wait_for_synced_node = || -> color_eyre::Result<()> {
|
||||
let is_synced = || -> color_eyre::Result<bool> {
|
||||
let info = rpc.get_blockchain_info()?;
|
||||
Ok(info.headers == info.blocks)
|
||||
};
|
||||
|
||||
if !is_synced()? {
|
||||
info!("Waiting for node to be synced...");
|
||||
while !is_synced()? {
|
||||
sleep(Duration::from_secs(1))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut computer = Computer::forced_import(
|
||||
&config.outputsdir(),
|
||||
&indexer,
|
||||
config.computation(),
|
||||
config.fetcher(),
|
||||
format,
|
||||
)?;
|
||||
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
@@ -46,8 +56,10 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
||||
|
||||
let server = Server::new(served_indexer, served_computer, config.website())?;
|
||||
|
||||
let watch = config.watch();
|
||||
let mcp = config.mcp();
|
||||
let opt = Some(tokio::spawn(async move {
|
||||
server.serve().await.unwrap();
|
||||
server.serve(watch, mcp).await.unwrap();
|
||||
}));
|
||||
|
||||
sleep(Duration::from_secs(1));
|
||||
@@ -58,22 +70,6 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
||||
};
|
||||
|
||||
if config.process() {
|
||||
let wait_for_synced_node = || -> color_eyre::Result<()> {
|
||||
let is_synced = || -> color_eyre::Result<bool> {
|
||||
let info = rpc.get_blockchain_info()?;
|
||||
Ok(info.headers == info.blocks)
|
||||
};
|
||||
|
||||
if !is_synced()? {
|
||||
info!("Waiting for node to be synced...");
|
||||
while !is_synced()? {
|
||||
sleep(Duration::from_secs(1))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
loop {
|
||||
wait_for_synced_node()?;
|
||||
|
||||
@@ -81,7 +77,8 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
||||
|
||||
info!("{} blocks found.", block_count + 1);
|
||||
|
||||
let starting_indexes = indexer.index(&parser, rpc, &exit)?;
|
||||
let starting_indexes =
|
||||
indexer.index(&parser, rpc, &exit, config.check_collisions())?;
|
||||
|
||||
computer.compute(&mut indexer, starting_indexes, &exit)?;
|
||||
|
||||
@@ -104,349 +101,3 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||
pub struct RunConfig {
|
||||
/// Bitcoin main directory path, defaults: ~/.bitcoin, ~/Library/Application\ Support/Bitcoin, saved
|
||||
#[arg(long, value_name = "PATH")]
|
||||
bitcoindir: Option<String>,
|
||||
|
||||
/// Bitcoin blocks directory path, default: --bitcoindir/blocks, saved
|
||||
#[arg(long, value_name = "PATH")]
|
||||
blocksdir: Option<String>,
|
||||
|
||||
/// Bitcoin Research Kit outputs directory path, default: ~/.brk, saved
|
||||
#[arg(long, value_name = "PATH")]
|
||||
brkdir: Option<String>,
|
||||
|
||||
/// Executed by the runner, default: all, saved
|
||||
#[arg(short, long)]
|
||||
mode: Option<Mode>,
|
||||
|
||||
/// Activate compression of datasets, set to true to save disk space or false if prioritize speed, default: true, saved
|
||||
#[arg(short, long, value_name = "BOOL")]
|
||||
compressed: Option<bool>,
|
||||
|
||||
/// Activate fetching prices from exchanges APIs and the computation of all related datasets, default: true, saved
|
||||
#[arg(short, long, value_name = "BOOL")]
|
||||
fetch: Option<bool>,
|
||||
|
||||
/// Website served by the server (if active), default: kibo.money, saved
|
||||
#[arg(short, long)]
|
||||
website: Option<Website>,
|
||||
|
||||
/// Bitcoin RPC ip, default: localhost, saved
|
||||
#[arg(long, value_name = "IP")]
|
||||
rpcconnect: Option<String>,
|
||||
|
||||
/// Bitcoin RPC port, default: 8332, saved
|
||||
#[arg(long, value_name = "PORT")]
|
||||
rpcport: Option<u16>,
|
||||
|
||||
/// Bitcoin RPC cookie file, default: --bitcoindir/.cookie, saved
|
||||
#[arg(long, value_name = "PATH")]
|
||||
rpccookiefile: Option<String>,
|
||||
|
||||
/// Bitcoin RPC username, saved
|
||||
#[arg(long, value_name = "USERNAME")]
|
||||
rpcuser: Option<String>,
|
||||
|
||||
/// Bitcoin RPC password, saved
|
||||
#[arg(long, value_name = "PASSWORD")]
|
||||
rpcpassword: Option<String>,
|
||||
|
||||
/// Delay between runs, default: 0, saved
|
||||
#[arg(long, value_name = "SECONDS")]
|
||||
delay: Option<u64>,
|
||||
|
||||
/// DEV: Activate checking address hashes for collisions when indexing, default: false, saved
|
||||
#[arg(long, value_name = "BOOL")]
|
||||
check_collisions: Option<bool>,
|
||||
}
|
||||
|
||||
impl RunConfig {
|
||||
pub fn import(config_args: Option<RunConfig>) -> color_eyre::Result<Self> {
|
||||
let path = dot_brk_path();
|
||||
|
||||
let _ = fs::create_dir_all(&path);
|
||||
|
||||
let path = path.join("config.toml");
|
||||
|
||||
let mut config_saved = Self::read(&path);
|
||||
|
||||
if let Some(mut config_args) = config_args {
|
||||
if let Some(bitcoindir) = config_args.bitcoindir.take() {
|
||||
config_saved.bitcoindir = Some(bitcoindir);
|
||||
}
|
||||
|
||||
if let Some(blocksdir) = config_args.blocksdir.take() {
|
||||
config_saved.blocksdir = Some(blocksdir);
|
||||
}
|
||||
|
||||
if let Some(brkdir) = config_args.brkdir.take() {
|
||||
config_saved.brkdir = Some(brkdir);
|
||||
}
|
||||
|
||||
if let Some(mode) = config_args.mode.take() {
|
||||
config_saved.mode = Some(mode);
|
||||
}
|
||||
|
||||
if let Some(fetch) = config_args.fetch.take() {
|
||||
config_saved.fetch = Some(fetch);
|
||||
}
|
||||
|
||||
if let Some(compressed) = config_args.compressed.take() {
|
||||
config_saved.compressed = Some(compressed);
|
||||
}
|
||||
|
||||
if let Some(website) = config_args.website.take() {
|
||||
config_saved.website = Some(website);
|
||||
}
|
||||
|
||||
if let Some(rpcconnect) = config_args.rpcconnect.take() {
|
||||
config_saved.rpcconnect = Some(rpcconnect);
|
||||
}
|
||||
|
||||
if let Some(rpcport) = config_args.rpcport.take() {
|
||||
config_saved.rpcport = Some(rpcport);
|
||||
}
|
||||
|
||||
if let Some(rpccookiefile) = config_args.rpccookiefile.take() {
|
||||
config_saved.rpccookiefile = Some(rpccookiefile);
|
||||
}
|
||||
|
||||
if let Some(rpcuser) = config_args.rpcuser.take() {
|
||||
config_saved.rpcuser = Some(rpcuser);
|
||||
}
|
||||
|
||||
if let Some(rpcpassword) = config_args.rpcpassword.take() {
|
||||
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 config_args != RunConfig::default() {
|
||||
dbg!(config_args);
|
||||
panic!("Didn't consume the full config")
|
||||
}
|
||||
}
|
||||
|
||||
let config = config_saved;
|
||||
|
||||
config.check();
|
||||
|
||||
config.write(&path)?;
|
||||
|
||||
// info!("Configuration {{");
|
||||
// info!(" bitcoindir: {:?}", config.bitcoindir);
|
||||
// info!(" brkdir: {:?}", config.brkdir);
|
||||
// info!(" mode: {:?}", config.mode);
|
||||
// info!(" website: {:?}", config.website);
|
||||
// info!(" rpcconnect: {:?}", config.rpcconnect);
|
||||
// info!(" rpcport: {:?}", config.rpcport);
|
||||
// info!(" rpccookiefile: {:?}", config.rpccookiefile);
|
||||
// info!(" rpcuser: {:?}", config.rpcuser);
|
||||
// info!(" rpcpassword: {:?}", config.rpcpassword);
|
||||
// info!(" delay: {:?}", config.delay);
|
||||
// info!("}}");
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn check(&self) {
|
||||
if !self.bitcoindir().is_dir() {
|
||||
println!("{:?} isn't a valid directory", self.bitcoindir());
|
||||
println!("Please use the --bitcoindir parameter to set a valid path.");
|
||||
println!("Run the program with '-h' for help.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if !self.blocksdir().is_dir() {
|
||||
println!("{:?} isn't a valid directory", self.blocksdir());
|
||||
println!("Please use the --blocksdir parameter to set a valid path.");
|
||||
println!("Run the program with '-h' for help.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if !self.brkdir().is_dir() {
|
||||
println!("{:?} isn't a valid directory", self.brkdir());
|
||||
println!("Please use the --brkdir parameter to set a valid path.");
|
||||
println!("Run the program with '-h' for help.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
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."
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn read(path: &Path) -> Self {
|
||||
fs::read_to_string(path).map_or_else(
|
||||
|_| RunConfig::default(),
|
||||
|contents| toml::from_str(&contents).unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn write(&self, path: &Path) -> std::io::Result<()> {
|
||||
fs::write(path, toml::to_string(self).unwrap())
|
||||
}
|
||||
|
||||
pub fn rpc(&self) -> color_eyre::Result<&'static Client> {
|
||||
Ok(Box::leak(Box::new(rpc::Client::new(
|
||||
&format!(
|
||||
"http://{}:{}",
|
||||
self.rpcconnect().unwrap_or(&"localhost".to_string()),
|
||||
self.rpcport().unwrap_or(8332)
|
||||
),
|
||||
self.rpc_auth().unwrap(),
|
||||
)?)))
|
||||
}
|
||||
|
||||
fn rpc_auth(&self) -> color_eyre::Result<Auth> {
|
||||
let cookie = self.path_cookiefile();
|
||||
|
||||
if cookie.is_file() {
|
||||
Ok(Auth::CookieFile(cookie))
|
||||
} else if self.rpcuser.is_some() && self.rpcpassword.is_some() {
|
||||
Ok(Auth::UserPass(
|
||||
self.rpcuser.clone().unwrap(),
|
||||
self.rpcpassword.clone().unwrap(),
|
||||
))
|
||||
} else {
|
||||
Err(eyre!("Failed to find correct auth"))
|
||||
}
|
||||
}
|
||||
|
||||
fn rpcconnect(&self) -> Option<&String> {
|
||||
self.rpcconnect.as_ref()
|
||||
}
|
||||
|
||||
fn rpcport(&self) -> Option<u16> {
|
||||
self.rpcport
|
||||
}
|
||||
|
||||
pub fn delay(&self) -> Option<u64> {
|
||||
self.delay
|
||||
}
|
||||
|
||||
pub fn bitcoindir(&self) -> PathBuf {
|
||||
self.bitcoindir
|
||||
.as_ref()
|
||||
.map_or_else(default_bitcoin_path, |s| Self::fix_user_path(s.as_ref()))
|
||||
}
|
||||
|
||||
pub fn blocksdir(&self) -> PathBuf {
|
||||
self.blocksdir.as_ref().map_or_else(
|
||||
|| self.bitcoindir().join("blocks"),
|
||||
|blocksdir| Self::fix_user_path(blocksdir.as_str()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn brkdir(&self) -> PathBuf {
|
||||
self.brkdir
|
||||
.as_ref()
|
||||
.map_or_else(default_brk_path, |s| Self::fix_user_path(s.as_ref()))
|
||||
}
|
||||
|
||||
fn outputsdir(&self) -> PathBuf {
|
||||
self.brkdir().join("outputs")
|
||||
}
|
||||
|
||||
pub fn indexeddir(&self) -> PathBuf {
|
||||
self.outputsdir().join("indexed")
|
||||
}
|
||||
|
||||
pub fn computeddir(&self) -> PathBuf {
|
||||
self.outputsdir().join("computed")
|
||||
}
|
||||
|
||||
pub fn harsdir(&self) -> PathBuf {
|
||||
self.outputsdir().join("hars")
|
||||
}
|
||||
|
||||
pub fn process(&self) -> bool {
|
||||
self.mode
|
||||
.is_none_or(|m| m == Mode::All || m == Mode::Processor)
|
||||
}
|
||||
|
||||
pub fn serve(&self) -> bool {
|
||||
self.mode
|
||||
.is_none_or(|m| m == Mode::All || m == Mode::Server)
|
||||
}
|
||||
|
||||
fn path_cookiefile(&self) -> PathBuf {
|
||||
self.rpccookiefile.as_ref().map_or_else(
|
||||
|| self.bitcoindir().join(".cookie"),
|
||||
|p| Self::fix_user_path(p.as_str()),
|
||||
)
|
||||
}
|
||||
|
||||
fn fix_user_path(path: &str) -> PathBuf {
|
||||
let fix = move |pattern: &str| {
|
||||
if path.starts_with(pattern) {
|
||||
let path = &path
|
||||
.replace(&format!("{pattern}/"), "")
|
||||
.replace(pattern, "");
|
||||
|
||||
let home = std::env::var("HOME").unwrap();
|
||||
|
||||
Some(Path::new(&home).join(path))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
fix("~").unwrap_or_else(|| fix("$HOME").unwrap_or_else(|| PathBuf::from(&path)))
|
||||
}
|
||||
|
||||
pub fn website(&self) -> Website {
|
||||
self.website.unwrap_or(Website::KiboMoney)
|
||||
}
|
||||
|
||||
pub fn fetch(&self) -> bool {
|
||||
self.fetch.is_none_or(|b| b)
|
||||
}
|
||||
|
||||
pub fn fetcher(&self) -> Option<Fetcher> {
|
||||
self.fetch()
|
||||
.then(|| Fetcher::import(Some(self.harsdir().as_path())).unwrap())
|
||||
}
|
||||
|
||||
pub fn compressed(&self) -> bool {
|
||||
self.compressed.is_none_or(|b| b)
|
||||
}
|
||||
|
||||
pub fn check_collisions(&self) -> bool {
|
||||
self.check_collisions.is_some_and(|b| b)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Default,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Parser,
|
||||
ValueEnum,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
)]
|
||||
pub enum Mode {
|
||||
#[default]
|
||||
All,
|
||||
Processor,
|
||||
Server,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
use clap_derive::{Parser, ValueEnum};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(
|
||||
Default,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Parser,
|
||||
ValueEnum,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
)]
|
||||
pub enum Services {
|
||||
#[default]
|
||||
All,
|
||||
Processor,
|
||||
Server,
|
||||
}
|
||||
@@ -4,15 +4,30 @@ description = "A Bitcoin dataset computer, built on top of brk_indexer"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode = { workspace = true }
|
||||
bitcoin = { workspace = true }
|
||||
bitcoincore-rpc = { workspace = true }
|
||||
brk_core = { workspace = true }
|
||||
brk_exit = { workspace = true }
|
||||
brk_fetcher = { workspace = true }
|
||||
brk_indexer = { workspace = true }
|
||||
brk_logger = { workspace = true }
|
||||
brk_parser = { workspace = true }
|
||||
brk_store = { workspace = true }
|
||||
brk_vec = { workspace = true }
|
||||
color-eyre = { workspace = true }
|
||||
derive_deref = { workspace = true }
|
||||
fjall = { workspace = true }
|
||||
jiff = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rayon = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
zerocopy = { workspace = true }
|
||||
zerocopy-derive = { workspace = true }
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["zerocopy"]
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
<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://kibo.money">
|
||||
<img alt="kibo.money" src="https://img.shields.io/badge/showcase-kib%C5%8D.money-orange">
|
||||
</a>
|
||||
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
|
||||
<img src="https://img.shields.io/crates/l/brk" alt="License" />
|
||||
</a>
|
||||
@@ -29,7 +26,7 @@
|
||||
<a href="https://bsky.app/profile/bitcoinresearchkit.org">
|
||||
<img src="https://img.shields.io/badge/bluesky-blue?link=https%3A%2F%2Fbsky.app%2Fprofile%2Fbitcoinresearchkit.org" alt="Bluesky" />
|
||||
</a>
|
||||
<a href="https://x.com/0xbrk">
|
||||
<a href="https://x.com/brkdotorg">
|
||||
<img src="https://img.shields.io/badge/x.com-black" alt="X" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use std::path::Path;
|
||||
use std::{path::Path, thread};
|
||||
|
||||
use brk_computer::Computer;
|
||||
use brk_core::default_bitcoin_path;
|
||||
use brk_core::{default_bitcoin_path, default_brk_path};
|
||||
use brk_exit::Exit;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_parser::{Parser, rpc};
|
||||
use brk_parser::Parser;
|
||||
use brk_vec::{Computation, Format};
|
||||
|
||||
pub fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
@@ -14,31 +15,42 @@ pub fn main() -> color_eyre::Result<()> {
|
||||
|
||||
let bitcoin_dir = default_bitcoin_path();
|
||||
|
||||
let rpc = Box::leak(Box::new(rpc::Client::new(
|
||||
let rpc = Box::leak(Box::new(bitcoincore_rpc::Client::new(
|
||||
"http://localhost:8332",
|
||||
rpc::Auth::CookieFile(bitcoin_dir.join(".cookie")),
|
||||
bitcoincore_rpc::Auth::CookieFile(bitcoin_dir.join(".cookie")),
|
||||
)?));
|
||||
let exit = Exit::new();
|
||||
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), rpc);
|
||||
// Can't increase main thread's stack programatically, thus we need to use another thread
|
||||
thread::Builder::new()
|
||||
.stack_size(32 * 1024 * 1024)
|
||||
.spawn(move || -> color_eyre::Result<()> {
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), rpc);
|
||||
|
||||
let outputs_dir = Path::new("../../_outputs");
|
||||
let _outputs_dir = default_brk_path().join("outputs");
|
||||
let outputs_dir = _outputs_dir.as_path();
|
||||
// let outputs_dir = Path::new("../../_outputs");
|
||||
|
||||
let compressed = true;
|
||||
let format = Format::Raw;
|
||||
|
||||
let mut indexer = Indexer::new(outputs_dir.join("indexed"), compressed, true)?;
|
||||
indexer.import_stores()?;
|
||||
indexer.import_vecs()?;
|
||||
let mut indexer = Indexer::forced_import(outputs_dir)?;
|
||||
|
||||
let fetcher = Fetcher::import(None)?;
|
||||
let fetcher = Fetcher::import(None)?;
|
||||
|
||||
let mut computer = Computer::new(outputs_dir.join("computed"), Some(fetcher), compressed);
|
||||
computer.import_stores()?;
|
||||
computer.import_vecs()?;
|
||||
let mut computer = Computer::forced_import(
|
||||
outputs_dir,
|
||||
&indexer,
|
||||
Computation::Lazy,
|
||||
Some(fetcher),
|
||||
format,
|
||||
)?;
|
||||
|
||||
let starting_indexes = indexer.index(&parser, rpc, &exit)?;
|
||||
let starting_indexes = indexer.index(&parser, rpc, &exit, true)?;
|
||||
|
||||
computer.compute(&mut indexer, starting_indexes, &exit)?;
|
||||
computer.compute(&mut indexer, starting_indexes, &exit)?;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})?
|
||||
.join()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -3,53 +3,60 @@
|
||||
#![doc = include_str!("../examples/main.rs")]
|
||||
#![doc = "```"]
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::Version;
|
||||
use brk_exit::Exit;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::{Indexer, Indexes};
|
||||
pub use brk_parser::rpc;
|
||||
|
||||
mod storage;
|
||||
|
||||
use brk_vec::Compressed;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{Computation, Format};
|
||||
use log::info;
|
||||
use storage::{Stores, Vecs};
|
||||
|
||||
mod states;
|
||||
mod stores;
|
||||
mod utils;
|
||||
mod vecs;
|
||||
|
||||
use states::*;
|
||||
use stores::Stores;
|
||||
use vecs::Vecs;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Computer {
|
||||
path: PathBuf,
|
||||
fetcher: Option<Fetcher>,
|
||||
vecs: Option<Vecs>,
|
||||
stores: Option<Stores>,
|
||||
compressed: Compressed,
|
||||
pub vecs: Vecs,
|
||||
pub stores: Stores,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE;
|
||||
|
||||
impl Computer {
|
||||
pub fn new(computed_dir: PathBuf, fetcher: Option<Fetcher>, compressed: bool) -> Self {
|
||||
Self {
|
||||
path: computed_dir,
|
||||
fetcher,
|
||||
vecs: None,
|
||||
stores: None,
|
||||
compressed: Compressed::from(compressed),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import_vecs(&mut self) -> color_eyre::Result<()> {
|
||||
self.vecs = Some(Vecs::import(
|
||||
&self.path.join("vecs"),
|
||||
self.fetcher.is_some(),
|
||||
self.compressed,
|
||||
)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Do NOT import multiple times or things will break !!!
|
||||
/// Clone struct instead
|
||||
pub fn import_stores(&mut self) -> color_eyre::Result<()> {
|
||||
self.stores = Some(Stores::import(&self.path.join("stores"))?);
|
||||
Ok(())
|
||||
pub fn forced_import(
|
||||
outputs_dir: &Path,
|
||||
indexer: &Indexer,
|
||||
computation: Computation,
|
||||
fetcher: Option<Fetcher>,
|
||||
format: Format,
|
||||
) -> color_eyre::Result<Self> {
|
||||
Ok(Self {
|
||||
vecs: Vecs::import(
|
||||
// TODO: Give self.path, join inside import
|
||||
&outputs_dir.join("vecs/computed"),
|
||||
VERSION + Version::ZERO,
|
||||
indexer,
|
||||
fetcher.is_some(),
|
||||
computation,
|
||||
format,
|
||||
)?,
|
||||
stores: Stores::import(
|
||||
// TODO: Give self.path, join inside import
|
||||
&outputs_dir.join("stores"),
|
||||
VERSION + Version::ZERO,
|
||||
&indexer.stores.keyspace,
|
||||
)?,
|
||||
fetcher,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,38 +64,16 @@ impl Computer {
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
starting_indexes: Indexes,
|
||||
starting_indexes: brk_indexer::Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
info!("Computing...");
|
||||
|
||||
self.vecs.as_mut().unwrap().compute(
|
||||
self.vecs.compute(
|
||||
indexer,
|
||||
starting_indexes,
|
||||
self.fetcher.as_mut(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &Path {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> &Vecs {
|
||||
self.vecs.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn mut_vecs(&mut self) -> &mut Vecs {
|
||||
self.vecs.as_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn stores(&self) -> &Stores {
|
||||
self.stores.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn mut_stores(&mut self) -> &mut Stores {
|
||||
self.stores.as_mut().unwrap()
|
||||
&mut self.stores,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
use std::ops::{Add, AddAssign, SubAssign};
|
||||
|
||||
use brk_core::{Dollars, Timestamp};
|
||||
|
||||
use super::SupplyState;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlockState {
|
||||
pub supply: SupplyState,
|
||||
pub price: Option<Dollars>,
|
||||
pub timestamp: Timestamp,
|
||||
}
|
||||
|
||||
impl Add<BlockState> for BlockState {
|
||||
type Output = Self;
|
||||
fn add(mut self, rhs: BlockState) -> Self::Output {
|
||||
self.supply += &rhs.supply;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&BlockState> for BlockState {
|
||||
fn add_assign(&mut self, rhs: &Self) {
|
||||
self.supply += &rhs.supply;
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<&BlockState> for BlockState {
|
||||
fn sub_assign(&mut self, rhs: &Self) {
|
||||
self.supply -= &rhs.supply;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{AddressData, Dollars, Height, Result, Sats};
|
||||
|
||||
use crate::SupplyState;
|
||||
|
||||
use super::CohortState;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AddressCohortState {
|
||||
pub address_count: usize,
|
||||
pub inner: CohortState,
|
||||
}
|
||||
|
||||
impl AddressCohortState {
|
||||
pub fn default_and_import(path: &Path, name: &str, compute_dollars: bool) -> Result<Self> {
|
||||
Ok(Self {
|
||||
address_count: 0,
|
||||
inner: CohortState::default_and_import(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_single_iteration_values(&mut self) {
|
||||
self.inner.reset_single_iteration_values();
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn send(
|
||||
&mut self,
|
||||
addressdata: &mut AddressData,
|
||||
value: Sats,
|
||||
current_price: Option<Dollars>,
|
||||
prev_price: Option<Dollars>,
|
||||
blocks_old: usize,
|
||||
days_old: f64,
|
||||
older_than_hour: bool,
|
||||
) -> Result<()> {
|
||||
let compute_price = current_price.is_some();
|
||||
|
||||
let prev_realized_price = compute_price.then(|| addressdata.realized_price());
|
||||
let prev_supply_state = SupplyState {
|
||||
utxos: addressdata.outputs_len as usize,
|
||||
value: addressdata.amount(),
|
||||
};
|
||||
|
||||
addressdata.send(value, prev_price)?;
|
||||
|
||||
let supply_state = SupplyState {
|
||||
utxos: addressdata.outputs_len as usize,
|
||||
value: addressdata.amount(),
|
||||
};
|
||||
|
||||
self.inner.send_(
|
||||
&SupplyState { utxos: 1, value },
|
||||
current_price,
|
||||
prev_price,
|
||||
blocks_old,
|
||||
days_old,
|
||||
older_than_hour,
|
||||
compute_price.then(|| (addressdata.realized_price(), &supply_state)),
|
||||
prev_realized_price.map(|prev_price| (prev_price, &prev_supply_state)),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn receive(&mut self, address_data: &mut AddressData, value: Sats, price: Option<Dollars>) {
|
||||
let compute_price = price.is_some();
|
||||
|
||||
let prev_realized_price = compute_price.then(|| address_data.realized_price());
|
||||
let prev_supply_state = SupplyState {
|
||||
utxos: address_data.outputs_len as usize,
|
||||
value: address_data.amount(),
|
||||
};
|
||||
|
||||
address_data.receive(value, price);
|
||||
|
||||
let supply_state = SupplyState {
|
||||
utxos: address_data.outputs_len as usize,
|
||||
value: address_data.amount(),
|
||||
};
|
||||
|
||||
self.inner.receive_(
|
||||
&SupplyState { utxos: 1, value },
|
||||
price,
|
||||
compute_price.then(|| (address_data.realized_price(), &supply_state)),
|
||||
prev_realized_price.map(|prev_price| (prev_price, &prev_supply_state)),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn add(&mut self, addressdata: &AddressData) {
|
||||
self.address_count += 1;
|
||||
self.inner.increment_(
|
||||
&addressdata.into(),
|
||||
addressdata.realized_cap,
|
||||
addressdata.realized_price(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn subtract(&mut self, addressdata: &AddressData) {
|
||||
self.address_count = self.address_count.checked_sub(1).unwrap();
|
||||
self.inner.decrement_(
|
||||
&addressdata.into(),
|
||||
addressdata.realized_cap,
|
||||
addressdata.realized_price(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn commit(&mut self, height: Height) -> Result<()> {
|
||||
self.inner.commit(height)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
use std::{cmp::Ordering, path::Path};
|
||||
|
||||
use brk_core::{CheckedSub, Dollars, Height, Result, Sats};
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
impl CohortState {
|
||||
pub fn default_and_import(path: &Path, name: &str, compute_dollars: bool) -> Result<Self> {
|
||||
Ok(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),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<Height> {
|
||||
self.price_to_amount.height()
|
||||
}
|
||||
|
||||
pub fn reset_price_to_amount(&mut self) -> Result<()> {
|
||||
self.price_to_amount.reset()
|
||||
}
|
||||
|
||||
pub fn price_to_amount_first_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.price_to_amount.first_key_value()
|
||||
}
|
||||
|
||||
pub fn price_to_amount_last_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.price_to_amount.last_key_value()
|
||||
}
|
||||
|
||||
pub fn reset_single_iteration_values(&mut self) {
|
||||
self.satdays_destroyed = Sats::ZERO;
|
||||
self.satblocks_destroyed = Sats::ZERO;
|
||||
if let Some(realized) = self.realized.as_mut() {
|
||||
realized.reset_single_iteration_values();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, supply_state: &SupplyState, price: Option<Dollars>) {
|
||||
self.supply += supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO {
|
||||
if let Some(realized) = self.realized.as_mut() {
|
||||
let price = price.unwrap();
|
||||
realized.increment(supply_state, price);
|
||||
self.price_to_amount.increment(price, supply_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment_(
|
||||
&mut self,
|
||||
supply_state: &SupplyState,
|
||||
realized_cap: Dollars,
|
||||
realized_price: Dollars,
|
||||
) {
|
||||
self.supply += supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO {
|
||||
if let Some(realized) = self.realized.as_mut() {
|
||||
realized.increment_(realized_cap);
|
||||
self.price_to_amount.increment(realized_price, supply_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, supply_state: &SupplyState, price: Option<Dollars>) {
|
||||
self.supply -= supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO {
|
||||
if let Some(realized) = self.realized.as_mut() {
|
||||
let price = price.unwrap();
|
||||
realized.decrement(supply_state, price);
|
||||
self.price_to_amount.decrement(price, supply_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrement_(
|
||||
&mut self,
|
||||
supply_state: &SupplyState,
|
||||
realized_cap: Dollars,
|
||||
realized_price: Dollars,
|
||||
) {
|
||||
self.supply -= supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO {
|
||||
if let Some(realized) = self.realized.as_mut() {
|
||||
realized.decrement_(realized_cap);
|
||||
self.price_to_amount.decrement(realized_price, supply_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive(&mut self, supply_state: &SupplyState, price: Option<Dollars>) {
|
||||
self.receive_(
|
||||
supply_state,
|
||||
price,
|
||||
price.map(|price| (price, supply_state)),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn receive_(
|
||||
&mut self,
|
||||
supply_state: &SupplyState,
|
||||
price: Option<Dollars>,
|
||||
price_to_amount_increment: Option<(Dollars, &SupplyState)>,
|
||||
price_to_amount_decrement: Option<(Dollars, &SupplyState)>,
|
||||
) {
|
||||
self.supply += supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO {
|
||||
if let Some(realized) = self.realized.as_mut() {
|
||||
let price = price.unwrap();
|
||||
realized.receive(supply_state, price);
|
||||
|
||||
if let Some((price, supply)) = price_to_amount_increment
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.increment(price, supply);
|
||||
}
|
||||
if let Some((price, supply)) = price_to_amount_decrement
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.decrement(price, supply);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(
|
||||
&mut self,
|
||||
supply_state: &SupplyState,
|
||||
current_price: Option<Dollars>,
|
||||
prev_price: Option<Dollars>,
|
||||
blocks_old: usize,
|
||||
days_old: f64,
|
||||
older_than_hour: bool,
|
||||
) {
|
||||
self.send_(
|
||||
supply_state,
|
||||
current_price,
|
||||
prev_price,
|
||||
blocks_old,
|
||||
days_old,
|
||||
older_than_hour,
|
||||
None,
|
||||
prev_price.map(|prev_price| (prev_price, supply_state)),
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn send_(
|
||||
&mut self,
|
||||
supply_state: &SupplyState,
|
||||
current_price: Option<Dollars>,
|
||||
prev_price: Option<Dollars>,
|
||||
blocks_old: usize,
|
||||
days_old: f64,
|
||||
older_than_hour: bool,
|
||||
price_to_amount_increment: Option<(Dollars, &SupplyState)>,
|
||||
price_to_amount_decrement: Option<(Dollars, &SupplyState)>,
|
||||
) {
|
||||
if supply_state.utxos == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
self.supply -= supply_state;
|
||||
|
||||
if supply_state.value > Sats::ZERO {
|
||||
self.satblocks_destroyed += supply_state.value * blocks_old;
|
||||
|
||||
self.satdays_destroyed +=
|
||||
Sats::from((u64::from(supply_state.value) as f64 * days_old).floor() as u64);
|
||||
|
||||
if let Some(realized) = self.realized.as_mut() {
|
||||
let current_price = current_price.unwrap();
|
||||
let prev_price = prev_price.unwrap();
|
||||
realized.send(supply_state, current_price, prev_price, older_than_hour);
|
||||
if let Some((price, supply)) = price_to_amount_increment
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.increment(price, supply);
|
||||
}
|
||||
if let Some((price, supply)) = price_to_amount_decrement
|
||||
&& supply.value.is_not_zero()
|
||||
{
|
||||
self.price_to_amount.decrement(price, supply);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_unrealized_states(
|
||||
&self,
|
||||
height_price: Dollars,
|
||||
date_price: Option<Dollars>,
|
||||
) -> (UnrealizedState, Option<UnrealizedState>) {
|
||||
if self.price_to_amount.is_empty() {
|
||||
return (
|
||||
UnrealizedState::NAN,
|
||||
date_price.map(|_| UnrealizedState::NAN),
|
||||
);
|
||||
}
|
||||
|
||||
let mut height_unrealized_state = UnrealizedState::ZERO;
|
||||
let mut date_unrealized_state = date_price.map(|_| UnrealizedState::ZERO);
|
||||
|
||||
let update_state =
|
||||
|price: Dollars, current_price: Dollars, sats: Sats, state: &mut UnrealizedState| {
|
||||
match price.cmp(¤t_price) {
|
||||
Ordering::Equal => {
|
||||
state.supply_even += sats;
|
||||
}
|
||||
Ordering::Less => {
|
||||
state.supply_in_profit += sats;
|
||||
if price > Dollars::ZERO && current_price > Dollars::ZERO {
|
||||
let diff = current_price.checked_sub(price).unwrap();
|
||||
if diff <= Dollars::ZERO {
|
||||
dbg!(price, current_price, diff, sats);
|
||||
panic!();
|
||||
}
|
||||
state.unrealized_profit += diff * sats;
|
||||
}
|
||||
}
|
||||
Ordering::Greater => {
|
||||
state.supply_in_loss += sats;
|
||||
if price > Dollars::ZERO && current_price > Dollars::ZERO {
|
||||
let diff = price.checked_sub(current_price).unwrap();
|
||||
if diff <= Dollars::ZERO {
|
||||
dbg!(price, current_price, diff, sats);
|
||||
panic!();
|
||||
}
|
||||
state.unrealized_loss += diff * sats;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.price_to_amount.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(),
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
(height_unrealized_state, date_unrealized_state)
|
||||
}
|
||||
|
||||
pub fn commit(&mut self, height: Height) -> Result<()> {
|
||||
self.price_to_amount.flush(height)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
mod address;
|
||||
mod common;
|
||||
mod utxo;
|
||||
|
||||
pub use address::*;
|
||||
pub use common::*;
|
||||
pub use utxo::*;
|
||||
@@ -0,0 +1,27 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Height, Result};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use super::CohortState;
|
||||
|
||||
#[derive(Clone, Deref, DerefMut)]
|
||||
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 height(&self) -> Option<Height> {
|
||||
self.0.height()
|
||||
}
|
||||
|
||||
pub fn reset_price_to_amount(&mut self) -> Result<()> {
|
||||
self.0.reset_price_to_amount()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
mod block;
|
||||
mod cohorts;
|
||||
mod price_to_amount;
|
||||
mod realized;
|
||||
mod supply;
|
||||
mod transacted;
|
||||
mod unrealized;
|
||||
|
||||
pub use block::*;
|
||||
pub use cohorts::*;
|
||||
pub use price_to_amount::*;
|
||||
pub use realized::*;
|
||||
pub use supply::*;
|
||||
pub use transacted::*;
|
||||
pub use unrealized::*;
|
||||
@@ -0,0 +1,120 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs::{self, File},
|
||||
io::{BufReader, BufWriter},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use bincode::{Decode, Encode, config, decode_from_std_read, encode_into_std_write};
|
||||
use brk_core::{Dollars, Height, Result, Sats};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::states::SupplyState;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PriceToAmount {
|
||||
pathbuf: PathBuf,
|
||||
height: Option<Height>,
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Deref, DerefMut, Serialize, Deserialize, Encode, Decode)]
|
||||
struct State(BTreeMap<Dollars, Sats>);
|
||||
|
||||
impl PriceToAmount {
|
||||
pub fn forced_import(path: &Path, name: &str) -> Self {
|
||||
Self::import(path, name).unwrap_or_else(|_| Self {
|
||||
pathbuf: Self::path_(path, name),
|
||||
height: None,
|
||||
state: State::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn import(path: &Path, name: &str) -> Result<Self> {
|
||||
let path = Self::path_(path, name);
|
||||
fs::create_dir_all(&path)?;
|
||||
|
||||
let config = config::standard();
|
||||
let file = File::open(Self::path_state_(&path))?;
|
||||
let mut reader = BufReader::new(file);
|
||||
let state = decode_from_std_read(&mut reader, config)?;
|
||||
|
||||
Ok(Self {
|
||||
height: Height::try_from(Self::path_height_(&path).as_path()).ok(),
|
||||
pathbuf: path,
|
||||
state,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Dollars, &Sats)> {
|
||||
self.state.iter()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.state.is_empty()
|
||||
}
|
||||
|
||||
pub fn first_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.state.first_key_value()
|
||||
}
|
||||
|
||||
pub fn last_key_value(&self) -> Option<(&Dollars, &Sats)> {
|
||||
self.state.last_key_value()
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, price: Dollars, supply_state: &SupplyState) {
|
||||
*self.state.entry(price).or_default() += supply_state.value;
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, price: Dollars, supply_state: &SupplyState) {
|
||||
let amount = self.state.get_mut(&price).unwrap();
|
||||
*amount -= supply_state.value;
|
||||
if *amount == Sats::ZERO {
|
||||
self.state.remove(&price);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Result<()> {
|
||||
self.state.clear();
|
||||
self.height = None;
|
||||
fs::remove_dir_all(&self.pathbuf)?;
|
||||
fs::create_dir_all(&self.pathbuf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flush(&mut self, height: Height) -> Result<()> {
|
||||
self.height = Some(height);
|
||||
height.write(&self.path_height())?;
|
||||
|
||||
let config = config::standard();
|
||||
let file = File::create(self.path_state()).inspect_err(|_| {
|
||||
dbg!(self.path_state());
|
||||
})?;
|
||||
let mut writer = BufWriter::new(file);
|
||||
encode_into_std_write(&self.state, &mut writer, config)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<Height> {
|
||||
self.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")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use brk_core::{CheckedSub, Dollars};
|
||||
|
||||
use super::SupplyState;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct RealizedState {
|
||||
pub cap: Dollars,
|
||||
pub profit: Dollars,
|
||||
pub loss: Dollars,
|
||||
pub value_created: Dollars,
|
||||
pub adj_value_created: Dollars,
|
||||
pub value_destroyed: Dollars,
|
||||
pub adj_value_destroyed: Dollars,
|
||||
}
|
||||
|
||||
impl RealizedState {
|
||||
pub const NAN: Self = Self {
|
||||
cap: Dollars::NAN,
|
||||
profit: Dollars::NAN,
|
||||
loss: Dollars::NAN,
|
||||
value_created: Dollars::NAN,
|
||||
adj_value_created: Dollars::NAN,
|
||||
value_destroyed: Dollars::NAN,
|
||||
adj_value_destroyed: Dollars::NAN,
|
||||
};
|
||||
|
||||
pub fn reset_single_iteration_values(&mut self) {
|
||||
if self.cap != Dollars::NAN {
|
||||
self.profit = Dollars::ZERO;
|
||||
self.loss = Dollars::ZERO;
|
||||
self.value_created = Dollars::ZERO;
|
||||
self.adj_value_created = Dollars::ZERO;
|
||||
self.value_destroyed = Dollars::ZERO;
|
||||
self.adj_value_destroyed = Dollars::ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, supply_state: &SupplyState, price: Dollars) {
|
||||
if supply_state.value.is_zero() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.increment_(price * supply_state.value)
|
||||
}
|
||||
|
||||
pub fn increment_(&mut self, realized_cap: Dollars) {
|
||||
if self.cap == Dollars::NAN {
|
||||
self.cap = Dollars::ZERO;
|
||||
self.profit = Dollars::ZERO;
|
||||
self.loss = Dollars::ZERO;
|
||||
self.value_created = Dollars::ZERO;
|
||||
self.adj_value_created = Dollars::ZERO;
|
||||
self.value_destroyed = Dollars::ZERO;
|
||||
self.adj_value_destroyed = Dollars::ZERO;
|
||||
}
|
||||
|
||||
self.cap += realized_cap;
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, supply_state: &SupplyState, price: Dollars) {
|
||||
self.decrement_(price * supply_state.value);
|
||||
}
|
||||
|
||||
pub fn decrement_(&mut self, realized_cap: Dollars) {
|
||||
self.cap = self.cap.checked_sub(realized_cap).unwrap();
|
||||
}
|
||||
|
||||
pub fn receive(&mut self, supply_state: &SupplyState, current_price: Dollars) {
|
||||
self.increment(supply_state, current_price);
|
||||
}
|
||||
|
||||
pub fn send(
|
||||
&mut self,
|
||||
supply_state: &SupplyState,
|
||||
current_price: Dollars,
|
||||
prev_price: Dollars,
|
||||
older_than_hour: bool,
|
||||
) {
|
||||
let current_value = current_price * supply_state.value;
|
||||
let prev_value = prev_price * supply_state.value;
|
||||
|
||||
self.value_created += current_value;
|
||||
self.value_destroyed += prev_value;
|
||||
|
||||
if older_than_hour {
|
||||
self.adj_value_created += current_value;
|
||||
self.adj_value_destroyed += prev_value;
|
||||
}
|
||||
|
||||
match current_price.cmp(&prev_price) {
|
||||
Ordering::Greater => {
|
||||
self.profit += current_value.checked_sub(prev_value).unwrap();
|
||||
}
|
||||
Ordering::Less => {
|
||||
self.loss += prev_value.checked_sub(current_value).unwrap();
|
||||
}
|
||||
Ordering::Equal => {}
|
||||
}
|
||||
|
||||
self.decrement(supply_state, prev_price);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
use std::ops::{Add, AddAssign, SubAssign};
|
||||
|
||||
use brk_core::{AddressData, CheckedSub, Sats};
|
||||
use serde::Serialize;
|
||||
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
||||
|
||||
#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, KnownLayout, Serialize)]
|
||||
pub struct SupplyState {
|
||||
pub utxos: usize,
|
||||
pub value: Sats,
|
||||
}
|
||||
|
||||
impl Add<SupplyState> for SupplyState {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: SupplyState) -> Self::Output {
|
||||
Self {
|
||||
utxos: self.utxos + rhs.utxos,
|
||||
value: self.value + rhs.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<SupplyState> for SupplyState {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self += &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&SupplyState> for SupplyState {
|
||||
fn add_assign(&mut self, rhs: &Self) {
|
||||
self.utxos += rhs.utxos;
|
||||
self.value += rhs.value;
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<&SupplyState> for SupplyState {
|
||||
fn sub_assign(&mut self, rhs: &Self) {
|
||||
self.utxos = self.utxos.checked_sub(rhs.utxos).unwrap();
|
||||
self.value = self.value.checked_sub(rhs.value).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&AddressData> for SupplyState {
|
||||
fn from(value: &AddressData) -> Self {
|
||||
Self {
|
||||
utxos: value.outputs_len as usize,
|
||||
value: value.amount(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use std::ops::{Add, AddAssign};
|
||||
|
||||
use brk_core::{ByAmountRange, GroupedByType, OutputType, Sats};
|
||||
|
||||
use super::SupplyState;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Transacted {
|
||||
pub spendable_supply: SupplyState,
|
||||
pub by_type: GroupedByType<SupplyState>,
|
||||
pub by_size_group: ByAmountRange<SupplyState>,
|
||||
}
|
||||
|
||||
impl Transacted {
|
||||
#[allow(clippy::inconsistent_digit_grouping)]
|
||||
pub fn iterate(&mut self, value: Sats, _type: OutputType) {
|
||||
let supply = SupplyState { utxos: 1, value };
|
||||
|
||||
*self.by_type.get_mut(_type) += &supply;
|
||||
|
||||
if _type.is_unspendable() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.spendable_supply += &supply;
|
||||
|
||||
*self.by_size_group.get_mut(value) += &supply;
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Transacted {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
spendable_supply: self.spendable_supply + rhs.spendable_supply,
|
||||
by_type: self.by_type + rhs.by_type,
|
||||
by_size_group: self.by_size_group + rhs.by_size_group,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Transacted {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.by_size_group += rhs.by_size_group;
|
||||
self.spendable_supply += &rhs.spendable_supply;
|
||||
self.by_type += rhs.by_type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
use brk_core::{Dollars, Sats};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct UnrealizedState {
|
||||
pub supply_in_profit: Sats,
|
||||
pub supply_even: Sats,
|
||||
pub supply_in_loss: Sats,
|
||||
pub unrealized_profit: Dollars,
|
||||
pub unrealized_loss: Dollars,
|
||||
}
|
||||
|
||||
impl UnrealizedState {
|
||||
pub const NAN: Self = Self {
|
||||
supply_in_profit: Sats::ZERO,
|
||||
supply_even: Sats::ZERO,
|
||||
supply_in_loss: Sats::ZERO,
|
||||
unrealized_profit: Dollars::NAN,
|
||||
unrealized_loss: Dollars::NAN,
|
||||
};
|
||||
|
||||
pub const ZERO: Self = Self {
|
||||
supply_in_profit: Sats::ZERO,
|
||||
supply_even: Sats::ZERO,
|
||||
supply_in_loss: Sats::ZERO,
|
||||
unrealized_profit: Dollars::ZERO,
|
||||
unrealized_loss: Dollars::ZERO,
|
||||
};
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
mod stores;
|
||||
mod vecs;
|
||||
|
||||
pub use stores::*;
|
||||
pub use vecs::*;
|
||||
@@ -1,25 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{AddressindexTxoutindex, Unit};
|
||||
use brk_indexer::Store;
|
||||
use brk_vec::Version;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Stores {
|
||||
pub address_to_utxos_received: Store<AddressindexTxoutindex, Unit>,
|
||||
pub address_to_utxos_spent: Store<AddressindexTxoutindex, Unit>,
|
||||
}
|
||||
|
||||
impl Stores {
|
||||
pub fn import(path: &Path) -> color_eyre::Result<Self> {
|
||||
let address_to_utxos_received =
|
||||
Store::import(&path.join("address_to_utxos_received"), Version::ZERO)?;
|
||||
let address_to_utxos_spent =
|
||||
Store::import(&path.join("address_to_utxos_spent"), Version::ZERO)?;
|
||||
|
||||
Ok(Self {
|
||||
address_to_utxos_received,
|
||||
address_to_utxos_spent,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,505 +0,0 @@
|
||||
use core::error;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
fmt::Debug,
|
||||
ops::Add,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use brk_core::{Bitcoin, CheckedSub, Close, Dollars, Height, Sats, Txindex};
|
||||
use brk_exit::Exit;
|
||||
use brk_vec::{
|
||||
Compressed, DynamicVec, Error, GenericVec, Result, StoredIndex, StoredType, StoredVec, Version,
|
||||
};
|
||||
use log::info;
|
||||
|
||||
const ONE_KIB: usize = 1024;
|
||||
const ONE_MIB: usize = ONE_KIB * ONE_KIB;
|
||||
const MAX_CACHE_SIZE: usize = 210 * ONE_MIB;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ComputedVec<I, T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: StoredType,
|
||||
{
|
||||
computed_version: Option<Version>,
|
||||
inner: StoredVec<I, T>,
|
||||
}
|
||||
|
||||
impl<I, T> ComputedVec<I, T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: StoredType,
|
||||
{
|
||||
const SIZE_OF: usize = size_of::<T>();
|
||||
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
version: Version,
|
||||
compressed: Compressed,
|
||||
) -> brk_vec::Result<Self> {
|
||||
let inner = StoredVec::forced_import(path, version, compressed)?;
|
||||
|
||||
Ok(Self {
|
||||
computed_version: None,
|
||||
inner,
|
||||
})
|
||||
}
|
||||
|
||||
fn safe_truncate_if_needed(&mut self, index: I, exit: &Exit) -> Result<()> {
|
||||
if exit.triggered() {
|
||||
return Ok(());
|
||||
}
|
||||
exit.block();
|
||||
self.inner.truncate_if_needed(index)?;
|
||||
exit.release();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn forced_push_at(&mut self, index: I, value: T, exit: &Exit) -> Result<()> {
|
||||
match self.len().cmp(&index.to_usize()?) {
|
||||
Ordering::Less => {
|
||||
return Err(Error::IndexTooHigh);
|
||||
}
|
||||
ord => {
|
||||
if ord == Ordering::Greater {
|
||||
self.safe_truncate_if_needed(index, exit)?;
|
||||
}
|
||||
self.inner.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
if self.inner.pushed_len() * Self::SIZE_OF >= MAX_CACHE_SIZE {
|
||||
self.safe_flush(exit)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn safe_flush(&mut self, exit: &Exit) -> Result<()> {
|
||||
if exit.triggered() {
|
||||
return Ok(());
|
||||
}
|
||||
exit.block();
|
||||
self.inner.flush()?;
|
||||
exit.release();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn version(&self) -> Version {
|
||||
self.inner.version()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.is_empty()
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
self.inner.file_name()
|
||||
}
|
||||
|
||||
pub fn vec(&self) -> &StoredVec<I, T> {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn mut_vec(&mut self) -> &mut StoredVec<I, T> {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
pub fn any_vec(&self) -> &dyn brk_vec::AnyStoredVec {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn mut_any_vec(&mut self) -> &mut dyn brk_vec::AnyStoredVec {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
pub fn unwrap_cached_get(&mut self, index: I) -> Option<T> {
|
||||
self.inner.unwrap_cached_get(index)
|
||||
}
|
||||
#[inline]
|
||||
pub fn double_unwrap_cached_get(&mut self, index: I) -> T {
|
||||
self.inner.double_unwrap_cached_get(index)
|
||||
}
|
||||
|
||||
pub fn collect_inclusive_range(&self, from: I, to: I) -> Result<Vec<T>> {
|
||||
self.inner.collect_inclusive_range(from, to)
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &Path {
|
||||
self.inner.path()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn path_computed_version(&self) -> PathBuf {
|
||||
self.inner.path().join("computed_version")
|
||||
}
|
||||
|
||||
fn validate_computed_version_or_reset_file(&mut self, version: Version) -> Result<()> {
|
||||
let path = self.path_computed_version();
|
||||
if version.validate(path.as_ref()).is_err() {
|
||||
self.inner.reset()?;
|
||||
}
|
||||
version.write(path.as_ref())?;
|
||||
|
||||
if self.is_empty() {
|
||||
info!("Computing {}...", self.file_name())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_transform<A, B, F>(
|
||||
&mut self,
|
||||
max_from: A,
|
||||
other: &mut StoredVec<A, B>,
|
||||
mut t: F,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
A: StoredIndex,
|
||||
B: StoredType,
|
||||
F: FnMut((A, B, &mut Self, &mut dyn DynamicVec<I = A, T = B>)) -> (I, T),
|
||||
{
|
||||
self.validate_computed_version_or_reset_file(
|
||||
Version::ZERO + self.version() + other.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(A::from(self.len()));
|
||||
other.iter_from(index, |(a, b, other)| {
|
||||
let (i, v) = t((a, b, self, other));
|
||||
self.forced_push_at(i, v, exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)
|
||||
}
|
||||
|
||||
pub fn compute_inverse_more_to_less(
|
||||
&mut self,
|
||||
max_from: T,
|
||||
other: &mut StoredVec<T, I>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
I: StoredType + StoredIndex,
|
||||
T: StoredIndex,
|
||||
{
|
||||
self.validate_computed_version_or_reset_file(
|
||||
Version::ZERO + self.version() + other.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(
|
||||
self.inner
|
||||
.cached_get_last()?
|
||||
.map_or_else(T::default, |v| v.into_inner()),
|
||||
);
|
||||
other.iter_from(index, |(v, i, ..)| {
|
||||
if self.unwrap_cached_get(i).is_none_or(|old_v| old_v > v) {
|
||||
self.forced_push_at(i, v, exit)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)
|
||||
}
|
||||
|
||||
pub fn compute_inverse_less_to_more(
|
||||
&mut self,
|
||||
max_from: T,
|
||||
first_indexes: &mut StoredVec<T, I>,
|
||||
last_indexes: &mut StoredVec<T, I>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
I: StoredType,
|
||||
T: StoredIndex,
|
||||
{
|
||||
self.validate_computed_version_or_reset_file(
|
||||
Version::ZERO + self.version() + first_indexes.version() + last_indexes.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(T::from(self.len()));
|
||||
first_indexes.iter_from(index, |(value, first_index, ..)| {
|
||||
let first_index = (first_index).to_usize()?;
|
||||
let last_index = (last_indexes.double_unwrap_cached_get(value)).to_usize()?;
|
||||
(first_index..last_index)
|
||||
.try_for_each(|index| self.forced_push_at(I::from(index), value, exit))
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)
|
||||
}
|
||||
|
||||
pub fn compute_last_index_from_first(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
first_indexes: &mut StoredVec<I, T>,
|
||||
final_len: usize,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Copy + From<usize> + CheckedSub<T> + StoredIndex,
|
||||
{
|
||||
self.validate_computed_version_or_reset_file(
|
||||
Version::ZERO + self.version() + first_indexes.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(I::from(self.len()));
|
||||
let one = T::from(1);
|
||||
let mut prev_index: Option<I> = None;
|
||||
first_indexes.iter_from(index, |(index, v, ..)| {
|
||||
if let Some(prev_index) = prev_index.take() {
|
||||
let value = v.checked_sub(one).unwrap();
|
||||
self.forced_push_at(prev_index, value, exit)?;
|
||||
}
|
||||
prev_index.replace(index);
|
||||
Ok(())
|
||||
})?;
|
||||
if let Some(prev_index) = prev_index {
|
||||
self.forced_push_at(
|
||||
prev_index,
|
||||
T::from(final_len).checked_sub(one).unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
self.safe_flush(exit)
|
||||
}
|
||||
|
||||
pub fn compute_count_from_indexes<T2>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
first_indexes: &mut StoredVec<I, T2>,
|
||||
last_indexes: &mut StoredVec<I, T2>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: From<T2>,
|
||||
T2: StoredType
|
||||
+ StoredIndex
|
||||
+ Copy
|
||||
+ Add<usize, Output = T2>
|
||||
+ CheckedSub<T2>
|
||||
+ TryInto<T>
|
||||
+ Default,
|
||||
<T2 as TryInto<T>>::Error: error::Error + 'static,
|
||||
{
|
||||
let opt: Option<Box<dyn FnMut(T2) -> bool>> = None;
|
||||
self.compute_filtered_count_from_indexes_(max_from, first_indexes, last_indexes, opt, exit)
|
||||
}
|
||||
|
||||
pub fn compute_filtered_count_from_indexes<T2, F>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
first_indexes: &mut StoredVec<I, T2>,
|
||||
last_indexes: &mut StoredVec<I, T2>,
|
||||
filter: F,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: From<T2>,
|
||||
T2: StoredType
|
||||
+ StoredIndex
|
||||
+ Copy
|
||||
+ Add<usize, Output = T2>
|
||||
+ CheckedSub<T2>
|
||||
+ TryInto<T>
|
||||
+ Default,
|
||||
<T2 as TryInto<T>>::Error: error::Error + 'static,
|
||||
F: FnMut(T2) -> bool,
|
||||
{
|
||||
self.compute_filtered_count_from_indexes_(
|
||||
max_from,
|
||||
first_indexes,
|
||||
last_indexes,
|
||||
Some(Box::new(filter)),
|
||||
exit,
|
||||
)
|
||||
}
|
||||
|
||||
fn compute_filtered_count_from_indexes_<T2>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
first_indexes: &mut StoredVec<I, T2>,
|
||||
last_indexes: &mut StoredVec<I, T2>,
|
||||
mut filter: Option<Box<dyn FnMut(T2) -> bool + '_>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: From<T2>,
|
||||
T2: StoredType
|
||||
+ StoredIndex
|
||||
+ Copy
|
||||
+ Add<usize, Output = T2>
|
||||
+ CheckedSub<T2>
|
||||
+ TryInto<T>
|
||||
+ Default,
|
||||
<T2 as TryInto<T>>::Error: error::Error + 'static,
|
||||
{
|
||||
self.validate_computed_version_or_reset_file(
|
||||
Version::ZERO + self.version() + first_indexes.version() + last_indexes.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(I::from(self.len()));
|
||||
first_indexes.iter_from(index, |(i, first_index, ..)| {
|
||||
let last_index = last_indexes.double_unwrap_cached_get(i);
|
||||
let range = first_index.unwrap_to_usize()..=last_index.unwrap_to_usize();
|
||||
let count = if let Some(filter) = filter.as_mut() {
|
||||
range.into_iter().filter(|i| filter(T2::from(*i))).count()
|
||||
} else {
|
||||
range.count()
|
||||
};
|
||||
self.forced_push_at(i, T::from(T2::from(count)), exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)
|
||||
}
|
||||
|
||||
pub fn compute_is_first_ordered<A>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
self_to_other: &mut StoredVec<I, A>,
|
||||
other_to_self: &mut StoredVec<A, I>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
I: StoredType,
|
||||
T: From<bool>,
|
||||
A: StoredIndex + StoredType,
|
||||
{
|
||||
self.validate_computed_version_or_reset_file(
|
||||
Version::ZERO + self.version() + self_to_other.version() + other_to_self.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(I::from(self.len()));
|
||||
self_to_other.iter_from(index, |(i, other, ..)| {
|
||||
self.forced_push_at(
|
||||
i,
|
||||
T::from(other_to_self.double_unwrap_cached_get(other) == i),
|
||||
exit,
|
||||
)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)
|
||||
}
|
||||
|
||||
pub fn compute_sum_from_indexes<T2>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
first_indexes: &mut StoredVec<I, T2>,
|
||||
last_indexes: &mut StoredVec<I, T2>,
|
||||
source: &mut StoredVec<T2, T>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: From<usize> + Add<T, Output = T>,
|
||||
T2: StoredIndex + StoredType,
|
||||
{
|
||||
self.validate_computed_version_or_reset_file(
|
||||
Version::ZERO + self.version() + first_indexes.version() + last_indexes.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(I::from(self.len()));
|
||||
first_indexes.iter_from(index, |(i, first_index, ..)| {
|
||||
let last_index = last_indexes.double_unwrap_cached_get(i);
|
||||
let range = first_index.unwrap_to_usize()..=last_index.unwrap_to_usize();
|
||||
let mut sum = T::from(0_usize);
|
||||
range.into_iter().for_each(|i| {
|
||||
sum = sum.clone() + source.double_unwrap_cached_get(T2::from(i));
|
||||
});
|
||||
self.forced_push_at(i, sum, exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> ComputedVec<I, Bitcoin>
|
||||
where
|
||||
I: StoredIndex,
|
||||
{
|
||||
pub fn compute_from_sats(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
sats: &mut StoredVec<I, Sats>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset_file(
|
||||
Version::ZERO + self.version() + sats.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(I::from(self.len()));
|
||||
sats.iter_from(index, |(i, sats, ..)| {
|
||||
let (i, v) = (i, Bitcoin::from(sats));
|
||||
self.forced_push_at(i, v, exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputedVec<Height, Dollars> {
|
||||
pub fn compute_from_bitcoin(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
bitcoin: &mut StoredVec<Height, Bitcoin>,
|
||||
price: &mut StoredVec<Height, Close<Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset_file(
|
||||
Version::ZERO + self.version() + bitcoin.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(Height::from(self.len()));
|
||||
bitcoin.iter_from(index, |(i, bitcoin, ..)| {
|
||||
let dollars = price.double_unwrap_cached_get(i);
|
||||
let (i, v) = (i, *dollars * bitcoin);
|
||||
self.forced_push_at(i, v, exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputedVec<Txindex, Dollars> {
|
||||
pub fn compute_from_bitcoin(
|
||||
&mut self,
|
||||
max_from: Txindex,
|
||||
bitcoin: &mut StoredVec<Txindex, Bitcoin>,
|
||||
i_to_height: &mut StoredVec<Txindex, Height>,
|
||||
price: &mut StoredVec<Height, Close<Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.validate_computed_version_or_reset_file(
|
||||
Version::ZERO + self.version() + bitcoin.version(),
|
||||
)?;
|
||||
|
||||
let index = max_from.min(Txindex::from(self.len()));
|
||||
bitcoin.iter_from(index, |(i, bitcoin, ..)| {
|
||||
let height = i_to_height.double_unwrap_cached_get(i);
|
||||
let dollars = price.double_unwrap_cached_get(height);
|
||||
let (i, v) = (i, *dollars * bitcoin);
|
||||
self.forced_push_at(i, v, exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> Clone for ComputedVec<I, T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: StoredType,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
computed_version: self.computed_version,
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use brk_core::{CheckedSub, Height, StoredU32, StoredU64, StoredUsize, Timestamp, Weight};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_parser::bitcoin;
|
||||
use brk_vec::{Compressed, Version};
|
||||
|
||||
use super::{
|
||||
Indexes,
|
||||
base::ComputedVec,
|
||||
grouped::{ComputedVecsFromHeight, StorableVecGeneatorOptions},
|
||||
indexes,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
pub height_to_interval: ComputedVec<Height, Timestamp>,
|
||||
pub indexes_to_block_interval: ComputedVecsFromHeight<Timestamp>,
|
||||
pub indexes_to_block_count: ComputedVecsFromHeight<StoredU32>,
|
||||
pub indexes_to_block_weight: ComputedVecsFromHeight<Weight>,
|
||||
pub height_to_vbytes: ComputedVec<Height, StoredU64>,
|
||||
pub indexes_to_block_vbytes: ComputedVecsFromHeight<StoredU64>,
|
||||
pub indexes_to_block_size: ComputedVecsFromHeight<StoredUsize>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(path: &Path, compressed: Compressed) -> color_eyre::Result<Self> {
|
||||
fs::create_dir_all(path)?;
|
||||
|
||||
Ok(Self {
|
||||
height_to_interval: ComputedVec::forced_import(
|
||||
&path.join("height_to_interval"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
indexes_to_block_interval: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"block_interval",
|
||||
false,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_percentiles()
|
||||
.add_minmax()
|
||||
.add_average(),
|
||||
)?,
|
||||
indexes_to_block_count: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"block_count",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default().add_sum().add_total(),
|
||||
)?,
|
||||
indexes_to_block_weight: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"block_weight",
|
||||
false,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default().add_sum().add_total(),
|
||||
)?,
|
||||
indexes_to_block_size: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"block_size",
|
||||
false,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default().add_sum().add_total(),
|
||||
)?,
|
||||
height_to_vbytes: ComputedVec::forced_import(
|
||||
&path.join("height_to_vbytes"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
indexes_to_block_vbytes: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"block_vbytes",
|
||||
false,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default().add_sum().add_total(),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexes: &mut indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.height_to_interval.compute_transform(
|
||||
starting_indexes.height,
|
||||
indexer.mut_vecs().height_to_timestamp.mut_vec(),
|
||||
|(height, timestamp, _, height_to_timestamp)| {
|
||||
let interval = height.decremented().map_or(Timestamp::ZERO, |prev_h| {
|
||||
let prev_timestamp = height_to_timestamp.double_unwrap_cached_get(prev_h);
|
||||
timestamp
|
||||
.checked_sub(prev_timestamp)
|
||||
.unwrap_or(Timestamp::ZERO)
|
||||
});
|
||||
(height, interval)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_interval.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.height_to_interval.mut_vec()),
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_count.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, indexer, _, starting_indexes, exit| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
indexer.mut_vecs().height_to_weight.mut_vec(),
|
||||
|(h, ..)| (h, StoredU32::from(1_u32)),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_weight.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(indexer.mut_vecs().height_to_weight.mut_vec()),
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_size.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(indexer.mut_vecs().height_to_total_size.mut_vec()),
|
||||
)?;
|
||||
|
||||
self.height_to_vbytes.compute_transform(
|
||||
starting_indexes.height,
|
||||
indexer.mut_vecs().height_to_weight.mut_vec(),
|
||||
|(h, w, ..)| {
|
||||
(
|
||||
h,
|
||||
StoredU64::from(bitcoin::Weight::from(w).to_vbytes_floor()),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_vbytes.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.height_to_vbytes.mut_vec()),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn as_any_vecs(&self) -> Vec<&dyn brk_vec::AnyStoredVec> {
|
||||
[
|
||||
vec![
|
||||
self.height_to_interval.any_vec(),
|
||||
self.height_to_vbytes.any_vec(),
|
||||
],
|
||||
self.indexes_to_block_interval.any_vecs(),
|
||||
self.indexes_to_block_count.any_vecs(),
|
||||
self.indexes_to_block_weight.any_vecs(),
|
||||
self.indexes_to_block_size.any_vecs(),
|
||||
self.indexes_to_block_vbytes.any_vecs(),
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
@@ -1,779 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_exit::Exit;
|
||||
use brk_vec::{
|
||||
Compressed, DynamicVec, GenericVec, Result, StoredIndex, StoredType, StoredVec, Version,
|
||||
};
|
||||
use color_eyre::eyre::ContextCompat;
|
||||
|
||||
use crate::storage::vecs::base::ComputedVec;
|
||||
|
||||
use super::ComputedType;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ComputedVecBuilder<I, T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: ComputedType,
|
||||
{
|
||||
first: Option<ComputedVec<I, T>>,
|
||||
average: Option<ComputedVec<I, T>>,
|
||||
sum: Option<ComputedVec<I, T>>,
|
||||
max: Option<ComputedVec<I, T>>,
|
||||
_90p: Option<ComputedVec<I, T>>,
|
||||
_75p: Option<ComputedVec<I, T>>,
|
||||
median: Option<ComputedVec<I, T>>,
|
||||
_25p: Option<ComputedVec<I, T>>,
|
||||
_10p: Option<ComputedVec<I, T>>,
|
||||
min: Option<ComputedVec<I, T>>,
|
||||
last: Option<ComputedVec<I, T>>,
|
||||
total: Option<ComputedVec<I, T>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<I, T> ComputedVecBuilder<I, T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: ComputedType,
|
||||
{
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
version: Version,
|
||||
compressed: Compressed,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let key = I::to_string().split("::").last().unwrap().to_lowercase();
|
||||
|
||||
let only_one_active = options.is_only_one_active();
|
||||
|
||||
let default = || path.join(format!("{key}_to_{name}"));
|
||||
|
||||
let prefix = |s: &str| path.join(format!("{key}_to_{s}_{name}"));
|
||||
|
||||
let maybe_prefix = |s: &str| {
|
||||
if only_one_active {
|
||||
default()
|
||||
} else {
|
||||
prefix(s)
|
||||
}
|
||||
};
|
||||
|
||||
let suffix = |s: &str| path.join(format!("{key}_to_{name}_{s}"));
|
||||
|
||||
let maybe_suffix = |s: &str| {
|
||||
if only_one_active {
|
||||
default()
|
||||
} else {
|
||||
suffix(s)
|
||||
}
|
||||
};
|
||||
|
||||
let version = VERSION + version;
|
||||
|
||||
let s = Self {
|
||||
first: options.first.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&maybe_prefix("first"),
|
||||
version + Version::ZERO,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
last: options.last.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&path.join(format!("{key}_to_{name}")),
|
||||
version + Version::ZERO,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
min: options.min.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&maybe_suffix("min"),
|
||||
version + Version::ZERO,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
max: options.max.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&maybe_suffix("max"),
|
||||
version + Version::ZERO,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
median: options.median.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&maybe_suffix("median"),
|
||||
version + Version::ZERO,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
average: options.average.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&maybe_suffix("average"),
|
||||
version + Version::ZERO,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
sum: options.sum.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&maybe_suffix("sum"),
|
||||
version + Version::ZERO,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
total: options.total.then(|| {
|
||||
ComputedVec::forced_import(&prefix("total"), version + Version::ZERO, compressed)
|
||||
.unwrap()
|
||||
}),
|
||||
_90p: options._90p.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&maybe_suffix("90p"),
|
||||
version + Version::ZERO,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
_75p: options._75p.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&maybe_suffix("75p"),
|
||||
version + Version::ZERO,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
_25p: options._25p.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&maybe_suffix("25p"),
|
||||
version + Version::ZERO,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
_10p: options._10p.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&maybe_suffix("10p"),
|
||||
version + Version::ZERO,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
};
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, max_from: I, source: &mut StoredVec<I, T>, exit: &Exit) -> Result<()> {
|
||||
if self.total.is_none() {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let index = self.starting_index(max_from);
|
||||
|
||||
let total_vec = self.total.as_mut().unwrap();
|
||||
|
||||
source.iter_from(index, |(i, v, ..)| {
|
||||
let prev = i
|
||||
.unwrap_to_usize()
|
||||
.checked_sub(1)
|
||||
.map_or(T::from(0_usize), |prev_i| {
|
||||
total_vec
|
||||
.unwrap_cached_get(I::from(prev_i))
|
||||
.unwrap_or(T::from(0_usize))
|
||||
});
|
||||
let value = v.clone() + prev;
|
||||
total_vec.forced_push_at(i, value, exit)?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute<I2>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
source: &mut StoredVec<I2, T>,
|
||||
first_indexes: &mut StoredVec<I, I2>,
|
||||
last_indexes: &mut StoredVec<I, I2>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
I2: StoredIndex + StoredType,
|
||||
{
|
||||
let index = self.starting_index(max_from);
|
||||
|
||||
first_indexes.iter_from(index, |(i, first_index, first_indexes)| {
|
||||
let last_index = last_indexes.double_unwrap_cached_get(i);
|
||||
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
let v = source.double_unwrap_cached_get(first_index);
|
||||
first.forced_push_at(index, v, exit)?;
|
||||
}
|
||||
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
let v = source.double_unwrap_cached_get(last_index);
|
||||
last.forced_push_at(index, v, exit)?;
|
||||
}
|
||||
|
||||
let needs_sum_or_total = self.sum.is_some() || self.total.is_some();
|
||||
let needs_average_sum_or_total = needs_sum_or_total || self.average.is_some();
|
||||
let needs_sorted = self.max.is_some()
|
||||
|| self._90p.is_some()
|
||||
|| self._75p.is_some()
|
||||
|| self.median.is_some()
|
||||
|| self._25p.is_some()
|
||||
|| self._10p.is_some()
|
||||
|| self.min.is_some();
|
||||
let needs_values = needs_sorted || needs_average_sum_or_total;
|
||||
|
||||
if needs_values {
|
||||
let mut values = source.collect_inclusive_range(first_index, last_index)?;
|
||||
|
||||
if needs_sorted {
|
||||
values.sort_unstable();
|
||||
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
max.forced_push_at(
|
||||
i,
|
||||
values
|
||||
.last()
|
||||
.context("expect some")
|
||||
.inspect_err(|_| {
|
||||
dbg!(
|
||||
&values,
|
||||
max.path(),
|
||||
first_indexes.path(),
|
||||
first_index,
|
||||
last_indexes.path(),
|
||||
last_index,
|
||||
source.len(),
|
||||
source.path()
|
||||
);
|
||||
})
|
||||
.unwrap()
|
||||
.clone(),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some(_90p) = self._90p.as_mut() {
|
||||
_90p.forced_push_at(i, Self::get_percentile(&values, 0.90), exit)?;
|
||||
}
|
||||
|
||||
if let Some(_75p) = self._75p.as_mut() {
|
||||
_75p.forced_push_at(i, Self::get_percentile(&values, 0.75), exit)?;
|
||||
}
|
||||
|
||||
if let Some(median) = self.median.as_mut() {
|
||||
median.forced_push_at(i, Self::get_percentile(&values, 0.50), exit)?;
|
||||
}
|
||||
|
||||
if let Some(_25p) = self._25p.as_mut() {
|
||||
_25p.forced_push_at(i, Self::get_percentile(&values, 0.25), exit)?;
|
||||
}
|
||||
|
||||
if let Some(_10p) = self._10p.as_mut() {
|
||||
_10p.forced_push_at(i, Self::get_percentile(&values, 0.10), exit)?;
|
||||
}
|
||||
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
min.forced_push_at(i, values.first().unwrap().clone(), exit)?;
|
||||
}
|
||||
}
|
||||
|
||||
if needs_average_sum_or_total {
|
||||
let len = values.len();
|
||||
let sum = values.into_iter().fold(T::from(0), |a, b| a + b);
|
||||
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
let avg = sum.clone() / len;
|
||||
average.forced_push_at(i, avg, exit)?;
|
||||
}
|
||||
|
||||
if needs_sum_or_total {
|
||||
if let Some(sum_vec) = self.sum.as_mut() {
|
||||
sum_vec.forced_push_at(i, sum.clone(), exit)?;
|
||||
}
|
||||
|
||||
if let Some(total_vec) = self.total.as_mut() {
|
||||
let prev = i
|
||||
.unwrap_to_usize()
|
||||
.checked_sub(1)
|
||||
.map_or(T::from(0_usize), |prev_i| {
|
||||
total_vec.double_unwrap_cached_get(I::from(prev_i))
|
||||
});
|
||||
total_vec.forced_push_at(i, prev + sum, exit)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn from_aligned<I2>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
source: &mut ComputedVecBuilder<I2, T>,
|
||||
first_indexes: &mut StoredVec<I, I2>,
|
||||
last_indexes: &mut StoredVec<I, I2>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
I2: StoredIndex + StoredType,
|
||||
{
|
||||
if self._90p.is_some()
|
||||
|| self._75p.is_some()
|
||||
|| self.median.is_some()
|
||||
|| self._25p.is_some()
|
||||
|| self._10p.is_some()
|
||||
{
|
||||
panic!("unsupported");
|
||||
}
|
||||
|
||||
let index = self.starting_index(max_from);
|
||||
|
||||
first_indexes.iter_from(index, |(i, first_index, ..)| {
|
||||
let last_index = last_indexes.double_unwrap_cached_get(i);
|
||||
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
let v = source
|
||||
.first
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.double_unwrap_cached_get(first_index);
|
||||
first.forced_push_at(index, v, exit)?;
|
||||
}
|
||||
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
let v = source
|
||||
.last
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.double_unwrap_cached_get(last_index);
|
||||
last.forced_push_at(index, v, exit)?;
|
||||
}
|
||||
|
||||
let needs_sum_or_total = self.sum.is_some() || self.total.is_some();
|
||||
let needs_average_sum_or_total = needs_sum_or_total || self.average.is_some();
|
||||
let needs_sorted = self.max.is_some() || self.min.is_some();
|
||||
let needs_values = needs_sorted || needs_average_sum_or_total;
|
||||
|
||||
if needs_values {
|
||||
if needs_sorted {
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
let mut values = source
|
||||
.max
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.collect_inclusive_range(first_index, last_index)?;
|
||||
values.sort_unstable();
|
||||
max.forced_push_at(i, values.last().unwrap().clone(), exit)?;
|
||||
}
|
||||
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
let mut values = source
|
||||
.min
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.collect_inclusive_range(first_index, last_index)?;
|
||||
values.sort_unstable();
|
||||
min.forced_push_at(i, values.first().unwrap().clone(), exit)?;
|
||||
}
|
||||
}
|
||||
|
||||
if needs_average_sum_or_total {
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
let values = source
|
||||
.average
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.collect_inclusive_range(first_index, last_index)?;
|
||||
|
||||
let len = values.len();
|
||||
let total = values.into_iter().fold(T::from(0), |a, b| a + b);
|
||||
// TODO: Multiply by count then divide by total
|
||||
// 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 = total / len;
|
||||
average.forced_push_at(i, avg, exit)?;
|
||||
}
|
||||
|
||||
if needs_sum_or_total {
|
||||
let values = source
|
||||
.sum
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.collect_inclusive_range(first_index, last_index)?;
|
||||
|
||||
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.clone(), exit)?;
|
||||
}
|
||||
|
||||
if let Some(total_vec) = self.total.as_mut() {
|
||||
let prev = i
|
||||
.unwrap_to_usize()
|
||||
.checked_sub(1)
|
||||
.map_or(T::from(0_usize), |prev_i| {
|
||||
total_vec.double_unwrap_cached_get(I::from(prev_i))
|
||||
});
|
||||
total_vec.forced_push_at(i, prev + sum, exit)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_percentile(sorted: &[T], percentile: f64) -> T {
|
||||
let len = sorted.len();
|
||||
|
||||
if len == 0 {
|
||||
panic!();
|
||||
} else if len == 1 {
|
||||
sorted[0].clone()
|
||||
} else {
|
||||
let index = (len - 1) as f64 * percentile;
|
||||
|
||||
let fract = index.fract();
|
||||
|
||||
if fract != 0.0 {
|
||||
let left = sorted.get(index as usize).unwrap().clone();
|
||||
let right = sorted.get(index.ceil() as usize).unwrap().clone();
|
||||
left / 2 + right / 2
|
||||
} else {
|
||||
sorted.get(index as usize).unwrap().clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn starting_index(&self, max_from: I) -> I {
|
||||
max_from.min(I::from(
|
||||
self.any_vecs().into_iter().map(|v| v.len()).min().unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn unwrap_first(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self.first.as_mut().unwrap()
|
||||
}
|
||||
pub fn unwrap_average(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self.average.as_mut().unwrap()
|
||||
}
|
||||
pub fn unwrap_sum(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self.sum.as_mut().unwrap()
|
||||
}
|
||||
pub fn unwrap_max(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self.max.as_mut().unwrap()
|
||||
}
|
||||
pub fn unwrap_90p(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self._90p.as_mut().unwrap()
|
||||
}
|
||||
pub fn unwrap_75p(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self._75p.as_mut().unwrap()
|
||||
}
|
||||
pub fn unwrap_median(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self.median.as_mut().unwrap()
|
||||
}
|
||||
pub fn unwrap_25p(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self._25p.as_mut().unwrap()
|
||||
}
|
||||
pub fn unwrap_10p(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self._10p.as_mut().unwrap()
|
||||
}
|
||||
pub fn unwrap_min(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self.min.as_mut().unwrap()
|
||||
}
|
||||
pub fn unwrap_last(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self.last.as_mut().unwrap()
|
||||
}
|
||||
pub fn unwrap_total(&mut self) -> &mut ComputedVec<I, T> {
|
||||
self.total.as_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn any_vecs(&self) -> Vec<&dyn brk_vec::AnyStoredVec> {
|
||||
let mut v: Vec<&dyn brk_vec::AnyStoredVec> = vec![];
|
||||
|
||||
if let Some(first) = self.first.as_ref() {
|
||||
v.push(first.any_vec());
|
||||
}
|
||||
if let Some(last) = self.last.as_ref() {
|
||||
v.push(last.any_vec());
|
||||
}
|
||||
if let Some(min) = self.min.as_ref() {
|
||||
v.push(min.any_vec());
|
||||
}
|
||||
if let Some(max) = self.max.as_ref() {
|
||||
v.push(max.any_vec());
|
||||
}
|
||||
if let Some(median) = self.median.as_ref() {
|
||||
v.push(median.any_vec());
|
||||
}
|
||||
if let Some(average) = self.average.as_ref() {
|
||||
v.push(average.any_vec());
|
||||
}
|
||||
if let Some(sum) = self.sum.as_ref() {
|
||||
v.push(sum.any_vec());
|
||||
}
|
||||
if let Some(total) = self.total.as_ref() {
|
||||
v.push(total.any_vec());
|
||||
}
|
||||
if let Some(_90p) = self._90p.as_ref() {
|
||||
v.push(_90p.any_vec());
|
||||
}
|
||||
if let Some(_75p) = self._75p.as_ref() {
|
||||
v.push(_75p.any_vec());
|
||||
}
|
||||
if let Some(_25p) = self._25p.as_ref() {
|
||||
v.push(_25p.any_vec());
|
||||
}
|
||||
if let Some(_10p) = self._10p.as_ref() {
|
||||
v.push(_10p.any_vec());
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
pub fn safe_flush(&mut self, exit: &Exit) -> Result<()> {
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
first.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
last.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
min.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
max.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(median) = self.median.as_mut() {
|
||||
median.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
average.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(sum) = self.sum.as_mut() {
|
||||
sum.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(total) = self.total.as_mut() {
|
||||
total.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(_90p) = self._90p.as_mut() {
|
||||
_90p.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(_75p) = self._75p.as_mut() {
|
||||
_75p.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(_25p) = self._25p.as_mut() {
|
||||
_25p.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(_10p) = self._10p.as_mut() {
|
||||
_10p.safe_flush(exit)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct StorableVecGeneatorOptions {
|
||||
average: bool,
|
||||
sum: bool,
|
||||
max: bool,
|
||||
_90p: bool,
|
||||
_75p: bool,
|
||||
median: bool,
|
||||
_25p: bool,
|
||||
_10p: bool,
|
||||
min: bool,
|
||||
first: bool,
|
||||
last: bool,
|
||||
total: bool,
|
||||
}
|
||||
|
||||
impl StorableVecGeneatorOptions {
|
||||
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_median(mut self) -> Self {
|
||||
self.median = 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_90p(mut self) -> Self {
|
||||
self._90p = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_75p(mut self) -> Self {
|
||||
self._75p = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_25p(mut self) -> Self {
|
||||
self._25p = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_10p(mut self) -> Self {
|
||||
self._10p = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_total(mut self) -> Self {
|
||||
self.total = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rm_min(mut self) -> Self {
|
||||
self.min = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rm_max(mut self) -> Self {
|
||||
self.max = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rm_median(mut self) -> Self {
|
||||
self.median = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rm_average(mut self) -> Self {
|
||||
self.average = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rm_sum(mut self) -> Self {
|
||||
self.sum = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rm_90p(mut self) -> Self {
|
||||
self._90p = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rm_75p(mut self) -> Self {
|
||||
self._75p = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rm_25p(mut self) -> Self {
|
||||
self._25p = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rm_10p(mut self) -> Self {
|
||||
self._10p = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rm_total(mut self) -> Self {
|
||||
self.total = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_minmax(mut self) -> Self {
|
||||
self.min = true;
|
||||
self.max = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_percentiles(mut self) -> Self {
|
||||
self._90p = true;
|
||||
self._75p = true;
|
||||
self.median = true;
|
||||
self._25p = true;
|
||||
self._10p = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn remove_percentiles(mut self) -> Self {
|
||||
self._90p = false;
|
||||
self._75p = false;
|
||||
self.median = false;
|
||||
self._25p = false;
|
||||
self._10p = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_only_one_active(&self) -> bool {
|
||||
[
|
||||
self.average,
|
||||
self.sum,
|
||||
self.max,
|
||||
self._90p,
|
||||
self._75p,
|
||||
self.median,
|
||||
self._25p,
|
||||
self._10p,
|
||||
self.min,
|
||||
self.first,
|
||||
self.last,
|
||||
self.total,
|
||||
]
|
||||
.iter()
|
||||
.filter(|b| **b)
|
||||
.count()
|
||||
== 1
|
||||
}
|
||||
|
||||
pub fn copy_self_extra(&self) -> Self {
|
||||
Self {
|
||||
total: self.total,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Dateindex, Decadeindex, Monthindex, Quarterindex, Weekindex, Yearindex};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyStoredVec, Compressed, Result, Version};
|
||||
|
||||
use crate::storage::vecs::{Indexes, base::ComputedVec, indexes};
|
||||
|
||||
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecsFromDateindex<T>
|
||||
where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub dateindex: ComputedVec<Dateindex, T>,
|
||||
pub dateindex_extra: ComputedVecBuilder<Dateindex, T>,
|
||||
pub weekindex: ComputedVecBuilder<Weekindex, T>,
|
||||
pub monthindex: ComputedVecBuilder<Monthindex, T>,
|
||||
pub quarterindex: ComputedVecBuilder<Quarterindex, T>,
|
||||
pub yearindex: ComputedVecBuilder<Yearindex, T>,
|
||||
pub decadeindex: ComputedVecBuilder<Decadeindex, T>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedVecsFromDateindex<T>
|
||||
where
|
||||
T: ComputedType,
|
||||
{
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
version: Version,
|
||||
compressed: Compressed,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let version = VERSION + version;
|
||||
|
||||
let dateindex_extra = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version,
|
||||
compressed,
|
||||
options.copy_self_extra(),
|
||||
)?;
|
||||
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
Ok(Self {
|
||||
dateindex: ComputedVec::forced_import(
|
||||
&path.join(format!("dateindex_to_{name}")),
|
||||
version,
|
||||
compressed,
|
||||
)?,
|
||||
dateindex_extra,
|
||||
weekindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute<F>(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexes: &mut indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut ComputedVec<Dateindex, T>,
|
||||
&mut Indexer,
|
||||
&mut indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
&mut self.dateindex,
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.dateindex_extra
|
||||
.extend(starting_indexes.dateindex, self.dateindex.mut_vec(), exit)?;
|
||||
|
||||
self.weekindex.compute(
|
||||
starting_indexes.weekindex,
|
||||
self.dateindex.mut_vec(),
|
||||
indexes.weekindex_to_first_dateindex.mut_vec(),
|
||||
indexes.weekindex_to_last_dateindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.compute(
|
||||
starting_indexes.monthindex,
|
||||
self.dateindex.mut_vec(),
|
||||
indexes.monthindex_to_first_dateindex.mut_vec(),
|
||||
indexes.monthindex_to_last_dateindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex.from_aligned(
|
||||
starting_indexes.quarterindex,
|
||||
&mut self.monthindex,
|
||||
indexes.quarterindex_to_first_monthindex.mut_vec(),
|
||||
indexes.quarterindex_to_last_monthindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.from_aligned(
|
||||
starting_indexes.yearindex,
|
||||
&mut self.monthindex,
|
||||
indexes.yearindex_to_first_monthindex.mut_vec(),
|
||||
indexes.yearindex_to_last_monthindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.from_aligned(
|
||||
starting_indexes.decadeindex,
|
||||
&mut self.yearindex,
|
||||
indexes.decadeindex_to_first_yearindex.mut_vec(),
|
||||
indexes.decadeindex_to_last_yearindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn any_vecs(&self) -> Vec<&dyn AnyStoredVec> {
|
||||
[
|
||||
vec![self.dateindex.any_vec()],
|
||||
self.dateindex_extra.any_vecs(),
|
||||
self.weekindex.any_vecs(),
|
||||
self.monthindex.any_vecs(),
|
||||
self.quarterindex.any_vecs(),
|
||||
self.yearindex.any_vecs(),
|
||||
self.decadeindex.any_vecs(),
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{
|
||||
Dateindex, Decadeindex, Difficultyepoch, Height, Monthindex, Quarterindex, Weekindex, Yearindex,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyStoredVec, Compressed, Result, StoredVec, Version};
|
||||
|
||||
use crate::storage::vecs::{Indexes, base::ComputedVec, indexes};
|
||||
|
||||
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecsFromHeight<T>
|
||||
where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub height: Option<ComputedVec<Height, T>>,
|
||||
pub height_extra: ComputedVecBuilder<Height, T>,
|
||||
pub dateindex: ComputedVecBuilder<Dateindex, T>,
|
||||
pub weekindex: ComputedVecBuilder<Weekindex, T>,
|
||||
pub difficultyepoch: ComputedVecBuilder<Difficultyepoch, T>,
|
||||
pub monthindex: ComputedVecBuilder<Monthindex, T>,
|
||||
pub quarterindex: ComputedVecBuilder<Quarterindex, T>,
|
||||
pub yearindex: ComputedVecBuilder<Yearindex, T>,
|
||||
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
|
||||
pub decadeindex: ComputedVecBuilder<Decadeindex, T>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedVecsFromHeight<T>
|
||||
where
|
||||
T: ComputedType + Ord + From<f64>,
|
||||
f64: From<T>,
|
||||
{
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
version: Version,
|
||||
compressed: Compressed,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let version = VERSION + version;
|
||||
|
||||
let height = compute_source.then(|| {
|
||||
ComputedVec::forced_import(&path.join(format!("height_to_{name}")), version, compressed)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
let height_extra = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version,
|
||||
compressed,
|
||||
options.copy_self_extra(),
|
||||
)?;
|
||||
|
||||
let dateindex =
|
||||
ComputedVecBuilder::forced_import(path, name, version, compressed, options)?;
|
||||
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
Ok(Self {
|
||||
height,
|
||||
height_extra,
|
||||
dateindex,
|
||||
weekindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
|
||||
difficultyepoch: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(path, name, version, compressed, options)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all<F>(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexes: &mut indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut ComputedVec<Height, T>,
|
||||
&mut Indexer,
|
||||
&mut indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
self.height.as_mut().unwrap(),
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.compute_rest(indexes, starting_indexes, exit, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &mut indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
height: Option<&mut StoredVec<Height, T>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
let height = height.unwrap_or_else(|| self.height.as_mut().unwrap().mut_vec());
|
||||
|
||||
self.height_extra
|
||||
.extend(starting_indexes.height, height, exit)?;
|
||||
|
||||
self.dateindex.compute(
|
||||
starting_indexes.dateindex,
|
||||
height,
|
||||
indexes.dateindex_to_first_height.mut_vec(),
|
||||
indexes.dateindex_to_last_height.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.weekindex.from_aligned(
|
||||
starting_indexes.weekindex,
|
||||
&mut self.dateindex,
|
||||
indexes.weekindex_to_first_dateindex.mut_vec(),
|
||||
indexes.weekindex_to_last_dateindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.from_aligned(
|
||||
starting_indexes.monthindex,
|
||||
&mut self.dateindex,
|
||||
indexes.monthindex_to_first_dateindex.mut_vec(),
|
||||
indexes.monthindex_to_last_dateindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex.from_aligned(
|
||||
starting_indexes.quarterindex,
|
||||
&mut self.monthindex,
|
||||
indexes.quarterindex_to_first_monthindex.mut_vec(),
|
||||
indexes.quarterindex_to_last_monthindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.from_aligned(
|
||||
starting_indexes.yearindex,
|
||||
&mut self.monthindex,
|
||||
indexes.yearindex_to_first_monthindex.mut_vec(),
|
||||
indexes.yearindex_to_last_monthindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.from_aligned(
|
||||
starting_indexes.decadeindex,
|
||||
&mut self.yearindex,
|
||||
indexes.decadeindex_to_first_yearindex.mut_vec(),
|
||||
indexes.decadeindex_to_last_yearindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.difficultyepoch.compute(
|
||||
starting_indexes.difficultyepoch,
|
||||
height,
|
||||
indexes.difficultyepoch_to_first_height.mut_vec(),
|
||||
indexes.difficultyepoch_to_last_height.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn any_vecs(&self) -> Vec<&dyn AnyStoredVec> {
|
||||
[
|
||||
self.height.as_ref().map_or(vec![], |v| vec![v.any_vec()]),
|
||||
self.height_extra.any_vecs(),
|
||||
self.dateindex.any_vecs(),
|
||||
self.weekindex.any_vecs(),
|
||||
self.difficultyepoch.any_vecs(),
|
||||
self.monthindex.any_vecs(),
|
||||
self.quarterindex.any_vecs(),
|
||||
self.yearindex.any_vecs(),
|
||||
// self.halvingepoch.as_any_vecs(),
|
||||
self.decadeindex.any_vecs(),
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{
|
||||
Dateindex, Decadeindex, Difficultyepoch, Height, Monthindex, Quarterindex, Txindex, Weekindex,
|
||||
Yearindex,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyStoredVec, Compressed, Result, StoredVec, Version};
|
||||
|
||||
use crate::storage::vecs::{Indexes, base::ComputedVec, indexes};
|
||||
|
||||
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecsFromTxindex<T>
|
||||
where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub txindex: Option<ComputedVec<Txindex, T>>,
|
||||
pub height: ComputedVecBuilder<Height, T>,
|
||||
pub dateindex: ComputedVecBuilder<Dateindex, T>,
|
||||
pub weekindex: ComputedVecBuilder<Weekindex, T>,
|
||||
pub difficultyepoch: ComputedVecBuilder<Difficultyepoch, T>,
|
||||
pub monthindex: ComputedVecBuilder<Monthindex, T>,
|
||||
pub quarterindex: ComputedVecBuilder<Quarterindex, T>,
|
||||
pub yearindex: ComputedVecBuilder<Yearindex, T>,
|
||||
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
|
||||
pub decadeindex: ComputedVecBuilder<Decadeindex, T>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedVecsFromTxindex<T>
|
||||
where
|
||||
T: ComputedType + Ord + From<f64>,
|
||||
f64: From<T>,
|
||||
{
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
version: Version,
|
||||
compressed: Compressed,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let version = VERSION + version;
|
||||
|
||||
let txindex = compute_source.then(|| {
|
||||
ComputedVec::forced_import(
|
||||
&path.join(format!("txindex_to_{name}")),
|
||||
version,
|
||||
compressed,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
let height = ComputedVecBuilder::forced_import(path, name, version, compressed, options)?;
|
||||
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
Ok(Self {
|
||||
txindex,
|
||||
height,
|
||||
dateindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
|
||||
weekindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
|
||||
difficultyepoch: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(path, name, version, compressed, options)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all<F>(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexes: &mut indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut ComputedVec<Txindex, T>,
|
||||
&mut Indexer,
|
||||
&mut indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
self.txindex.as_mut().unwrap(),
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.compute_rest(indexer, indexes, starting_indexes, exit, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexes: &mut indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
txindex: Option<&mut StoredVec<Txindex, T>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
let txindex = txindex.unwrap_or_else(|| self.txindex.as_mut().unwrap().mut_vec());
|
||||
|
||||
self.height.compute(
|
||||
starting_indexes.height,
|
||||
txindex,
|
||||
indexer.mut_vecs().height_to_first_txindex.mut_vec(),
|
||||
indexes.height_to_last_txindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.dateindex.from_aligned(
|
||||
starting_indexes.dateindex,
|
||||
&mut self.height,
|
||||
indexes.dateindex_to_first_height.mut_vec(),
|
||||
indexes.dateindex_to_last_height.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.weekindex.from_aligned(
|
||||
starting_indexes.weekindex,
|
||||
&mut self.dateindex,
|
||||
indexes.weekindex_to_first_dateindex.mut_vec(),
|
||||
indexes.weekindex_to_last_dateindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.from_aligned(
|
||||
starting_indexes.monthindex,
|
||||
&mut self.dateindex,
|
||||
indexes.monthindex_to_first_dateindex.mut_vec(),
|
||||
indexes.monthindex_to_last_dateindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex.from_aligned(
|
||||
starting_indexes.quarterindex,
|
||||
&mut self.monthindex,
|
||||
indexes.quarterindex_to_first_monthindex.mut_vec(),
|
||||
indexes.quarterindex_to_last_monthindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.from_aligned(
|
||||
starting_indexes.yearindex,
|
||||
&mut self.monthindex,
|
||||
indexes.yearindex_to_first_monthindex.mut_vec(),
|
||||
indexes.yearindex_to_last_monthindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.from_aligned(
|
||||
starting_indexes.decadeindex,
|
||||
&mut self.yearindex,
|
||||
indexes.decadeindex_to_first_yearindex.mut_vec(),
|
||||
indexes.decadeindex_to_last_yearindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.difficultyepoch.from_aligned(
|
||||
starting_indexes.difficultyepoch,
|
||||
&mut self.height,
|
||||
indexes.difficultyepoch_to_first_height.mut_vec(),
|
||||
indexes.difficultyepoch_to_last_height.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn any_vecs(&self) -> Vec<&dyn AnyStoredVec> {
|
||||
[
|
||||
self.txindex.as_ref().map_or(vec![], |v| vec![v.any_vec()]),
|
||||
self.height.any_vecs(),
|
||||
self.dateindex.any_vecs(),
|
||||
self.weekindex.any_vecs(),
|
||||
self.difficultyepoch.any_vecs(),
|
||||
self.monthindex.any_vecs(),
|
||||
self.quarterindex.any_vecs(),
|
||||
self.yearindex.any_vecs(),
|
||||
// self.halvingepoch.as_any_vecs(),
|
||||
self.decadeindex.any_vecs(),
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Bitcoin, Dollars, Height, Sats};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyStoredVec, Compressed, Result, StoredVec, Version};
|
||||
|
||||
use crate::storage::{
|
||||
base::ComputedVec,
|
||||
marketprice,
|
||||
vecs::{Indexes, indexes},
|
||||
};
|
||||
|
||||
use super::{ComputedVecsFromHeight, StorableVecGeneatorOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedValueVecsFromHeight {
|
||||
pub sats: ComputedVecsFromHeight<Sats>,
|
||||
pub bitcoin: ComputedVecsFromHeight<Bitcoin>,
|
||||
pub dollars: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE;
|
||||
|
||||
impl ComputedValueVecsFromHeight {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
version: Version,
|
||||
compressed: Compressed,
|
||||
options: StorableVecGeneatorOptions,
|
||||
compute_dollars: bool,
|
||||
) -> color_eyre::Result<Self> {
|
||||
Ok(Self {
|
||||
sats: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
name,
|
||||
compute_source,
|
||||
VERSION + version,
|
||||
compressed,
|
||||
options,
|
||||
)?,
|
||||
bitcoin: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
&format!("{name}_in_btc"),
|
||||
true,
|
||||
VERSION + version,
|
||||
compressed,
|
||||
options,
|
||||
)?,
|
||||
dollars: compute_dollars.then(|| {
|
||||
ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
&format!("{name}_in_usd"),
|
||||
true,
|
||||
VERSION + version,
|
||||
compressed,
|
||||
options,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all<F>(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexes: &mut indexes::Vecs,
|
||||
marketprices: &mut Option<&mut marketprice::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut ComputedVec<Height, Sats>,
|
||||
&mut Indexer,
|
||||
&mut indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
self.sats.height.as_mut().unwrap(),
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.compute_rest(indexer, indexes, marketprices, starting_indexes, exit, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexes: &mut indexes::Vecs,
|
||||
marketprices: &mut Option<&mut marketprice::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut height: Option<&mut StoredVec<Height, Sats>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
if let Some(height) = height.as_mut() {
|
||||
self.sats
|
||||
.compute_rest(indexes, starting_indexes, exit, Some(height))?;
|
||||
} else {
|
||||
self.sats
|
||||
.compute_rest(indexes, starting_indexes, exit, None)?;
|
||||
}
|
||||
|
||||
let height = height.unwrap_or_else(|| self.sats.height.as_mut().unwrap().mut_vec());
|
||||
|
||||
self.bitcoin.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, _, _, starting_indexes, exit| {
|
||||
v.compute_from_sats(starting_indexes.height, height, exit)
|
||||
},
|
||||
)?;
|
||||
|
||||
let txindex = self.bitcoin.height.as_mut().unwrap().mut_vec();
|
||||
let price = marketprices
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.chainindexes_to_close
|
||||
.height
|
||||
.mut_vec();
|
||||
|
||||
if let Some(dollars) = self.dollars.as_mut() {
|
||||
dollars.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, _, _, starting_indexes, exit| {
|
||||
v.compute_from_bitcoin(starting_indexes.height, txindex, price, exit)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn any_vecs(&self) -> Vec<&dyn AnyStoredVec> {
|
||||
[
|
||||
self.sats.any_vecs(),
|
||||
self.bitcoin.any_vecs(),
|
||||
self.dollars.as_ref().map_or(vec![], |v| v.any_vecs()),
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Bitcoin, Dollars, Sats, Txindex};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyStoredVec, Compressed, Result, StoredVec, Version};
|
||||
|
||||
use crate::storage::{
|
||||
base::ComputedVec,
|
||||
marketprice,
|
||||
vecs::{Indexes, indexes},
|
||||
};
|
||||
|
||||
use super::{ComputedVecsFromTxindex, StorableVecGeneatorOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedValueVecsFromTxindex {
|
||||
pub sats: ComputedVecsFromTxindex<Sats>,
|
||||
pub bitcoin: ComputedVecsFromTxindex<Bitcoin>,
|
||||
pub dollars: Option<ComputedVecsFromTxindex<Dollars>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE;
|
||||
|
||||
impl ComputedValueVecsFromTxindex {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
version: Version,
|
||||
compressed: Compressed,
|
||||
options: StorableVecGeneatorOptions,
|
||||
compute_dollars: bool,
|
||||
) -> color_eyre::Result<Self> {
|
||||
Ok(Self {
|
||||
sats: ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
name,
|
||||
compute_source,
|
||||
VERSION + version,
|
||||
compressed,
|
||||
options,
|
||||
)?,
|
||||
bitcoin: ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
&format!("{name}_in_btc"),
|
||||
true,
|
||||
VERSION + version,
|
||||
compressed,
|
||||
options,
|
||||
)?,
|
||||
dollars: compute_dollars.then(|| {
|
||||
ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
&format!("{name}_in_usd"),
|
||||
true,
|
||||
VERSION + version,
|
||||
compressed,
|
||||
options,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all<F>(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexes: &mut indexes::Vecs,
|
||||
marketprices: &mut Option<&mut marketprice::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut ComputedVec<Txindex, Sats>,
|
||||
&mut Indexer,
|
||||
&mut indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
self.sats.txindex.as_mut().unwrap(),
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.compute_rest(indexer, indexes, marketprices, starting_indexes, exit, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexes: &mut indexes::Vecs,
|
||||
marketprices: &mut Option<&mut marketprice::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut txindex: Option<&mut StoredVec<Txindex, Sats>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
if let Some(txindex) = txindex.as_mut() {
|
||||
self.sats
|
||||
.compute_rest(indexer, indexes, starting_indexes, exit, Some(txindex))?;
|
||||
} else {
|
||||
self.sats
|
||||
.compute_rest(indexer, indexes, starting_indexes, exit, None)?;
|
||||
}
|
||||
|
||||
let txindex = txindex.unwrap_or_else(|| self.sats.txindex.as_mut().unwrap().mut_vec());
|
||||
|
||||
self.bitcoin.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, _, _, starting_indexes, exit| {
|
||||
v.compute_from_sats(starting_indexes.txindex, txindex, exit)
|
||||
},
|
||||
)?;
|
||||
|
||||
let txindex = self.bitcoin.txindex.as_mut().unwrap().mut_vec();
|
||||
let price = marketprices
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.chainindexes_to_close
|
||||
.height
|
||||
.mut_vec();
|
||||
|
||||
if let Some(dollars) = self.dollars.as_mut() {
|
||||
dollars.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, indexer, _, starting_indexes, exit| {
|
||||
v.compute_from_bitcoin(
|
||||
starting_indexes.txindex,
|
||||
txindex,
|
||||
indexer.mut_vecs().txindex_to_height.mut_vec(),
|
||||
price,
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn any_vecs(&self) -> Vec<&dyn AnyStoredVec> {
|
||||
[
|
||||
self.sats.any_vecs(),
|
||||
self.bitcoin.any_vecs(),
|
||||
self.dollars.as_ref().map_or(vec![], |v| v.any_vecs()),
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
@@ -1,845 +0,0 @@
|
||||
use std::{fs, ops::Deref, path::Path};
|
||||
|
||||
use brk_core::{
|
||||
Date, Dateindex, Decadeindex, Difficultyepoch, Halvingepoch, Height, Monthindex, Quarterindex,
|
||||
Timestamp, Txindex, Txinindex, Txoutindex, Weekindex, Yearindex,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{Compressed, Version};
|
||||
|
||||
use super::ComputedVec;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
pub dateindex_to_date: ComputedVec<Dateindex, Date>,
|
||||
pub dateindex_to_dateindex: ComputedVec<Dateindex, Dateindex>,
|
||||
pub dateindex_to_first_height: ComputedVec<Dateindex, Height>,
|
||||
pub dateindex_to_last_height: ComputedVec<Dateindex, Height>,
|
||||
pub dateindex_to_monthindex: ComputedVec<Dateindex, Monthindex>,
|
||||
pub dateindex_to_timestamp: ComputedVec<Dateindex, Timestamp>,
|
||||
pub dateindex_to_weekindex: ComputedVec<Dateindex, Weekindex>,
|
||||
pub decadeindex_to_decadeindex: ComputedVec<Decadeindex, Decadeindex>,
|
||||
pub decadeindex_to_first_yearindex: ComputedVec<Decadeindex, Yearindex>,
|
||||
pub decadeindex_to_last_yearindex: ComputedVec<Decadeindex, Yearindex>,
|
||||
pub decadeindex_to_timestamp: ComputedVec<Decadeindex, Timestamp>,
|
||||
pub difficultyepoch_to_difficultyepoch: ComputedVec<Difficultyepoch, Difficultyepoch>,
|
||||
pub difficultyepoch_to_first_height: ComputedVec<Difficultyepoch, Height>,
|
||||
pub difficultyepoch_to_last_height: ComputedVec<Difficultyepoch, Height>,
|
||||
pub difficultyepoch_to_timestamp: ComputedVec<Difficultyepoch, Timestamp>,
|
||||
pub halvingepoch_to_first_height: ComputedVec<Halvingepoch, Height>,
|
||||
pub halvingepoch_to_halvingepoch: ComputedVec<Halvingepoch, Halvingepoch>,
|
||||
pub halvingepoch_to_last_height: ComputedVec<Halvingepoch, Height>,
|
||||
pub halvingepoch_to_timestamp: ComputedVec<Halvingepoch, Timestamp>,
|
||||
pub height_to_dateindex: ComputedVec<Height, Dateindex>,
|
||||
pub height_to_difficultyepoch: ComputedVec<Height, Difficultyepoch>,
|
||||
pub height_to_fixed_date: ComputedVec<Height, Date>,
|
||||
pub height_to_fixed_timestamp: ComputedVec<Height, Timestamp>,
|
||||
pub height_to_halvingepoch: ComputedVec<Height, Halvingepoch>,
|
||||
pub height_to_height: ComputedVec<Height, Height>,
|
||||
pub height_to_last_txindex: ComputedVec<Height, Txindex>,
|
||||
pub height_to_real_date: ComputedVec<Height, Date>,
|
||||
pub monthindex_to_first_dateindex: ComputedVec<Monthindex, Dateindex>,
|
||||
pub monthindex_to_last_dateindex: ComputedVec<Monthindex, Dateindex>,
|
||||
pub monthindex_to_monthindex: ComputedVec<Monthindex, Monthindex>,
|
||||
pub monthindex_to_quarterindex: ComputedVec<Monthindex, Quarterindex>,
|
||||
pub monthindex_to_timestamp: ComputedVec<Monthindex, Timestamp>,
|
||||
pub monthindex_to_yearindex: ComputedVec<Monthindex, Yearindex>,
|
||||
pub quarterindex_to_first_monthindex: ComputedVec<Quarterindex, Monthindex>,
|
||||
pub quarterindex_to_last_monthindex: ComputedVec<Quarterindex, Monthindex>,
|
||||
pub quarterindex_to_quarterindex: ComputedVec<Quarterindex, Quarterindex>,
|
||||
pub quarterindex_to_timestamp: ComputedVec<Quarterindex, Timestamp>,
|
||||
pub txindex_to_last_txinindex: ComputedVec<Txindex, Txinindex>,
|
||||
pub txindex_to_last_txoutindex: ComputedVec<Txindex, Txoutindex>,
|
||||
pub weekindex_to_first_dateindex: ComputedVec<Weekindex, Dateindex>,
|
||||
pub weekindex_to_last_dateindex: ComputedVec<Weekindex, Dateindex>,
|
||||
pub weekindex_to_timestamp: ComputedVec<Weekindex, Timestamp>,
|
||||
pub weekindex_to_weekindex: ComputedVec<Weekindex, Weekindex>,
|
||||
pub yearindex_to_decadeindex: ComputedVec<Yearindex, Decadeindex>,
|
||||
pub yearindex_to_first_monthindex: ComputedVec<Yearindex, Monthindex>,
|
||||
pub yearindex_to_last_monthindex: ComputedVec<Yearindex, Monthindex>,
|
||||
pub yearindex_to_timestamp: ComputedVec<Yearindex, Timestamp>,
|
||||
pub yearindex_to_yearindex: ComputedVec<Yearindex, Yearindex>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(path: &Path, compressed: Compressed) -> color_eyre::Result<Self> {
|
||||
fs::create_dir_all(path)?;
|
||||
|
||||
Ok(Self {
|
||||
dateindex_to_date: ComputedVec::forced_import(
|
||||
&path.join("dateindex_to_date"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
dateindex_to_dateindex: ComputedVec::forced_import(
|
||||
&path.join("dateindex_to_dateindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
dateindex_to_first_height: ComputedVec::forced_import(
|
||||
&path.join("dateindex_to_first_height"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
dateindex_to_last_height: ComputedVec::forced_import(
|
||||
&path.join("dateindex_to_last_height"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
height_to_real_date: ComputedVec::forced_import(
|
||||
&path.join("height_to_real_date"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
height_to_fixed_date: ComputedVec::forced_import(
|
||||
&path.join("height_to_fixed_date"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
height_to_dateindex: ComputedVec::forced_import(
|
||||
&path.join("height_to_dateindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
height_to_height: ComputedVec::forced_import(
|
||||
&path.join("height_to_height"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
height_to_last_txindex: ComputedVec::forced_import(
|
||||
&path.join("height_to_last_txindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
txindex_to_last_txinindex: ComputedVec::forced_import(
|
||||
&path.join("txindex_to_last_txinindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
txindex_to_last_txoutindex: ComputedVec::forced_import(
|
||||
&path.join("txindex_to_last_txoutindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
difficultyepoch_to_first_height: ComputedVec::forced_import(
|
||||
&path.join("difficultyepoch_to_first_height"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
difficultyepoch_to_last_height: ComputedVec::forced_import(
|
||||
&path.join("difficultyepoch_to_last_height"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
halvingepoch_to_first_height: ComputedVec::forced_import(
|
||||
&path.join("halvingepoch_to_first_height"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
halvingepoch_to_last_height: ComputedVec::forced_import(
|
||||
&path.join("halvingepoch_to_last_height"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
weekindex_to_first_dateindex: ComputedVec::forced_import(
|
||||
&path.join("weekindex_to_first_dateindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
weekindex_to_last_dateindex: ComputedVec::forced_import(
|
||||
&path.join("weekindex_to_last_dateindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
monthindex_to_first_dateindex: ComputedVec::forced_import(
|
||||
&path.join("monthindex_to_first_dateindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
monthindex_to_last_dateindex: ComputedVec::forced_import(
|
||||
&path.join("monthindex_to_last_dateindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
yearindex_to_first_monthindex: ComputedVec::forced_import(
|
||||
&path.join("yearindex_to_first_monthindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
yearindex_to_last_monthindex: ComputedVec::forced_import(
|
||||
&path.join("yearindex_to_last_monthindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
decadeindex_to_first_yearindex: ComputedVec::forced_import(
|
||||
&path.join("decadeindex_to_first_yearindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
decadeindex_to_last_yearindex: ComputedVec::forced_import(
|
||||
&path.join("decadeindex_to_last_yearindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
dateindex_to_weekindex: ComputedVec::forced_import(
|
||||
&path.join("dateindex_to_weekindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
dateindex_to_monthindex: ComputedVec::forced_import(
|
||||
&path.join("dateindex_to_monthindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
monthindex_to_yearindex: ComputedVec::forced_import(
|
||||
&path.join("monthindex_to_yearindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
yearindex_to_decadeindex: ComputedVec::forced_import(
|
||||
&path.join("yearindex_to_decadeindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
height_to_difficultyepoch: ComputedVec::forced_import(
|
||||
&path.join("height_to_difficultyepoch"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
height_to_halvingepoch: ComputedVec::forced_import(
|
||||
&path.join("height_to_halvingepoch"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
weekindex_to_weekindex: ComputedVec::forced_import(
|
||||
&path.join("weekindex_to_weekindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
monthindex_to_monthindex: ComputedVec::forced_import(
|
||||
&path.join("monthindex_to_monthindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
yearindex_to_yearindex: ComputedVec::forced_import(
|
||||
&path.join("yearindex_to_yearindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
decadeindex_to_decadeindex: ComputedVec::forced_import(
|
||||
&path.join("decadeindex_to_decadeindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
difficultyepoch_to_difficultyepoch: ComputedVec::forced_import(
|
||||
&path.join("difficultyepoch_to_difficultyepoch"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
halvingepoch_to_halvingepoch: ComputedVec::forced_import(
|
||||
&path.join("halvingepoch_to_halvingepoch"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
dateindex_to_timestamp: ComputedVec::forced_import(
|
||||
&path.join("dateindex_to_timestamp"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
decadeindex_to_timestamp: ComputedVec::forced_import(
|
||||
&path.join("decadeindex_to_timestamp"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
difficultyepoch_to_timestamp: ComputedVec::forced_import(
|
||||
&path.join("difficultyepoch_to_timestamp"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
halvingepoch_to_timestamp: ComputedVec::forced_import(
|
||||
&path.join("halvingepoch_to_timestamp"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
monthindex_to_timestamp: ComputedVec::forced_import(
|
||||
&path.join("monthindex_to_timestamp"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
weekindex_to_timestamp: ComputedVec::forced_import(
|
||||
&path.join("weekindex_to_timestamp"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
yearindex_to_timestamp: ComputedVec::forced_import(
|
||||
&path.join("yearindex_to_timestamp"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
height_to_fixed_timestamp: ComputedVec::forced_import(
|
||||
&path.join("height_to_fixed_timestamp"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
monthindex_to_quarterindex: ComputedVec::forced_import(
|
||||
&path.join("monthindex_to_quarterindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
quarterindex_to_first_monthindex: ComputedVec::forced_import(
|
||||
&path.join("quarterindex_to_first_monthindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
quarterindex_to_last_monthindex: ComputedVec::forced_import(
|
||||
&path.join("quarterindex_to_last_monthindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
quarterindex_to_quarterindex: ComputedVec::forced_import(
|
||||
&path.join("quarterindex_to_quarterindex"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
quarterindex_to_timestamp: ComputedVec::forced_import(
|
||||
&path.join("quarterindex_to_timestamp"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
starting_indexes: brk_indexer::Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<Indexes> {
|
||||
let indexer_vecs = indexer.mut_vecs();
|
||||
|
||||
let height_count = indexer_vecs.height_to_total_size.len();
|
||||
let txindexes_count = indexer_vecs.txindex_to_txid.len();
|
||||
let txinindexes_count = indexer_vecs.txinindex_to_txoutindex.len();
|
||||
let txoutindexes_count = indexer_vecs.txoutindex_to_addressindex.len();
|
||||
|
||||
self.height_to_height.compute_transform(
|
||||
starting_indexes.height,
|
||||
indexer_vecs.height_to_timestamp.mut_vec(),
|
||||
|(h, ..)| (h, h),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.height_to_real_date.compute_transform(
|
||||
starting_indexes.height,
|
||||
indexer_vecs.height_to_timestamp.mut_vec(),
|
||||
|(h, t, ..)| (h, Date::from(t)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.height_to_fixed_timestamp.compute_transform(
|
||||
starting_indexes.height,
|
||||
indexer_vecs.height_to_timestamp.mut_vec(),
|
||||
|(h, d, s, ..)| {
|
||||
let d = h
|
||||
.decremented()
|
||||
.and_then(|h| s.unwrap_cached_get(h))
|
||||
.map_or(d, |prev_d| prev_d.max(d));
|
||||
(h, d)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.height_to_fixed_date.compute_transform(
|
||||
starting_indexes.height,
|
||||
self.height_to_fixed_timestamp.mut_vec(),
|
||||
|(h, t, ..)| (h, Date::from(t)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let decremented_starting_height = starting_indexes.height.decremented().unwrap_or_default();
|
||||
|
||||
let starting_dateindex = self
|
||||
.height_to_dateindex
|
||||
.unwrap_cached_get(decremented_starting_height)
|
||||
.unwrap_or_default();
|
||||
|
||||
self.height_to_dateindex.compute_transform(
|
||||
starting_indexes.height,
|
||||
self.height_to_fixed_date.mut_vec(),
|
||||
|(h, d, ..)| (h, Dateindex::try_from(d).unwrap()),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let starting_dateindex = if let Some(dateindex) = self
|
||||
.height_to_dateindex
|
||||
.unwrap_cached_get(decremented_starting_height)
|
||||
{
|
||||
starting_dateindex.min(dateindex)
|
||||
} else {
|
||||
starting_dateindex
|
||||
};
|
||||
|
||||
self.dateindex_to_first_height
|
||||
.compute_inverse_more_to_less(
|
||||
starting_indexes.height,
|
||||
self.height_to_dateindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let date_count = self.dateindex_to_first_height.len();
|
||||
|
||||
self.dateindex_to_last_height
|
||||
.compute_last_index_from_first(
|
||||
starting_dateindex,
|
||||
self.dateindex_to_first_height.mut_vec(),
|
||||
height_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.dateindex_to_dateindex.compute_transform(
|
||||
starting_dateindex,
|
||||
self.dateindex_to_first_height.mut_vec(),
|
||||
|(di, ..)| (di, di),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.dateindex_to_date.compute_transform(
|
||||
starting_dateindex,
|
||||
self.dateindex_to_dateindex.mut_vec(),
|
||||
|(di, ..)| (di, Date::from(di)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.dateindex_to_timestamp.compute_transform(
|
||||
starting_dateindex,
|
||||
self.dateindex_to_date.mut_vec(),
|
||||
|(di, d, ..)| (di, Timestamp::from(d)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txindex_to_last_txinindex
|
||||
.compute_last_index_from_first(
|
||||
starting_indexes.txindex,
|
||||
indexer_vecs.txindex_to_first_txinindex.mut_vec(),
|
||||
txinindexes_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txindex_to_last_txoutindex
|
||||
.compute_last_index_from_first(
|
||||
starting_indexes.txindex,
|
||||
indexer_vecs.txindex_to_first_txoutindex.mut_vec(),
|
||||
txoutindexes_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.height_to_last_txindex.compute_last_index_from_first(
|
||||
starting_indexes.height,
|
||||
indexer_vecs.height_to_first_txindex.mut_vec(),
|
||||
txindexes_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// ---
|
||||
|
||||
let starting_weekindex = self
|
||||
.dateindex_to_weekindex
|
||||
.unwrap_cached_get(starting_dateindex)
|
||||
.unwrap_or_default();
|
||||
|
||||
self.dateindex_to_weekindex.compute_transform(
|
||||
starting_dateindex,
|
||||
self.dateindex_to_dateindex.mut_vec(),
|
||||
|(di, ..)| (di, Weekindex::from(di)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.weekindex_to_first_dateindex
|
||||
.compute_inverse_more_to_less(
|
||||
starting_dateindex,
|
||||
self.dateindex_to_weekindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.weekindex_to_last_dateindex
|
||||
.compute_last_index_from_first(
|
||||
starting_weekindex,
|
||||
self.weekindex_to_first_dateindex.mut_vec(),
|
||||
date_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.weekindex_to_weekindex.compute_transform(
|
||||
starting_weekindex,
|
||||
self.weekindex_to_first_dateindex.mut_vec(),
|
||||
|(wi, ..)| (wi, wi),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.weekindex_to_timestamp.compute_transform(
|
||||
starting_weekindex,
|
||||
self.weekindex_to_first_dateindex.mut_vec(),
|
||||
|(i, d, ..)| (i, self.dateindex_to_timestamp.double_unwrap_cached_get(d)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// ---
|
||||
|
||||
let starting_monthindex = self
|
||||
.dateindex_to_monthindex
|
||||
.unwrap_cached_get(starting_dateindex)
|
||||
.unwrap_or_default();
|
||||
|
||||
self.dateindex_to_monthindex.compute_transform(
|
||||
starting_dateindex,
|
||||
self.dateindex_to_dateindex.mut_vec(),
|
||||
|(di, ..)| (di, Monthindex::from(di)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex_to_first_dateindex
|
||||
.compute_inverse_more_to_less(
|
||||
starting_dateindex,
|
||||
self.dateindex_to_monthindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let month_count = self.monthindex_to_first_dateindex.len();
|
||||
|
||||
self.monthindex_to_last_dateindex
|
||||
.compute_last_index_from_first(
|
||||
starting_monthindex,
|
||||
self.monthindex_to_first_dateindex.mut_vec(),
|
||||
date_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex_to_monthindex.compute_transform(
|
||||
starting_monthindex,
|
||||
self.monthindex_to_first_dateindex.mut_vec(),
|
||||
|(mi, ..)| (mi, mi),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex_to_timestamp.compute_transform(
|
||||
starting_monthindex,
|
||||
self.monthindex_to_first_dateindex.mut_vec(),
|
||||
|(i, d, ..)| (i, self.dateindex_to_timestamp.double_unwrap_cached_get(d)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// ---
|
||||
|
||||
let starting_quarterindex = self
|
||||
.monthindex_to_quarterindex
|
||||
.unwrap_cached_get(starting_monthindex)
|
||||
.unwrap_or_default();
|
||||
|
||||
self.monthindex_to_quarterindex.compute_transform(
|
||||
starting_monthindex,
|
||||
self.monthindex_to_monthindex.mut_vec(),
|
||||
|(mi, ..)| (mi, Quarterindex::from(mi)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex_to_first_monthindex
|
||||
.compute_inverse_more_to_less(
|
||||
starting_monthindex,
|
||||
self.monthindex_to_quarterindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// let quarter_count = self.quarterindex_to_first_monthindex.len();
|
||||
|
||||
self.quarterindex_to_last_monthindex
|
||||
.compute_last_index_from_first(
|
||||
starting_quarterindex,
|
||||
self.quarterindex_to_first_monthindex.mut_vec(),
|
||||
month_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex_to_quarterindex.compute_transform(
|
||||
starting_quarterindex,
|
||||
self.quarterindex_to_first_monthindex.mut_vec(),
|
||||
|(yi, ..)| (yi, yi),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex_to_timestamp.compute_transform(
|
||||
starting_quarterindex,
|
||||
self.quarterindex_to_first_monthindex.mut_vec(),
|
||||
|(i, m, ..)| (i, self.monthindex_to_timestamp.double_unwrap_cached_get(m)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// ---
|
||||
|
||||
let starting_yearindex = self
|
||||
.monthindex_to_yearindex
|
||||
.unwrap_cached_get(starting_monthindex)
|
||||
.unwrap_or_default();
|
||||
|
||||
self.monthindex_to_yearindex.compute_transform(
|
||||
starting_monthindex,
|
||||
self.monthindex_to_monthindex.mut_vec(),
|
||||
|(mi, ..)| (mi, Yearindex::from(mi)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex_to_first_monthindex
|
||||
.compute_inverse_more_to_less(
|
||||
starting_monthindex,
|
||||
self.monthindex_to_yearindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let year_count = self.yearindex_to_first_monthindex.len();
|
||||
|
||||
self.yearindex_to_last_monthindex
|
||||
.compute_last_index_from_first(
|
||||
starting_yearindex,
|
||||
self.yearindex_to_first_monthindex.mut_vec(),
|
||||
month_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex_to_yearindex.compute_transform(
|
||||
starting_yearindex,
|
||||
self.yearindex_to_first_monthindex.mut_vec(),
|
||||
|(yi, ..)| (yi, yi),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex_to_timestamp.compute_transform(
|
||||
starting_yearindex,
|
||||
self.yearindex_to_first_monthindex.mut_vec(),
|
||||
|(i, m, ..)| (i, self.monthindex_to_timestamp.double_unwrap_cached_get(m)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// ---
|
||||
|
||||
let starting_decadeindex = self
|
||||
.yearindex_to_decadeindex
|
||||
.unwrap_cached_get(starting_yearindex)
|
||||
.unwrap_or_default();
|
||||
|
||||
self.yearindex_to_decadeindex.compute_transform(
|
||||
starting_yearindex,
|
||||
self.yearindex_to_yearindex.mut_vec(),
|
||||
|(yi, ..)| (yi, Decadeindex::from(yi)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex_to_first_yearindex
|
||||
.compute_inverse_more_to_less(
|
||||
starting_yearindex,
|
||||
self.yearindex_to_decadeindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex_to_last_yearindex
|
||||
.compute_last_index_from_first(
|
||||
starting_decadeindex,
|
||||
self.decadeindex_to_first_yearindex.mut_vec(),
|
||||
year_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex_to_decadeindex.compute_transform(
|
||||
starting_decadeindex,
|
||||
self.decadeindex_to_first_yearindex.mut_vec(),
|
||||
|(di, ..)| (di, di),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex_to_timestamp.compute_transform(
|
||||
starting_decadeindex,
|
||||
self.decadeindex_to_first_yearindex.mut_vec(),
|
||||
|(i, y, ..)| (i, self.yearindex_to_timestamp.double_unwrap_cached_get(y)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// ---
|
||||
|
||||
let starting_difficultyepoch = self
|
||||
.height_to_difficultyepoch
|
||||
.unwrap_cached_get(decremented_starting_height)
|
||||
.unwrap_or_default();
|
||||
|
||||
self.height_to_difficultyepoch.compute_transform(
|
||||
starting_indexes.height,
|
||||
self.height_to_height.mut_vec(),
|
||||
|(h, ..)| (h, Difficultyepoch::from(h)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.difficultyepoch_to_first_height
|
||||
.compute_inverse_more_to_less(
|
||||
starting_indexes.height,
|
||||
self.height_to_difficultyepoch.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.difficultyepoch_to_last_height
|
||||
.compute_last_index_from_first(
|
||||
starting_difficultyepoch,
|
||||
self.difficultyepoch_to_first_height.mut_vec(),
|
||||
height_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.difficultyepoch_to_difficultyepoch.compute_transform(
|
||||
starting_difficultyepoch,
|
||||
self.difficultyepoch_to_first_height.mut_vec(),
|
||||
|(de, ..)| (de, de),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.difficultyepoch_to_timestamp.compute_transform(
|
||||
starting_difficultyepoch,
|
||||
self.difficultyepoch_to_first_height.mut_vec(),
|
||||
|(i, h, ..)| {
|
||||
(
|
||||
i,
|
||||
indexer_vecs.height_to_timestamp.double_unwrap_cached_get(h),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// ---
|
||||
|
||||
let starting_halvingepoch = self
|
||||
.height_to_halvingepoch
|
||||
.unwrap_cached_get(decremented_starting_height)
|
||||
.unwrap_or_default();
|
||||
|
||||
self.height_to_halvingepoch.compute_transform(
|
||||
starting_indexes.height,
|
||||
self.height_to_height.mut_vec(),
|
||||
|(h, ..)| (h, Halvingepoch::from(h)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.halvingepoch_to_first_height
|
||||
.compute_inverse_more_to_less(
|
||||
starting_indexes.height,
|
||||
self.height_to_halvingepoch.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.halvingepoch_to_last_height
|
||||
.compute_last_index_from_first(
|
||||
starting_halvingepoch,
|
||||
self.halvingepoch_to_first_height.mut_vec(),
|
||||
height_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.halvingepoch_to_halvingepoch.compute_transform(
|
||||
starting_halvingepoch,
|
||||
self.halvingepoch_to_first_height.mut_vec(),
|
||||
|(he, ..)| (he, he),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// self.difficultyepoch_to_timestamp.compute_transform(
|
||||
// starting_difficultyepoch,
|
||||
// self.difficultyepoch_to_first_height.mut_vec(),
|
||||
// |(i, h, ..)| {
|
||||
// (
|
||||
// i,
|
||||
// *indexer_vecs.height_to_timestamp.unwraped_cached_get(h).unwrap().unwrap(),
|
||||
// )
|
||||
// },
|
||||
// exit,
|
||||
// )?;
|
||||
|
||||
Ok(Indexes {
|
||||
indexes: starting_indexes,
|
||||
dateindex: starting_dateindex,
|
||||
weekindex: starting_weekindex,
|
||||
monthindex: starting_monthindex,
|
||||
quarterindex: starting_quarterindex,
|
||||
yearindex: starting_yearindex,
|
||||
decadeindex: starting_decadeindex,
|
||||
difficultyepoch: starting_difficultyepoch,
|
||||
halvingepoch: starting_halvingepoch,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_any_vecs(&self) -> Vec<&dyn brk_vec::AnyStoredVec> {
|
||||
vec![
|
||||
self.dateindex_to_date.any_vec(),
|
||||
self.dateindex_to_dateindex.any_vec(),
|
||||
self.dateindex_to_first_height.any_vec(),
|
||||
self.dateindex_to_last_height.any_vec(),
|
||||
self.height_to_dateindex.any_vec(),
|
||||
self.height_to_fixed_date.any_vec(),
|
||||
self.height_to_height.any_vec(),
|
||||
self.height_to_last_txindex.any_vec(),
|
||||
self.height_to_real_date.any_vec(),
|
||||
self.txindex_to_last_txinindex.any_vec(),
|
||||
self.txindex_to_last_txoutindex.any_vec(),
|
||||
self.difficultyepoch_to_first_height.any_vec(),
|
||||
self.difficultyepoch_to_last_height.any_vec(),
|
||||
self.halvingepoch_to_first_height.any_vec(),
|
||||
self.halvingepoch_to_last_height.any_vec(),
|
||||
self.weekindex_to_first_dateindex.any_vec(),
|
||||
self.weekindex_to_last_dateindex.any_vec(),
|
||||
self.monthindex_to_first_dateindex.any_vec(),
|
||||
self.monthindex_to_last_dateindex.any_vec(),
|
||||
self.yearindex_to_first_monthindex.any_vec(),
|
||||
self.yearindex_to_last_monthindex.any_vec(),
|
||||
self.decadeindex_to_first_yearindex.any_vec(),
|
||||
self.decadeindex_to_last_yearindex.any_vec(),
|
||||
self.dateindex_to_weekindex.any_vec(),
|
||||
self.dateindex_to_monthindex.any_vec(),
|
||||
self.monthindex_to_yearindex.any_vec(),
|
||||
self.yearindex_to_decadeindex.any_vec(),
|
||||
self.height_to_difficultyepoch.any_vec(),
|
||||
self.height_to_halvingepoch.any_vec(),
|
||||
self.weekindex_to_weekindex.any_vec(),
|
||||
self.monthindex_to_monthindex.any_vec(),
|
||||
self.yearindex_to_yearindex.any_vec(),
|
||||
self.decadeindex_to_decadeindex.any_vec(),
|
||||
self.difficultyepoch_to_difficultyepoch.any_vec(),
|
||||
self.halvingepoch_to_halvingepoch.any_vec(),
|
||||
self.dateindex_to_timestamp.any_vec(),
|
||||
self.decadeindex_to_timestamp.any_vec(),
|
||||
self.difficultyepoch_to_timestamp.any_vec(),
|
||||
self.halvingepoch_to_timestamp.any_vec(),
|
||||
self.monthindex_to_timestamp.any_vec(),
|
||||
self.weekindex_to_timestamp.any_vec(),
|
||||
self.yearindex_to_timestamp.any_vec(),
|
||||
self.height_to_fixed_timestamp.any_vec(),
|
||||
self.monthindex_to_quarterindex.any_vec(),
|
||||
self.quarterindex_to_first_monthindex.any_vec(),
|
||||
self.quarterindex_to_last_monthindex.any_vec(),
|
||||
self.quarterindex_to_quarterindex.any_vec(),
|
||||
self.quarterindex_to_timestamp.any_vec(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Indexes {
|
||||
indexes: brk_indexer::Indexes,
|
||||
pub dateindex: Dateindex,
|
||||
pub weekindex: Weekindex,
|
||||
pub monthindex: Monthindex,
|
||||
pub quarterindex: Quarterindex,
|
||||
pub yearindex: Yearindex,
|
||||
pub decadeindex: Decadeindex,
|
||||
pub difficultyepoch: Difficultyepoch,
|
||||
pub halvingepoch: Halvingepoch,
|
||||
}
|
||||
|
||||
impl Deref for Indexes {
|
||||
type Target = brk_indexer::Indexes;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.indexes
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use brk_exit::Exit;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyStoredVec, Compressed};
|
||||
|
||||
pub mod base;
|
||||
pub mod blocks;
|
||||
pub mod grouped;
|
||||
pub mod indexes;
|
||||
pub mod marketprice;
|
||||
pub mod transactions;
|
||||
|
||||
use base::*;
|
||||
use indexes::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
pub blocks: blocks::Vecs,
|
||||
pub indexes: indexes::Vecs,
|
||||
pub transactions: transactions::Vecs,
|
||||
pub marketprice: Option<marketprice::Vecs>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn import(path: &Path, fetch: bool, compressed: Compressed) -> color_eyre::Result<Self> {
|
||||
fs::create_dir_all(path)?;
|
||||
|
||||
Ok(Self {
|
||||
blocks: blocks::Vecs::forced_import(path, compressed)?,
|
||||
indexes: indexes::Vecs::forced_import(path, compressed)?,
|
||||
transactions: transactions::Vecs::forced_import(path, compressed, fetch)?,
|
||||
marketprice: fetch.then(|| marketprice::Vecs::forced_import(path, compressed).unwrap()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
starting_indexes: brk_indexer::Indexes,
|
||||
fetcher: Option<&mut Fetcher>,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
let starting_indexes = self.indexes.compute(indexer, starting_indexes, exit)?;
|
||||
|
||||
self.blocks
|
||||
.compute(indexer, &mut self.indexes, &starting_indexes, exit)?;
|
||||
|
||||
if let Some(marketprice) = self.marketprice.as_mut() {
|
||||
marketprice.compute(
|
||||
indexer,
|
||||
&mut self.indexes,
|
||||
&starting_indexes,
|
||||
fetcher.unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
self.transactions.compute(
|
||||
indexer,
|
||||
&mut self.indexes,
|
||||
&starting_indexes,
|
||||
&mut self.marketprice.as_mut(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn as_any_vecs(&self) -> Vec<&dyn AnyStoredVec> {
|
||||
[
|
||||
self.indexes.as_any_vecs(),
|
||||
self.blocks.as_any_vecs(),
|
||||
self.transactions.as_any_vecs(),
|
||||
self.marketprice
|
||||
.as_ref()
|
||||
.map_or(vec![], |v| v.as_any_vecs()),
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
@@ -1,578 +0,0 @@
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use brk_core::{
|
||||
CheckedSub, Feerate, Sats, StoredU32, StoredU64, StoredUsize, TxVersion, Txindex, Txinindex,
|
||||
Txoutindex, Weight,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_parser::bitcoin;
|
||||
use brk_vec::{Compressed, DynamicVec, StoredIndex, Version};
|
||||
|
||||
use super::{
|
||||
ComputedVec, Indexes,
|
||||
grouped::{
|
||||
ComputedValueVecsFromHeight, ComputedValueVecsFromTxindex, ComputedVecsFromHeight,
|
||||
ComputedVecsFromTxindex, StorableVecGeneatorOptions,
|
||||
},
|
||||
indexes, marketprice,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
pub indexes_to_tx_count: ComputedVecsFromHeight<StoredU64>,
|
||||
pub indexes_to_fee: ComputedValueVecsFromTxindex,
|
||||
pub indexes_to_feerate: ComputedVecsFromTxindex<Feerate>,
|
||||
pub indexes_to_input_value: ComputedVecsFromTxindex<Sats>,
|
||||
pub indexes_to_output_value: ComputedVecsFromTxindex<Sats>,
|
||||
// pub txindex_to_is_v1: ComputedVec<Txindex, bool>,
|
||||
pub indexes_to_tx_v1: ComputedVecsFromHeight<StoredU32>,
|
||||
// pub txindex_to_is_v2: ComputedVec<Txindex, bool>,
|
||||
pub indexes_to_tx_v2: ComputedVecsFromHeight<StoredU32>,
|
||||
// pub txindex_to_is_v3: ComputedVec<Txindex, bool>,
|
||||
pub indexes_to_tx_v3: ComputedVecsFromHeight<StoredU32>,
|
||||
pub indexes_to_tx_vsize: ComputedVecsFromTxindex<StoredUsize>,
|
||||
pub indexes_to_tx_weight: ComputedVecsFromTxindex<Weight>,
|
||||
pub txindex_to_input_count: ComputedVecsFromTxindex<StoredU64>,
|
||||
pub txindex_to_is_coinbase: ComputedVec<Txindex, bool>,
|
||||
pub txindex_to_output_count: ComputedVecsFromTxindex<StoredU64>,
|
||||
pub txindex_to_vsize: ComputedVec<Txindex, StoredUsize>,
|
||||
pub txindex_to_weight: ComputedVec<Txindex, Weight>,
|
||||
/// Value == 0 when Coinbase
|
||||
pub txinindex_to_value: ComputedVec<Txinindex, Sats>,
|
||||
pub indexes_to_subsidy: ComputedValueVecsFromHeight,
|
||||
pub indexes_to_coinbase: ComputedValueVecsFromHeight,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
compressed: Compressed,
|
||||
compute_dollars: bool,
|
||||
) -> color_eyre::Result<Self> {
|
||||
fs::create_dir_all(path)?;
|
||||
|
||||
Ok(Self {
|
||||
indexes_to_tx_count: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"tx_count",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_average()
|
||||
.add_minmax()
|
||||
.add_percentiles()
|
||||
.add_sum()
|
||||
.add_total(),
|
||||
)?,
|
||||
// height_to_subsidy: StorableVec::forced_import(&path.join("height_to_subsidy"), Version::ZERO)?,
|
||||
txindex_to_is_coinbase: ComputedVec::forced_import(
|
||||
&path.join("txindex_to_is_coinbase"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
txindex_to_input_count: ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
"input_count",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_average()
|
||||
.add_minmax()
|
||||
.add_percentiles()
|
||||
.add_sum()
|
||||
.add_total(),
|
||||
)?,
|
||||
txindex_to_output_count: ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
"output_count",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_average()
|
||||
.add_minmax()
|
||||
.add_percentiles()
|
||||
.add_sum()
|
||||
.add_total(),
|
||||
)?,
|
||||
txinindex_to_value: ComputedVec::forced_import(
|
||||
&path.join("txinindex_to_value"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
indexes_to_tx_v1: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"tx_v1",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default().add_sum().add_total(),
|
||||
)?,
|
||||
indexes_to_tx_v2: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"tx_v2",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default().add_sum().add_total(),
|
||||
)?,
|
||||
indexes_to_tx_v3: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"tx_v3",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default().add_sum().add_total(),
|
||||
)?,
|
||||
indexes_to_input_value: ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
"input_value",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_average()
|
||||
.add_sum()
|
||||
.add_total(),
|
||||
)?,
|
||||
indexes_to_output_value: ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
"output_value",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_average()
|
||||
.add_sum()
|
||||
.add_total(),
|
||||
)?,
|
||||
indexes_to_fee: ComputedValueVecsFromTxindex::forced_import(
|
||||
path,
|
||||
"fee",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_total()
|
||||
.add_percentiles()
|
||||
.add_minmax()
|
||||
.add_average(),
|
||||
compute_dollars,
|
||||
)?,
|
||||
indexes_to_feerate: ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
"feerate",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_percentiles()
|
||||
.add_minmax()
|
||||
.add_average(),
|
||||
)?,
|
||||
txindex_to_weight: ComputedVec::forced_import(
|
||||
&path.join("txindex_to_weight"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
txindex_to_vsize: ComputedVec::forced_import(
|
||||
&path.join("txindex_to_vsize"),
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
)?,
|
||||
indexes_to_tx_vsize: ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
"tx_vsize",
|
||||
false,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_percentiles()
|
||||
.add_minmax()
|
||||
.add_average(),
|
||||
)?,
|
||||
indexes_to_tx_weight: ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
"tx_weight",
|
||||
false,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_percentiles()
|
||||
.add_minmax()
|
||||
.add_average(),
|
||||
)?,
|
||||
indexes_to_subsidy: ComputedValueVecsFromHeight::forced_import(
|
||||
path,
|
||||
"subsidy",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_percentiles()
|
||||
.add_sum()
|
||||
.add_total()
|
||||
.add_minmax()
|
||||
.add_average(),
|
||||
compute_dollars,
|
||||
)?,
|
||||
indexes_to_coinbase: ComputedValueVecsFromHeight::forced_import(
|
||||
path,
|
||||
"coinbase",
|
||||
true,
|
||||
Version::ZERO,
|
||||
compressed,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_total()
|
||||
.add_percentiles()
|
||||
.add_minmax()
|
||||
.add_average(),
|
||||
compute_dollars,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexes: &mut indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
marketprices: &mut Option<&mut marketprice::Vecs>,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.indexes_to_tx_count.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, indexer, indexes, starting_indexes, exit| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
indexer.mut_vecs().height_to_first_txindex.mut_vec(),
|
||||
indexes.height_to_last_txindex.mut_vec(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.txindex_to_input_count.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, indexer, indexes, starting_indexes, exit| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
indexer.mut_vecs().txindex_to_first_txinindex.mut_vec(),
|
||||
indexes.txindex_to_last_txinindex.mut_vec(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.txindex_to_output_count.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, indexer, indexes, starting_indexes, exit| {
|
||||
v.compute_count_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
indexer.mut_vecs().txindex_to_first_txoutindex.mut_vec(),
|
||||
indexes.txindex_to_last_txoutindex.mut_vec(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
let mut compute_indexes_to_tx_vany =
|
||||
|indexes_to_tx_vany: &mut ComputedVecsFromHeight<StoredU32>, txversion| {
|
||||
indexes_to_tx_vany.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, indexer, indexes, starting_indexes, exit| {
|
||||
let indexer_vecs = indexer.mut_vecs();
|
||||
vec.compute_filtered_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
indexer_vecs.height_to_first_txindex.mut_vec(),
|
||||
indexes.height_to_last_txindex.mut_vec(),
|
||||
|txindex| {
|
||||
let v = indexer_vecs
|
||||
.txindex_to_txversion
|
||||
.double_unwrap_cached_get(txindex);
|
||||
v == txversion
|
||||
},
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)
|
||||
};
|
||||
compute_indexes_to_tx_vany(&mut self.indexes_to_tx_v1, TxVersion::ONE)?;
|
||||
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)?;
|
||||
|
||||
let indexer_vecs = indexer.mut_vecs();
|
||||
|
||||
self.txindex_to_is_coinbase.compute_is_first_ordered(
|
||||
starting_indexes.txindex,
|
||||
indexer_vecs.txindex_to_height.mut_vec(),
|
||||
indexer_vecs.height_to_first_txindex.mut_vec(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txindex_to_weight.compute_transform(
|
||||
starting_indexes.txindex,
|
||||
indexer_vecs.txindex_to_base_size.mut_vec(),
|
||||
|(txindex, base_size, ..)| {
|
||||
let total_size = indexer_vecs
|
||||
.txindex_to_total_size
|
||||
.mut_vec()
|
||||
.double_unwrap_cached_get(txindex);
|
||||
|
||||
// This is the exact definition of a weight unit, as defined by BIP-141 (quote above).
|
||||
let wu = base_size * 3 + total_size;
|
||||
let weight = Weight::from(bitcoin::Weight::from_wu_usize(wu));
|
||||
|
||||
(txindex, weight)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txindex_to_vsize.compute_transform(
|
||||
starting_indexes.txindex,
|
||||
self.txindex_to_weight.mut_vec(),
|
||||
|(txindex, weight, ..)| {
|
||||
let vbytes =
|
||||
StoredUsize::from(bitcoin::Weight::from(weight).to_vbytes_ceil() as usize);
|
||||
(txindex, vbytes)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.txinindex_to_value.compute_transform(
|
||||
starting_indexes.txinindex,
|
||||
indexer_vecs.txinindex_to_txoutindex.mut_vec(),
|
||||
|(txinindex, txoutindex, slf, other)| {
|
||||
let value = if txoutindex == Txoutindex::COINBASE {
|
||||
Sats::ZERO
|
||||
} else if let Some(value) = indexer_vecs
|
||||
.txoutindex_to_value
|
||||
.mut_vec()
|
||||
.unwrap_cached_get(txoutindex)
|
||||
{
|
||||
value
|
||||
} else {
|
||||
dbg!(txinindex, txoutindex, slf.len(), other.len());
|
||||
panic!()
|
||||
};
|
||||
(txinindex, value)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.indexes_to_output_value.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, indexer, indexes, starting_indexes, exit| {
|
||||
let indexer_vecs = indexer.mut_vecs();
|
||||
vec.compute_sum_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
indexer_vecs.txindex_to_first_txoutindex.mut_vec(),
|
||||
indexes.txindex_to_last_txoutindex.mut_vec(),
|
||||
indexer_vecs.txoutindex_to_value.mut_vec(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_input_value.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, indexer, indexes, starting_indexes, exit| {
|
||||
let indexer_vecs = indexer.mut_vecs();
|
||||
vec.compute_sum_from_indexes(
|
||||
starting_indexes.txindex,
|
||||
indexer_vecs.txindex_to_first_txinindex.mut_vec(),
|
||||
indexes.txindex_to_last_txinindex.mut_vec(),
|
||||
self.txinindex_to_value.mut_vec(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_fee.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
marketprices,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
let txindex_to_output_value = self
|
||||
.indexes_to_output_value
|
||||
.txindex
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.mut_vec();
|
||||
vec.compute_transform(
|
||||
starting_indexes.txindex,
|
||||
self.indexes_to_input_value
|
||||
.txindex
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.mut_vec(),
|
||||
|(txindex, input_value, ..)| {
|
||||
if input_value.is_zero() {
|
||||
(txindex, input_value)
|
||||
} else {
|
||||
let output_value =
|
||||
txindex_to_output_value.double_unwrap_cached_get(txindex);
|
||||
(txindex, input_value.checked_sub(output_value).unwrap())
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_feerate.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.txindex,
|
||||
self.indexes_to_fee.sats.txindex.as_mut().unwrap().mut_vec(),
|
||||
|(txindex, fee, ..)| {
|
||||
let vsize = self
|
||||
.txindex_to_vsize
|
||||
.mut_vec()
|
||||
.double_unwrap_cached_get(txindex);
|
||||
|
||||
(txindex, Feerate::from((fee, vsize)))
|
||||
},
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_tx_weight.compute_rest(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.txindex_to_weight.mut_vec()),
|
||||
)?;
|
||||
|
||||
self.indexes_to_tx_vsize.compute_rest(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.txindex_to_vsize.mut_vec()),
|
||||
)?;
|
||||
|
||||
self.indexes_to_subsidy.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
marketprices,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, indexer, indexes, starting_indexes, exit| {
|
||||
let indexer_vecs = indexer.mut_vecs();
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
indexer_vecs.height_to_first_txindex.mut_vec(),
|
||||
|(height, txindex, ..)| {
|
||||
let first_txoutindex = indexer_vecs
|
||||
.txindex_to_first_txoutindex
|
||||
.double_unwrap_cached_get(txindex)
|
||||
.unwrap_to_usize();
|
||||
let last_txoutindex = indexes
|
||||
.txindex_to_last_txoutindex
|
||||
.mut_vec()
|
||||
.double_unwrap_cached_get(txindex)
|
||||
.unwrap_to_usize();
|
||||
let mut sats = Sats::ZERO;
|
||||
(first_txoutindex..=last_txoutindex).for_each(|txoutindex| {
|
||||
sats += indexer_vecs
|
||||
.txoutindex_to_value
|
||||
.double_unwrap_cached_get(Txoutindex::from(txoutindex));
|
||||
});
|
||||
(height, sats)
|
||||
},
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_coinbase.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
marketprices,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
self.indexes_to_subsidy
|
||||
.sats
|
||||
.height
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.mut_vec(),
|
||||
|(height, subsidy, ..)| {
|
||||
let fees = self
|
||||
.indexes_to_fee
|
||||
.sats
|
||||
.height
|
||||
.unwrap_sum()
|
||||
.mut_vec()
|
||||
.double_unwrap_cached_get(height);
|
||||
(height, subsidy.checked_sub(fees).unwrap())
|
||||
},
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn as_any_vecs(&self) -> Vec<&dyn brk_vec::AnyStoredVec> {
|
||||
[
|
||||
vec![
|
||||
self.txindex_to_is_coinbase.any_vec(),
|
||||
self.txinindex_to_value.any_vec(),
|
||||
self.txindex_to_weight.any_vec(),
|
||||
self.txindex_to_vsize.any_vec(),
|
||||
],
|
||||
self.indexes_to_tx_count.any_vecs(),
|
||||
self.indexes_to_coinbase.any_vecs(),
|
||||
self.indexes_to_fee.any_vecs(),
|
||||
self.indexes_to_feerate.any_vecs(),
|
||||
self.indexes_to_input_value.any_vecs(),
|
||||
self.indexes_to_output_value.any_vecs(),
|
||||
self.indexes_to_subsidy.any_vecs(),
|
||||
self.indexes_to_tx_v1.any_vecs(),
|
||||
self.indexes_to_tx_v2.any_vecs(),
|
||||
self.indexes_to_tx_v3.any_vecs(),
|
||||
self.indexes_to_tx_vsize.any_vecs(),
|
||||
self.indexes_to_tx_weight.any_vecs(),
|
||||
self.txindex_to_input_count.any_vecs(),
|
||||
self.txindex_to_output_count.any_vecs(),
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,647 @@
|
||||
use std::{path::Path, thread};
|
||||
|
||||
use brk_core::{
|
||||
AddressData, ByAddressType, EmptyAddressData, Height, OutputType, P2AAddressIndex,
|
||||
P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2TRAddressIndex,
|
||||
P2WPKHAddressIndex, P2WSHAddressIndex, Result, TypeIndex, Version,
|
||||
};
|
||||
use brk_store::{AnyStore, Store};
|
||||
use fjall::{PersistMode, TransactionalKeyspace};
|
||||
use log::info;
|
||||
|
||||
use crate::vecs::stateful::{AddressTypeToTypeIndexTree, WithAddressDataSource};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Stores {
|
||||
keyspace: TransactionalKeyspace,
|
||||
|
||||
pub p2aaddressindex_to_addressdata: Store<P2AAddressIndex, AddressData>,
|
||||
pub p2aaddressindex_to_emptyaddressdata: Store<P2AAddressIndex, EmptyAddressData>,
|
||||
pub p2pk33addressindex_to_addressdata: Store<P2PK33AddressIndex, AddressData>,
|
||||
pub p2pk33addressindex_to_emptyaddressdata: Store<P2PK33AddressIndex, EmptyAddressData>,
|
||||
pub p2pk65addressindex_to_addressdata: Store<P2PK65AddressIndex, AddressData>,
|
||||
pub p2pk65addressindex_to_emptyaddressdata: Store<P2PK65AddressIndex, EmptyAddressData>,
|
||||
pub p2pkhaddressindex_to_addressdata: Store<P2PKHAddressIndex, AddressData>,
|
||||
pub p2pkhaddressindex_to_emptyaddressdata: Store<P2PKHAddressIndex, EmptyAddressData>,
|
||||
pub p2shaddressindex_to_addressdata: Store<P2SHAddressIndex, AddressData>,
|
||||
pub p2shaddressindex_to_emptyaddressdata: Store<P2SHAddressIndex, EmptyAddressData>,
|
||||
pub p2traddressindex_to_addressdata: Store<P2TRAddressIndex, AddressData>,
|
||||
pub p2traddressindex_to_emptyaddressdata: Store<P2TRAddressIndex, EmptyAddressData>,
|
||||
pub p2wpkhaddressindex_to_addressdata: Store<P2WPKHAddressIndex, AddressData>,
|
||||
pub p2wpkhaddressindex_to_emptyaddressdata: Store<P2WPKHAddressIndex, EmptyAddressData>,
|
||||
pub p2wshaddressindex_to_addressdata: Store<P2WSHAddressIndex, AddressData>,
|
||||
pub p2wshaddressindex_to_emptyaddressdata: Store<P2WSHAddressIndex, EmptyAddressData>,
|
||||
}
|
||||
|
||||
impl Stores {
|
||||
pub fn import(
|
||||
path: &Path,
|
||||
version: Version,
|
||||
keyspace: &TransactionalKeyspace,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let (
|
||||
(p2aaddressindex_to_addressdata, p2aaddressindex_to_emptyaddressdata),
|
||||
(p2pk33addressindex_to_addressdata, p2pk33addressindex_to_emptyaddressdata),
|
||||
(p2pk65addressindex_to_addressdata, p2pk65addressindex_to_emptyaddressdata),
|
||||
(p2pkhaddressindex_to_addressdata, p2pkhaddressindex_to_emptyaddressdata),
|
||||
(p2shaddressindex_to_addressdata, p2shaddressindex_to_emptyaddressdata),
|
||||
(p2traddressindex_to_addressdata, p2traddressindex_to_emptyaddressdata),
|
||||
(p2wpkhaddressindex_to_addressdata, p2wpkhaddressindex_to_emptyaddressdata),
|
||||
(p2wshaddressindex_to_addressdata, p2wshaddressindex_to_emptyaddressdata),
|
||||
) = thread::scope(|scope| {
|
||||
let p2a = scope.spawn(|| {
|
||||
(
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2aaddressindex_to_addressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2aaddressindex_to_emptyaddressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let p2pk33 = scope.spawn(|| {
|
||||
(
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2pk33addressindex_to_addressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2pk33addressindex_to_emptyaddressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let p2pk65 = scope.spawn(|| {
|
||||
(
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2pk65addressindex_to_addressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2pk65addressindex_to_emptyaddressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let p2pkh = scope.spawn(|| {
|
||||
(
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2pkhaddressindex_to_addressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2pkhaddressindex_to_emptyaddressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let p2sh = scope.spawn(|| {
|
||||
(
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2shaddressindex_to_addressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2shaddressindex_to_emptyaddressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let p2tr = scope.spawn(|| {
|
||||
(
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2traddressindex_to_addressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2traddressindex_to_emptyaddressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let p2wpkh = scope.spawn(|| {
|
||||
(
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2wpkhaddressindex_to_addressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2wpkhaddressindex_to_emptyaddressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let p2wsh = scope.spawn(|| {
|
||||
(
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2wshaddressindex_to_addressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
Store::import(
|
||||
keyspace,
|
||||
path,
|
||||
"p2wshaddressindex_to_emptyaddressdata",
|
||||
version + VERSION + Version::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
(
|
||||
p2a.join().unwrap(),
|
||||
p2pk33.join().unwrap(),
|
||||
p2pk65.join().unwrap(),
|
||||
p2pkh.join().unwrap(),
|
||||
p2sh.join().unwrap(),
|
||||
p2tr.join().unwrap(),
|
||||
p2wpkh.join().unwrap(),
|
||||
p2wsh.join().unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
keyspace: keyspace.clone(),
|
||||
|
||||
p2aaddressindex_to_addressdata,
|
||||
p2aaddressindex_to_emptyaddressdata,
|
||||
|
||||
p2pk33addressindex_to_addressdata,
|
||||
p2pk33addressindex_to_emptyaddressdata,
|
||||
|
||||
p2pk65addressindex_to_addressdata,
|
||||
p2pk65addressindex_to_emptyaddressdata,
|
||||
|
||||
p2pkhaddressindex_to_addressdata,
|
||||
p2pkhaddressindex_to_emptyaddressdata,
|
||||
|
||||
p2shaddressindex_to_addressdata,
|
||||
p2shaddressindex_to_emptyaddressdata,
|
||||
|
||||
p2traddressindex_to_addressdata,
|
||||
p2traddressindex_to_emptyaddressdata,
|
||||
|
||||
p2wpkhaddressindex_to_addressdata,
|
||||
p2wpkhaddressindex_to_emptyaddressdata,
|
||||
|
||||
p2wshaddressindex_to_addressdata,
|
||||
p2wshaddressindex_to_emptyaddressdata,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn starting_height(&self) -> Height {
|
||||
self.as_slice()
|
||||
.into_iter()
|
||||
.map(|store| store.height().map(Height::incremented).unwrap_or_default())
|
||||
.min()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Result<()> {
|
||||
info!("Resetting stores...");
|
||||
info!("> If it gets stuck here, stop the program and start it again");
|
||||
|
||||
self.as_mut_slice()
|
||||
.into_iter()
|
||||
.try_for_each(|store| store.reset())?;
|
||||
|
||||
self.keyspace
|
||||
.persist(PersistMode::SyncAll)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn get_addressdata(
|
||||
&self,
|
||||
address_type: OutputType,
|
||||
type_index: TypeIndex,
|
||||
) -> Result<Option<AddressData>> {
|
||||
Ok(match address_type {
|
||||
OutputType::P2A => self
|
||||
.p2aaddressindex_to_addressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2PK33 => self
|
||||
.p2pk33addressindex_to_addressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2PK65 => self
|
||||
.p2pk65addressindex_to_addressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2PKH => self
|
||||
.p2pkhaddressindex_to_addressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2SH => self
|
||||
.p2shaddressindex_to_addressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2TR => self
|
||||
.p2traddressindex_to_addressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2WPKH => self
|
||||
.p2wpkhaddressindex_to_addressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2WSH => self
|
||||
.p2wshaddressindex_to_addressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_emptyaddressdata(
|
||||
&self,
|
||||
address_type: OutputType,
|
||||
type_index: TypeIndex,
|
||||
) -> Result<Option<EmptyAddressData>> {
|
||||
Ok(match address_type {
|
||||
OutputType::P2A => self
|
||||
.p2aaddressindex_to_emptyaddressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2PK33 => self
|
||||
.p2pk33addressindex_to_emptyaddressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2PK65 => self
|
||||
.p2pk65addressindex_to_emptyaddressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2PKH => self
|
||||
.p2pkhaddressindex_to_emptyaddressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2SH => self
|
||||
.p2shaddressindex_to_emptyaddressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2TR => self
|
||||
.p2traddressindex_to_emptyaddressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2WPKH => self
|
||||
.p2wpkhaddressindex_to_emptyaddressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
OutputType::P2WSH => self
|
||||
.p2wshaddressindex_to_emptyaddressdata
|
||||
.get(&type_index.into())?
|
||||
.map(|c| c.into_owned()),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn commit(
|
||||
&mut self,
|
||||
height: Height,
|
||||
addresstype_to_typeindex_to_addressdata: AddressTypeToTypeIndexTree<
|
||||
WithAddressDataSource<AddressData>,
|
||||
>,
|
||||
addresstype_to_typeindex_to_emptyaddressdata: AddressTypeToTypeIndexTree<
|
||||
WithAddressDataSource<EmptyAddressData>,
|
||||
>,
|
||||
) -> Result<()> {
|
||||
let ByAddressType {
|
||||
p2pk65,
|
||||
p2pk33,
|
||||
p2pkh,
|
||||
p2sh,
|
||||
p2wpkh,
|
||||
p2wsh,
|
||||
p2tr,
|
||||
p2a,
|
||||
} = addresstype_to_typeindex_to_addressdata.unwrap();
|
||||
|
||||
let ByAddressType {
|
||||
p2pk65: empty_p2pk65,
|
||||
p2pk33: empty_p2pk33,
|
||||
p2pkh: empty_p2pkh,
|
||||
p2sh: empty_p2sh,
|
||||
p2wpkh: empty_p2wpkh,
|
||||
p2wsh: empty_p2wsh,
|
||||
p2tr: empty_p2tr,
|
||||
p2a: empty_p2a,
|
||||
} = addresstype_to_typeindex_to_emptyaddressdata.unwrap();
|
||||
|
||||
thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
self.p2aaddressindex_to_addressdata.commit_(
|
||||
height,
|
||||
empty_p2a
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_addressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
p2a.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
s.spawn(|| {
|
||||
self.p2pk33addressindex_to_addressdata.commit_(
|
||||
height,
|
||||
empty_p2pk33
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_addressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
p2pk33.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
s.spawn(|| {
|
||||
self.p2pk65addressindex_to_addressdata.commit_(
|
||||
height,
|
||||
empty_p2pk65
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_addressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
p2pk65.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
s.spawn(|| {
|
||||
self.p2pkhaddressindex_to_addressdata.commit_(
|
||||
height,
|
||||
empty_p2pkh
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_addressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
p2pkh.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
s.spawn(|| {
|
||||
self.p2shaddressindex_to_addressdata.commit_(
|
||||
height,
|
||||
empty_p2sh
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_addressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
p2sh.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
s.spawn(|| {
|
||||
self.p2traddressindex_to_addressdata.commit_(
|
||||
height,
|
||||
empty_p2tr
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_addressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
p2tr.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
s.spawn(|| {
|
||||
self.p2wpkhaddressindex_to_addressdata.commit_(
|
||||
height,
|
||||
empty_p2wpkh
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_addressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
p2wpkh.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
s.spawn(|| {
|
||||
self.p2wshaddressindex_to_addressdata.commit_(
|
||||
height,
|
||||
empty_p2wsh
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_addressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
p2wsh.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
});
|
||||
|
||||
thread::scope(|scope| {
|
||||
scope.spawn(|| {
|
||||
self.p2aaddressindex_to_emptyaddressdata.commit_(
|
||||
height,
|
||||
p2a.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_emptyaddressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
empty_p2a.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
scope.spawn(|| {
|
||||
self.p2pk33addressindex_to_emptyaddressdata.commit_(
|
||||
height,
|
||||
p2pk33
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_emptyaddressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
empty_p2pk33.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
scope.spawn(|| {
|
||||
self.p2pk65addressindex_to_emptyaddressdata.commit_(
|
||||
height,
|
||||
p2pk65
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_emptyaddressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
empty_p2pk65.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
scope.spawn(|| {
|
||||
self.p2pkhaddressindex_to_emptyaddressdata.commit_(
|
||||
height,
|
||||
p2pkh
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_emptyaddressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
empty_p2pkh.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
scope.spawn(|| {
|
||||
self.p2shaddressindex_to_emptyaddressdata.commit_(
|
||||
height,
|
||||
p2sh.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_emptyaddressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
empty_p2sh.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
scope.spawn(|| {
|
||||
self.p2traddressindex_to_emptyaddressdata.commit_(
|
||||
height,
|
||||
p2tr.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_emptyaddressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
empty_p2tr.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
scope.spawn(|| {
|
||||
self.p2wpkhaddressindex_to_emptyaddressdata.commit_(
|
||||
height,
|
||||
p2wpkh
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_emptyaddressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
empty_p2wpkh.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
scope.spawn(|| {
|
||||
self.p2wshaddressindex_to_emptyaddressdata.commit_(
|
||||
height,
|
||||
p2wsh
|
||||
.iter()
|
||||
.filter(|(_, addressdata)| addressdata.is_from_emptyaddressdata())
|
||||
.map(|(typeindex, _)| (*typeindex).into()),
|
||||
empty_p2wsh.iter().map(|(typeindex, addressdata)| {
|
||||
((*typeindex).into(), addressdata.deref().clone())
|
||||
}),
|
||||
)
|
||||
});
|
||||
});
|
||||
|
||||
self.keyspace
|
||||
.persist(PersistMode::SyncAll)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn rotate_memtables(&self) {
|
||||
self.as_slice()
|
||||
.into_iter()
|
||||
.for_each(|store| store.rotate_memtable());
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> [&(dyn AnyStore + Send + Sync); 16] {
|
||||
[
|
||||
&self.p2aaddressindex_to_addressdata,
|
||||
&self.p2aaddressindex_to_emptyaddressdata,
|
||||
&self.p2pk33addressindex_to_addressdata,
|
||||
&self.p2pk33addressindex_to_emptyaddressdata,
|
||||
&self.p2pk65addressindex_to_addressdata,
|
||||
&self.p2pk65addressindex_to_emptyaddressdata,
|
||||
&self.p2pkhaddressindex_to_addressdata,
|
||||
&self.p2pkhaddressindex_to_emptyaddressdata,
|
||||
&self.p2shaddressindex_to_addressdata,
|
||||
&self.p2shaddressindex_to_emptyaddressdata,
|
||||
&self.p2traddressindex_to_addressdata,
|
||||
&self.p2traddressindex_to_emptyaddressdata,
|
||||
&self.p2wpkhaddressindex_to_addressdata,
|
||||
&self.p2wpkhaddressindex_to_emptyaddressdata,
|
||||
&self.p2wshaddressindex_to_addressdata,
|
||||
&self.p2wshaddressindex_to_emptyaddressdata,
|
||||
]
|
||||
}
|
||||
|
||||
fn as_mut_slice(&mut self) -> [&mut (dyn AnyStore + Send + Sync); 16] {
|
||||
[
|
||||
&mut self.p2aaddressindex_to_addressdata,
|
||||
&mut self.p2aaddressindex_to_emptyaddressdata,
|
||||
&mut self.p2pk33addressindex_to_addressdata,
|
||||
&mut self.p2pk33addressindex_to_emptyaddressdata,
|
||||
&mut self.p2pk65addressindex_to_addressdata,
|
||||
&mut self.p2pk65addressindex_to_emptyaddressdata,
|
||||
&mut self.p2pkhaddressindex_to_addressdata,
|
||||
&mut self.p2pkhaddressindex_to_emptyaddressdata,
|
||||
&mut self.p2shaddressindex_to_addressdata,
|
||||
&mut self.p2shaddressindex_to_emptyaddressdata,
|
||||
&mut self.p2traddressindex_to_addressdata,
|
||||
&mut self.p2traddressindex_to_emptyaddressdata,
|
||||
&mut self.p2wpkhaddressindex_to_addressdata,
|
||||
&mut self.p2wpkhaddressindex_to_emptyaddressdata,
|
||||
&mut self.p2wshaddressindex_to_addressdata,
|
||||
&mut self.p2wshaddressindex_to_emptyaddressdata,
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
use std::ops::{Add, Div};
|
||||
|
||||
pub fn get_percentile<T>(sorted: &[T], percentile: f64) -> T
|
||||
where
|
||||
T: Clone + Div<usize, Output = T> + Add<T, Output = T>,
|
||||
{
|
||||
let len = sorted.len();
|
||||
|
||||
if len == 0 {
|
||||
panic!();
|
||||
} else if len == 1 {
|
||||
sorted[0].clone()
|
||||
} else {
|
||||
let index = (len - 1) as f64 * percentile;
|
||||
|
||||
let fract = index.fract();
|
||||
|
||||
if fract != 0.0 {
|
||||
let left = sorted.get(index as usize).unwrap().clone();
|
||||
let right = sorted.get(index.ceil() as usize).unwrap().clone();
|
||||
left / 2 + right / 2
|
||||
} else {
|
||||
// dbg!(sorted.len(), index);
|
||||
sorted.get(index as usize).unwrap().clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{
|
||||
CheckedSub, DifficultyEpoch, HalvingEpoch, Height, StoredU32, StoredU64, StoredUsize,
|
||||
Timestamp, Version, Weight,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, AnyIterableVec, Computation, EagerVec, Format};
|
||||
|
||||
use super::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, StorableVecGeneatorOptions},
|
||||
indexes,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
pub height_to_interval: EagerVec<Height, Timestamp>,
|
||||
pub height_to_vbytes: EagerVec<Height, StoredU64>,
|
||||
pub difficultyepoch_to_timestamp: EagerVec<DifficultyEpoch, Timestamp>,
|
||||
pub halvingepoch_to_timestamp: EagerVec<HalvingEpoch, Timestamp>,
|
||||
pub timeindexes_to_timestamp: ComputedVecsFromDateIndex<Timestamp>,
|
||||
pub indexes_to_block_count: ComputedVecsFromHeight<StoredU32>,
|
||||
pub indexes_to_block_interval: ComputedVecsFromHeight<Timestamp>,
|
||||
pub indexes_to_block_size: ComputedVecsFromHeight<StoredUsize>,
|
||||
pub indexes_to_block_vbytes: ComputedVecsFromHeight<StoredU64>,
|
||||
pub indexes_to_block_weight: ComputedVecsFromHeight<Weight>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
version: Version,
|
||||
_computation: Computation,
|
||||
format: Format,
|
||||
) -> color_eyre::Result<Self> {
|
||||
Ok(Self {
|
||||
height_to_interval: EagerVec::forced_import(
|
||||
path,
|
||||
"interval",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
timeindexes_to_timestamp: ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
"timestamp",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_first(),
|
||||
)?,
|
||||
indexes_to_block_interval: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"block_interval",
|
||||
false,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_percentiles()
|
||||
.add_minmax()
|
||||
.add_average(),
|
||||
)?,
|
||||
indexes_to_block_count: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"block_count",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_block_weight: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"block_weight",
|
||||
false,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_block_size: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"block_size",
|
||||
false,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
height_to_vbytes: EagerVec::forced_import(
|
||||
path,
|
||||
"vbytes",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
indexes_to_block_vbytes: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"block_vbytes",
|
||||
false,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
difficultyepoch_to_timestamp: EagerVec::forced_import(
|
||||
path,
|
||||
"timestamp",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
halvingepoch_to_timestamp: EagerVec::forced_import(
|
||||
path,
|
||||
"timestamp",
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.timeindexes_to_timestamp.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.dateindex,
|
||||
&indexes.dateindex_to_date,
|
||||
|(di, d, ..)| (di, Timestamp::from(d)),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_count.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, indexer, _, starting_indexes, exit| {
|
||||
v.compute_range(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_weight,
|
||||
|h| (h, StoredU32::from(1_u32)),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
let mut height_to_timestamp_iter = indexer.vecs.height_to_timestamp.iter();
|
||||
self.height_to_interval.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_timestamp,
|
||||
|(height, timestamp, ..)| {
|
||||
let interval = height.decremented().map_or(Timestamp::ZERO, |prev_h| {
|
||||
let prev_timestamp = height_to_timestamp_iter.unwrap_get_inner(prev_h);
|
||||
timestamp
|
||||
.checked_sub(prev_timestamp)
|
||||
.unwrap_or(Timestamp::ZERO)
|
||||
});
|
||||
(height, interval)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_interval.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.height_to_interval),
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_weight.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&indexer.vecs.height_to_weight),
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_size.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&indexer.vecs.height_to_total_size),
|
||||
)?;
|
||||
|
||||
self.height_to_vbytes.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.height_to_weight,
|
||||
|(h, w, ..)| {
|
||||
(
|
||||
h,
|
||||
StoredU64::from(bitcoin::Weight::from(w).to_vbytes_floor()),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.indexes_to_block_vbytes.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.height_to_vbytes),
|
||||
)?;
|
||||
|
||||
let mut height_to_timestamp_iter = indexer.vecs.height_to_timestamp.iter();
|
||||
|
||||
self.difficultyepoch_to_timestamp.compute_transform(
|
||||
starting_indexes.difficultyepoch,
|
||||
&indexes.difficultyepoch_to_first_height,
|
||||
|(i, h, ..)| (i, height_to_timestamp_iter.unwrap_get_inner(h)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.halvingepoch_to_timestamp.compute_transform(
|
||||
starting_indexes.halvingepoch,
|
||||
&indexes.halvingepoch_to_first_height,
|
||||
|(i, h, ..)| (i, height_to_timestamp_iter.unwrap_get_inner(h)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
vec![
|
||||
&self.height_to_interval as &dyn AnyCollectableVec,
|
||||
&self.height_to_vbytes,
|
||||
&self.difficultyepoch_to_timestamp,
|
||||
&self.halvingepoch_to_timestamp,
|
||||
],
|
||||
self.timeindexes_to_timestamp.vecs(),
|
||||
self.indexes_to_block_count.vecs(),
|
||||
self.indexes_to_block_interval.vecs(),
|
||||
self.indexes_to_block_size.vecs(),
|
||||
self.indexes_to_block_vbytes.vecs(),
|
||||
self.indexes_to_block_weight.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,679 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Bitcoin, CheckedSub, Dollars, StoredF64, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, Computation, Format, VecIterator};
|
||||
|
||||
use crate::vecs::{
|
||||
fetched,
|
||||
grouped::{ComputedRatioVecsFromDateIndex, ComputedValueVecsFromHeight},
|
||||
stateful, transactions,
|
||||
};
|
||||
|
||||
use super::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecsFromHeight, StorableVecGeneatorOptions},
|
||||
indexes,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
pub indexes_to_coinblocks_created: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_coinblocks_stored: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_liveliness: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_vaultedness: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_activity_to_vaultedness_ratio: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_vaulted_supply: ComputedValueVecsFromHeight,
|
||||
pub indexes_to_active_supply: ComputedValueVecsFromHeight,
|
||||
pub indexes_to_thermo_cap: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_investor_cap: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_vaulted_cap: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_active_cap: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_vaulted_price: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_vaulted_price_ratio: ComputedRatioVecsFromDateIndex,
|
||||
pub indexes_to_active_price: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_active_price_ratio: ComputedRatioVecsFromDateIndex,
|
||||
pub indexes_to_true_market_mean: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_true_market_mean_ratio: ComputedRatioVecsFromDateIndex,
|
||||
pub indexes_to_cointime_value_destroyed: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_cointime_value_created: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_cointime_value_stored: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_cointime_price: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_cointime_cap: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_cointime_price_ratio: ComputedRatioVecsFromDateIndex,
|
||||
// pub indexes_to_thermo_cap_relative_to_investor_cap: ComputedValueVecsFromHeight,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
version: Version,
|
||||
_computation: Computation,
|
||||
format: Format,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let compute_dollars = fetched.is_some();
|
||||
|
||||
Ok(Self {
|
||||
indexes_to_coinblocks_created: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"coinblocks_created",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_coinblocks_stored: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"coinblocks_stored",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_liveliness: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"liveliness",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaultedness: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"vaultedness",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_activity_to_vaultedness_ratio: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"activity_to_vaultedness_ratio",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_supply: ComputedValueVecsFromHeight::forced_import(
|
||||
path,
|
||||
"vaulted_supply",
|
||||
true,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
compute_dollars,
|
||||
)?,
|
||||
indexes_to_active_supply: ComputedValueVecsFromHeight::forced_import(
|
||||
path,
|
||||
"active_supply",
|
||||
true,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
compute_dollars,
|
||||
)?,
|
||||
indexes_to_thermo_cap: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"thermo_cap",
|
||||
true,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_investor_cap: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"investor_cap",
|
||||
true,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_cap: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"vaulted_cap",
|
||||
true,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_active_cap: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"active_cap",
|
||||
true,
|
||||
version + VERSION + Version::ONE,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_price: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"vaulted_price",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_vaulted_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
"vaulted_price",
|
||||
false,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
indexes_to_active_price: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"active_price",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_active_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
"active_price",
|
||||
false,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
indexes_to_true_market_mean: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"true_market_mean",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_true_market_mean_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
"true_market_mean",
|
||||
false,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
indexes_to_cointime_value_destroyed: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"cointime_value_destroyed",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_cointime_value_created: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"cointime_value_created",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_cointime_value_stored: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"cointime_value_stored",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default()
|
||||
.add_sum()
|
||||
.add_cumulative(),
|
||||
)?,
|
||||
indexes_to_cointime_price: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"cointime_price",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_cointime_cap: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"cointime_cap",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_cointime_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
"cointime_price",
|
||||
false,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
transactions: &transactions::Vecs,
|
||||
stateful: &stateful::Vecs,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
let circulating_supply = &stateful.utxo_vecs.all.1.height_to_supply;
|
||||
|
||||
self.indexes_to_coinblocks_created.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
circulating_supply,
|
||||
|(i, v, ..)| (i, StoredF64::from(Bitcoin::from(v))),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
let indexes_to_coinblocks_destroyed =
|
||||
&stateful.utxo_vecs.all.1.indexes_to_coinblocks_destroyed;
|
||||
|
||||
self.indexes_to_coinblocks_stored.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
let mut coinblocks_destroyed_iter = indexes_to_coinblocks_destroyed
|
||||
.height
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.into_iter();
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
self.indexes_to_coinblocks_created.height.as_ref().unwrap(),
|
||||
|(i, created, ..)| {
|
||||
let destroyed = coinblocks_destroyed_iter.unwrap_get_inner(i);
|
||||
(i, created.checked_sub(destroyed).unwrap())
|
||||
},
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_liveliness.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
indexes_to_coinblocks_destroyed
|
||||
.height_extra
|
||||
.unwrap_cumulative(),
|
||||
self.indexes_to_coinblocks_created
|
||||
.height_extra
|
||||
.unwrap_cumulative(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
let liveliness = &self.indexes_to_liveliness;
|
||||
|
||||
self.indexes_to_vaultedness.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
liveliness.height.as_ref().unwrap(),
|
||||
|(i, v, ..)| (i, StoredF64::from(1.0).checked_sub(v).unwrap()),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
let vaultedness = &self.indexes_to_vaultedness;
|
||||
|
||||
self.indexes_to_activity_to_vaultedness_ratio.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
liveliness.height.as_ref().unwrap(),
|
||||
vaultedness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_vaulted_supply.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
fetched,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
circulating_supply,
|
||||
vaultedness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_active_supply.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
fetched,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
circulating_supply,
|
||||
liveliness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
if let Some(fetched) = fetched {
|
||||
let realized_cap = stateful
|
||||
.utxo_vecs
|
||||
.all
|
||||
.1
|
||||
.height_to_realized_cap
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
let realized_price = stateful
|
||||
.utxo_vecs
|
||||
.all
|
||||
.1
|
||||
.indexes_to_realized_price
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.height
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
self.indexes_to_thermo_cap.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
transactions
|
||||
.indexes_to_subsidy
|
||||
.dollars
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.height_extra
|
||||
.unwrap_cumulative(),
|
||||
|(i, v, ..)| (i, v),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_investor_cap.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_subtract(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
self.indexes_to_thermo_cap.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_vaulted_cap.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
self.indexes_to_vaultedness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_active_cap.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
self.indexes_to_liveliness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_vaulted_price.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
realized_price,
|
||||
self.indexes_to_vaultedness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_vaulted_price_ratio.compute_rest(
|
||||
indexer,
|
||||
indexes,
|
||||
fetched,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.indexes_to_vaulted_price.dateindex.unwrap_last()),
|
||||
)?;
|
||||
|
||||
self.indexes_to_active_price.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
realized_price,
|
||||
self.indexes_to_liveliness.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_active_price_ratio.compute_rest(
|
||||
indexer,
|
||||
indexes,
|
||||
fetched,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.indexes_to_active_price.dateindex.unwrap_last()),
|
||||
)?;
|
||||
|
||||
self.indexes_to_true_market_mean.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
self.indexes_to_investor_cap.height.as_ref().unwrap(),
|
||||
self.indexes_to_active_supply
|
||||
.bitcoin
|
||||
.height
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_true_market_mean_ratio.compute_rest(
|
||||
indexer,
|
||||
indexes,
|
||||
fetched,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.indexes_to_true_market_mean.dateindex.unwrap_last()),
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_value_destroyed.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
// TODO: Another example when the callback should be applied to each index, instead of to base then merging from more granular to less
|
||||
// The price taken won't be correct for time based indexes
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&fetched.chainindexes_to_close.height,
|
||||
indexes_to_coinblocks_destroyed.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_value_created.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&fetched.chainindexes_to_close.height,
|
||||
self.indexes_to_coinblocks_created.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_value_stored.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&fetched.chainindexes_to_close.height,
|
||||
self.indexes_to_coinblocks_stored.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_price.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
self.indexes_to_cointime_value_destroyed
|
||||
.height_extra
|
||||
.unwrap_cumulative(),
|
||||
self.indexes_to_coinblocks_stored
|
||||
.height_extra
|
||||
.unwrap_cumulative(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_cap.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, _, starting_indexes, exit| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
self.indexes_to_cointime_price.height.as_ref().unwrap(),
|
||||
circulating_supply,
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_cointime_price_ratio.compute_rest(
|
||||
indexer,
|
||||
indexes,
|
||||
fetched,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(self.indexes_to_cointime_price.dateindex.unwrap_last()),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.indexes_to_coinblocks_created.vecs(),
|
||||
self.indexes_to_coinblocks_stored.vecs(),
|
||||
self.indexes_to_liveliness.vecs(),
|
||||
self.indexes_to_vaultedness.vecs(),
|
||||
self.indexes_to_activity_to_vaultedness_ratio.vecs(),
|
||||
self.indexes_to_vaulted_supply.vecs(),
|
||||
self.indexes_to_active_supply.vecs(),
|
||||
self.indexes_to_thermo_cap.vecs(),
|
||||
self.indexes_to_investor_cap.vecs(),
|
||||
self.indexes_to_vaulted_cap.vecs(),
|
||||
self.indexes_to_active_cap.vecs(),
|
||||
self.indexes_to_vaulted_price.vecs(),
|
||||
self.indexes_to_vaulted_price_ratio.vecs(),
|
||||
self.indexes_to_active_price.vecs(),
|
||||
self.indexes_to_active_price_ratio.vecs(),
|
||||
self.indexes_to_true_market_mean.vecs(),
|
||||
self.indexes_to_true_market_mean_ratio.vecs(),
|
||||
self.indexes_to_cointime_price.vecs(),
|
||||
self.indexes_to_cointime_cap.vecs(),
|
||||
self.indexes_to_cointime_price_ratio.vecs(),
|
||||
self.indexes_to_cointime_value_destroyed.vecs(),
|
||||
self.indexes_to_cointime_value_created.vecs(),
|
||||
self.indexes_to_cointime_value_stored.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{StoredU8, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, AnyVec, Computation, Format};
|
||||
|
||||
use super::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecsFromHeight, StorableVecGeneatorOptions},
|
||||
indexes,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
pub constant_0: ComputedVecsFromHeight<StoredU8>,
|
||||
pub constant_1: ComputedVecsFromHeight<StoredU8>,
|
||||
pub constant_50: ComputedVecsFromHeight<StoredU8>,
|
||||
pub constant_100: ComputedVecsFromHeight<StoredU8>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
version: Version,
|
||||
_computation: Computation,
|
||||
format: Format,
|
||||
) -> color_eyre::Result<Self> {
|
||||
Ok(Self {
|
||||
constant_0: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"constant_0",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
constant_1: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"constant_1",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
constant_50: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"constant_50",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
constant_100: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"constant_100",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.constant_0.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, StoredU8::new(0)),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_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, StoredU8::new(1)),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_50.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, StoredU8::new(50)),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.constant_100.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, StoredU8::new(100)),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.constant_0.vecs(),
|
||||
self.constant_1.vecs(),
|
||||
self.constant_50.vecs(),
|
||||
self.constant_100.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,901 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{CheckedSub, Result, StoredUsize, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format, StoredIndex, StoredType};
|
||||
use color_eyre::eyre::ContextCompat;
|
||||
|
||||
use crate::utils::get_percentile;
|
||||
|
||||
use super::ComputedType;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ComputedVecBuilder<I, T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: ComputedType,
|
||||
{
|
||||
pub first: Option<Box<EagerVec<I, T>>>,
|
||||
pub average: Option<Box<EagerVec<I, T>>>,
|
||||
pub sum: Option<Box<EagerVec<I, T>>>,
|
||||
pub max: Option<Box<EagerVec<I, T>>>,
|
||||
pub _90p: Option<Box<EagerVec<I, T>>>,
|
||||
pub _75p: Option<Box<EagerVec<I, T>>>,
|
||||
pub median: Option<Box<EagerVec<I, T>>>,
|
||||
pub _25p: Option<Box<EagerVec<I, T>>>,
|
||||
pub _10p: Option<Box<EagerVec<I, T>>>,
|
||||
pub min: Option<Box<EagerVec<I, T>>>,
|
||||
pub last: Option<Box<EagerVec<I, T>>>,
|
||||
pub cumulative: Option<Box<EagerVec<I, T>>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<I, T> ComputedVecBuilder<I, T>
|
||||
where
|
||||
I: StoredIndex,
|
||||
T: ComputedType,
|
||||
{
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<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)
|
||||
}
|
||||
};
|
||||
|
||||
let s = Self {
|
||||
first: options.first.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&maybe_suffix("first"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
last: options.last.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(path, name, version + Version::ZERO, format).unwrap(),
|
||||
)
|
||||
}),
|
||||
min: options.min.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&maybe_suffix("min"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
max: options.max.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&maybe_suffix("max"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
median: options.median.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&maybe_suffix("median"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
average: options.average.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&maybe_suffix("average"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
sum: options.sum.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&(if !options.last && !options.average && !options.min && !options.max {
|
||||
name.to_string()
|
||||
} else {
|
||||
maybe_suffix("sum")
|
||||
}),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
cumulative: options.cumulative.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&suffix("cumulative"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
_90p: options._90p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&maybe_suffix("90p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
_75p: options._75p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&maybe_suffix("75p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
_25p: options._25p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&maybe_suffix("25p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
_10p: options._10p.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&maybe_suffix("10p"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}),
|
||||
};
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
pub fn extend(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
source: &impl AnyIterableVec<I, T>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
if self.cumulative.is_none() {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
self.validate_computed_version_or_reset_file(source.version())?;
|
||||
|
||||
let index = self.starting_index(max_from);
|
||||
|
||||
let cumulative_vec = self.cumulative.as_mut().unwrap();
|
||||
|
||||
let mut cumulative = index.decremented().map_or(T::from(0_usize), |index| {
|
||||
cumulative_vec.iter().unwrap_get_inner(index)
|
||||
});
|
||||
source.iter_at(index).try_for_each(|(i, v)| -> Result<()> {
|
||||
cumulative = cumulative.clone() + v.into_owned();
|
||||
cumulative_vec.forced_push_at(i, cumulative.clone(), exit)
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute<I2>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
source: &impl AnyIterableVec<I2, T>,
|
||||
first_indexes: &impl AnyIterableVec<I, I2>,
|
||||
count_indexes: &impl AnyIterableVec<I, StoredUsize>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
I2: StoredIndex + StoredType + CheckedSub<I2>,
|
||||
{
|
||||
self.validate_computed_version_or_reset_file(
|
||||
source.version() + first_indexes.version() + count_indexes.version(),
|
||||
)?;
|
||||
|
||||
let index = self.starting_index(max_from);
|
||||
|
||||
let mut count_indexes_iter = count_indexes.iter();
|
||||
let mut source_iter = source.iter();
|
||||
|
||||
let cumulative_vec = self.cumulative.as_mut();
|
||||
|
||||
let mut cumulative = cumulative_vec.map(|cumulative_vec| {
|
||||
index.decremented().map_or(T::from(0_usize), |index| {
|
||||
cumulative_vec.iter().unwrap_get_inner(index)
|
||||
})
|
||||
});
|
||||
|
||||
first_indexes
|
||||
.iter_at(index)
|
||||
.try_for_each(|(i, first_index)| -> Result<()> {
|
||||
let first_index = first_index.into_owned();
|
||||
|
||||
let count_index = count_indexes_iter.unwrap_get_inner(i);
|
||||
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
let f = source_iter
|
||||
.get_inner(first_index)
|
||||
.unwrap_or_else(|| T::from(0_usize));
|
||||
first.forced_push_at(index, f, exit)?;
|
||||
}
|
||||
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
let count_index = *count_index;
|
||||
if count_index == 0 {
|
||||
panic!("should compute last if count can be 0")
|
||||
}
|
||||
let last_index = first_index + (count_index - 1);
|
||||
let v = source_iter.unwrap_get_inner(last_index);
|
||||
// .context("to work")
|
||||
// .inspect_err(|_| {
|
||||
// dbg!(first_index, count_index, last_index);
|
||||
// })
|
||||
// .unwrap()
|
||||
// .into_owned();
|
||||
last.forced_push_at(index, v, exit)?;
|
||||
}
|
||||
|
||||
let needs_sum_or_cumulative = self.sum.is_some() || self.cumulative.is_some();
|
||||
let needs_average_sum_or_cumulative =
|
||||
needs_sum_or_cumulative || self.average.is_some();
|
||||
let needs_sorted = self.max.is_some()
|
||||
|| self._90p.is_some()
|
||||
|| self._75p.is_some()
|
||||
|| self.median.is_some()
|
||||
|| self._25p.is_some()
|
||||
|| self._10p.is_some()
|
||||
|| self.min.is_some();
|
||||
let needs_values = needs_sorted || needs_average_sum_or_cumulative;
|
||||
|
||||
if needs_values {
|
||||
source_iter.set(first_index);
|
||||
let mut values = (&mut source_iter)
|
||||
.take(*count_index)
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if needs_sorted {
|
||||
values.sort_unstable();
|
||||
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
max.forced_push_at(
|
||||
i,
|
||||
values
|
||||
.last()
|
||||
.context("expect some")
|
||||
.inspect_err(|_| {
|
||||
dbg!(
|
||||
&values,
|
||||
max.path(),
|
||||
first_indexes.name(),
|
||||
first_index,
|
||||
count_indexes.name(),
|
||||
count_index,
|
||||
source.len(),
|
||||
source.name()
|
||||
);
|
||||
})
|
||||
.unwrap()
|
||||
.clone(),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some(_90p) = self._90p.as_mut() {
|
||||
_90p.forced_push_at(i, 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)?;
|
||||
}
|
||||
|
||||
if let Some(median) = self.median.as_mut() {
|
||||
median.forced_push_at(i, 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)?;
|
||||
}
|
||||
|
||||
if let Some(_10p) = self._10p.as_mut() {
|
||||
_10p.forced_push_at(i, get_percentile(&values, 0.10), exit)?;
|
||||
}
|
||||
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
min.forced_push_at(i, values.first().unwrap().clone(), exit)?;
|
||||
}
|
||||
}
|
||||
|
||||
if needs_average_sum_or_cumulative {
|
||||
let len = values.len();
|
||||
let sum = values.into_iter().fold(T::from(0), |a, b| a + b);
|
||||
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
let avg = sum.clone() / len;
|
||||
average.forced_push_at(i, avg, exit)?;
|
||||
}
|
||||
|
||||
if needs_sum_or_cumulative {
|
||||
if let Some(sum_vec) = self.sum.as_mut() {
|
||||
sum_vec.forced_push_at(i, sum.clone(), exit)?;
|
||||
}
|
||||
|
||||
if let Some(cumulative_vec) = self.cumulative.as_mut() {
|
||||
let t = cumulative.as_ref().unwrap().clone() + sum;
|
||||
cumulative.replace(t.clone());
|
||||
cumulative_vec.forced_push_at(i, t, exit)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn from_aligned<I2>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
source: &ComputedVecBuilder<I2, T>,
|
||||
first_indexes: &impl AnyIterableVec<I, I2>,
|
||||
count_indexes: &impl AnyIterableVec<I, StoredUsize>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
I2: StoredIndex + StoredType + CheckedSub<I2>,
|
||||
{
|
||||
if self._90p.is_some()
|
||||
|| self._75p.is_some()
|
||||
|| self.median.is_some()
|
||||
|| self._25p.is_some()
|
||||
|| self._10p.is_some()
|
||||
{
|
||||
panic!("unsupported");
|
||||
}
|
||||
|
||||
self.validate_computed_version_or_reset_file(
|
||||
VERSION + first_indexes.version() + count_indexes.version(),
|
||||
)?;
|
||||
|
||||
let index = self.starting_index(max_from);
|
||||
|
||||
let mut count_indexes_iter = count_indexes.iter();
|
||||
|
||||
let mut source_first_iter = source.first.as_ref().map(|f| f.iter());
|
||||
let mut source_last_iter = source.last.as_ref().map(|f| f.iter());
|
||||
let mut source_max_iter = source.max.as_ref().map(|f| f.iter());
|
||||
let mut source_min_iter = source.min.as_ref().map(|f| f.iter());
|
||||
let mut source_average_iter = source.average.as_ref().map(|f| f.iter());
|
||||
let mut source_sum_iter = source.sum.as_ref().map(|f| f.iter());
|
||||
|
||||
let mut cumulative = self.cumulative.as_mut().map(|cumulative_vec| {
|
||||
index.decremented().map_or(T::from(0_usize), |index| {
|
||||
cumulative_vec.iter().unwrap_get_inner(index)
|
||||
})
|
||||
});
|
||||
|
||||
first_indexes
|
||||
.iter_at(index)
|
||||
.try_for_each(|(i, first_index, ..)| -> Result<()> {
|
||||
let first_index = first_index.into_owned();
|
||||
|
||||
let count_index = count_indexes_iter.unwrap_get_inner(i);
|
||||
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
let v = source_first_iter
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.unwrap_get_inner(first_index);
|
||||
first.forced_push_at(index, v, exit)?;
|
||||
}
|
||||
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
let count_index = *count_index;
|
||||
if count_index == 0 {
|
||||
panic!("should compute last if count can be 0")
|
||||
}
|
||||
let last_index = first_index + (count_index - 1);
|
||||
let v = source_last_iter
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.unwrap_get_inner(last_index);
|
||||
last.forced_push_at(index, v, exit)?;
|
||||
}
|
||||
|
||||
let needs_sum_or_cumulative = self.sum.is_some() || self.cumulative.is_some();
|
||||
let needs_average_sum_or_cumulative =
|
||||
needs_sum_or_cumulative || self.average.is_some();
|
||||
let needs_sorted = self.max.is_some() || self.min.is_some();
|
||||
let needs_values = needs_sorted || needs_average_sum_or_cumulative;
|
||||
|
||||
if needs_values {
|
||||
if needs_sorted {
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
let source_max_iter = source_max_iter.as_mut().unwrap();
|
||||
source_max_iter.set(first_index);
|
||||
let mut values = source_max_iter
|
||||
.take(*count_index)
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
values.sort_unstable();
|
||||
max.forced_push_at(i, values.last().unwrap().clone(), exit)?;
|
||||
}
|
||||
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
let source_min_iter = source_min_iter.as_mut().unwrap();
|
||||
source_min_iter.set(first_index);
|
||||
let mut values = source_min_iter
|
||||
.take(*count_index)
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
values.sort_unstable();
|
||||
min.forced_push_at(i, values.first().unwrap().clone(), exit)?;
|
||||
}
|
||||
}
|
||||
|
||||
if needs_average_sum_or_cumulative {
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
let source_average_iter = source_average_iter.as_mut().unwrap();
|
||||
source_average_iter.set(first_index);
|
||||
let values = source_average_iter
|
||||
.take(*count_index)
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let len = values.len();
|
||||
let cumulative = values.into_iter().fold(T::from(0), |a, b| a + b);
|
||||
// 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)?;
|
||||
}
|
||||
|
||||
if needs_sum_or_cumulative {
|
||||
let source_sum_iter = source_sum_iter.as_mut().unwrap();
|
||||
source_sum_iter.set(first_index);
|
||||
let values = source_sum_iter
|
||||
.take(*count_index)
|
||||
.map(|(_, v)| v.into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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.clone(), exit)?;
|
||||
}
|
||||
|
||||
if let Some(cumulative_vec) = self.cumulative.as_mut() {
|
||||
let t = cumulative.as_ref().unwrap().clone() + sum;
|
||||
cumulative.replace(t.clone());
|
||||
cumulative_vec.forced_push_at(i, t, exit)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.safe_flush(exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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) -> &EagerVec<I, T> {
|
||||
self.first.as_ref().unwrap()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn unwrap_average(&self) -> &EagerVec<I, T> {
|
||||
self.average.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_sum(&self) -> &EagerVec<I, T> {
|
||||
self.sum.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_max(&self) -> &EagerVec<I, T> {
|
||||
self.max.as_ref().unwrap()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn unwrap_90p(&self) -> &EagerVec<I, T> {
|
||||
self._90p.as_ref().unwrap()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn unwrap_75p(&self) -> &EagerVec<I, T> {
|
||||
self._75p.as_ref().unwrap()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn unwrap_median(&self) -> &EagerVec<I, T> {
|
||||
self.median.as_ref().unwrap()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn unwrap_25p(&self) -> &EagerVec<I, T> {
|
||||
self._25p.as_ref().unwrap()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn unwrap_10p(&self) -> &EagerVec<I, T> {
|
||||
self._10p.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_min(&self) -> &EagerVec<I, T> {
|
||||
self.min.as_ref().unwrap()
|
||||
}
|
||||
pub fn unwrap_last(&self) -> &EagerVec<I, T> {
|
||||
self.last.as_ref().unwrap()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn unwrap_cumulative(&self) -> &EagerVec<I, T> {
|
||||
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(median) = self.median.as_ref() {
|
||||
v.push(median.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());
|
||||
}
|
||||
if let Some(_90p) = self._90p.as_ref() {
|
||||
v.push(_90p.as_ref());
|
||||
}
|
||||
if let Some(_75p) = self._75p.as_ref() {
|
||||
v.push(_75p.as_ref());
|
||||
}
|
||||
if let Some(_25p) = self._25p.as_ref() {
|
||||
v.push(_25p.as_ref());
|
||||
}
|
||||
if let Some(_10p) = self._10p.as_ref() {
|
||||
v.push(_10p.as_ref());
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
pub fn safe_flush(&mut self, exit: &Exit) -> Result<()> {
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
first.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
last.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
min.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
max.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(median) = self.median.as_mut() {
|
||||
median.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
average.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(sum) = self.sum.as_mut() {
|
||||
sum.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(cumulative) = self.cumulative.as_mut() {
|
||||
cumulative.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(_90p) = self._90p.as_mut() {
|
||||
_90p.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(_75p) = self._75p.as_mut() {
|
||||
_75p.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(_25p) = self._25p.as_mut() {
|
||||
_25p.safe_flush(exit)?;
|
||||
}
|
||||
if let Some(_10p) = self._10p.as_mut() {
|
||||
_10p.safe_flush(exit)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_computed_version_or_reset_file(&mut self, version: Version) -> Result<()> {
|
||||
if let Some(first) = self.first.as_mut() {
|
||||
first.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(last) = self.last.as_mut() {
|
||||
last.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(min) = self.min.as_mut() {
|
||||
min.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(max) = self.max.as_mut() {
|
||||
max.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(median) = self.median.as_mut() {
|
||||
median.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(average) = self.average.as_mut() {
|
||||
average.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(sum) = self.sum.as_mut() {
|
||||
sum.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(cumulative) = self.cumulative.as_mut() {
|
||||
cumulative.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_90p) = self._90p.as_mut() {
|
||||
_90p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_75p) = self._75p.as_mut() {
|
||||
_75p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_25p) = self._25p.as_mut() {
|
||||
_25p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
if let Some(_10p) = self._10p.as_mut() {
|
||||
_10p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct StorableVecGeneatorOptions {
|
||||
average: bool,
|
||||
sum: bool,
|
||||
max: bool,
|
||||
_90p: bool,
|
||||
_75p: bool,
|
||||
median: bool,
|
||||
_25p: bool,
|
||||
_10p: bool,
|
||||
min: bool,
|
||||
first: bool,
|
||||
last: bool,
|
||||
cumulative: bool,
|
||||
}
|
||||
|
||||
impl StorableVecGeneatorOptions {
|
||||
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
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn add_median(mut self) -> Self {
|
||||
self.median = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_average(mut self) -> Self {
|
||||
self.average = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_sum(mut self) -> Self {
|
||||
self.sum = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn add_90p(mut self) -> Self {
|
||||
self._90p = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn add_75p(mut self) -> Self {
|
||||
self._75p = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn add_25p(mut self) -> Self {
|
||||
self._25p = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn add_10p(mut self) -> Self {
|
||||
self._10p = 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_median(mut self) -> Self {
|
||||
self.median = 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_90p(mut self) -> Self {
|
||||
self._90p = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_75p(mut self) -> Self {
|
||||
self._75p = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_25p(mut self) -> Self {
|
||||
self._25p = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn rm_10p(mut self) -> Self {
|
||||
self._10p = 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 add_percentiles(mut self) -> Self {
|
||||
self._90p = true;
|
||||
self._75p = true;
|
||||
self.median = true;
|
||||
self._25p = true;
|
||||
self._10p = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn remove_percentiles(mut self) -> Self {
|
||||
self._90p = false;
|
||||
self._75p = false;
|
||||
self.median = false;
|
||||
self._25p = false;
|
||||
self._10p = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_only_one_active(&self) -> bool {
|
||||
[
|
||||
self.average,
|
||||
self.sum,
|
||||
self.max,
|
||||
self._90p,
|
||||
self._75p,
|
||||
self.median,
|
||||
self._25p,
|
||||
self._10p,
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{
|
||||
DateIndex, DecadeIndex, MonthIndex, QuarterIndex, Result, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format};
|
||||
|
||||
use crate::vecs::{Indexes, indexes};
|
||||
|
||||
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecsFromDateIndex<T>
|
||||
where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub dateindex: Option<EagerVec<DateIndex, T>>,
|
||||
pub dateindex_extra: ComputedVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T>,
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedVecsFromDateIndex<T>
|
||||
where
|
||||
T: ComputedType,
|
||||
{
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let dateindex = compute_source.then(|| {
|
||||
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format).unwrap()
|
||||
});
|
||||
|
||||
let dateindex_extra = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options.copy_self_extra(),
|
||||
)?;
|
||||
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
Ok(Self {
|
||||
dateindex,
|
||||
dateindex_extra,
|
||||
weekindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all<F>(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut EagerVec<DateIndex, T>,
|
||||
&Indexer,
|
||||
&indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
self.dateindex.as_mut().unwrap(),
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let dateindex: Option<&EagerVec<DateIndex, T>> = None;
|
||||
self.compute_rest(indexes, starting_indexes, exit, dateindex)
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
dateindex: Option<&impl AnyIterableVec<DateIndex, T>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
if let Some(dateindex) = dateindex {
|
||||
self.dateindex_extra
|
||||
.extend(starting_indexes.dateindex, dateindex, exit)?;
|
||||
|
||||
self.weekindex.compute(
|
||||
starting_indexes.weekindex,
|
||||
dateindex,
|
||||
&indexes.weekindex_to_first_dateindex,
|
||||
&indexes.weekindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.compute(
|
||||
starting_indexes.monthindex,
|
||||
dateindex,
|
||||
&indexes.monthindex_to_first_dateindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
} else {
|
||||
let dateindex = self.dateindex.as_ref().unwrap();
|
||||
|
||||
self.dateindex_extra
|
||||
.extend(starting_indexes.dateindex, dateindex, exit)?;
|
||||
|
||||
self.weekindex.compute(
|
||||
starting_indexes.weekindex,
|
||||
dateindex,
|
||||
&indexes.weekindex_to_first_dateindex,
|
||||
&indexes.weekindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.compute(
|
||||
starting_indexes.monthindex,
|
||||
dateindex,
|
||||
&indexes.monthindex_to_first_dateindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
self.quarterindex.from_aligned(
|
||||
starting_indexes.quarterindex,
|
||||
&self.monthindex,
|
||||
&indexes.quarterindex_to_first_monthindex,
|
||||
&indexes.quarterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.from_aligned(
|
||||
starting_indexes.yearindex,
|
||||
&self.monthindex,
|
||||
&indexes.yearindex_to_first_monthindex,
|
||||
&indexes.yearindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.from_aligned(
|
||||
starting_indexes.decadeindex,
|
||||
&self.yearindex,
|
||||
&indexes.decadeindex_to_first_yearindex,
|
||||
&indexes.decadeindex_to_yearindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.dateindex
|
||||
.as_ref()
|
||||
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
|
||||
self.dateindex_extra.vecs(),
|
||||
self.weekindex.vecs(),
|
||||
self.monthindex.vecs(),
|
||||
self.quarterindex.vecs(),
|
||||
self.yearindex.vecs(),
|
||||
self.decadeindex.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{
|
||||
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, Result, Version,
|
||||
WeekIndex, YearIndex,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format};
|
||||
|
||||
use crate::vecs::{Indexes, indexes};
|
||||
|
||||
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecsFromHeight<T>
|
||||
where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub height: Option<EagerVec<Height, T>>,
|
||||
pub height_extra: ComputedVecBuilder<Height, T>,
|
||||
pub dateindex: ComputedVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T>,
|
||||
pub difficultyepoch: ComputedVecBuilder<DifficultyEpoch, T>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T>,
|
||||
// 6 months
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T>,
|
||||
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedVecsFromHeight<T>
|
||||
where
|
||||
T: ComputedType + Ord + From<f64>,
|
||||
f64: From<T>,
|
||||
{
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let height = compute_source.then(|| {
|
||||
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format).unwrap()
|
||||
});
|
||||
|
||||
let height_extra = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options.copy_self_extra(),
|
||||
)?;
|
||||
|
||||
let dateindex = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?;
|
||||
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
Ok(Self {
|
||||
height,
|
||||
height_extra,
|
||||
dateindex,
|
||||
weekindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
difficultyepoch: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(path, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all<F>(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(&mut EagerVec<Height, T>, &Indexer, &indexes::Vecs, &Indexes, &Exit) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
self.height.as_mut().unwrap(),
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let height: Option<&EagerVec<Height, T>> = None;
|
||||
self.compute_rest(indexes, starting_indexes, exit, height)
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
height_vec: Option<&impl AnyIterableVec<Height, T>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
if let Some(height) = height_vec {
|
||||
self.height_extra
|
||||
.extend(starting_indexes.height, height, exit)?;
|
||||
|
||||
self.dateindex.compute(
|
||||
starting_indexes.dateindex,
|
||||
height,
|
||||
&indexes.dateindex_to_first_height,
|
||||
&indexes.dateindex_to_height_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.difficultyepoch.compute(
|
||||
starting_indexes.difficultyepoch,
|
||||
height,
|
||||
&indexes.difficultyepoch_to_first_height,
|
||||
&indexes.difficultyepoch_to_height_count,
|
||||
exit,
|
||||
)?;
|
||||
} else {
|
||||
let height = self.height.as_ref().unwrap();
|
||||
|
||||
self.height_extra
|
||||
.extend(starting_indexes.height, height, exit)?;
|
||||
|
||||
self.dateindex.compute(
|
||||
starting_indexes.dateindex,
|
||||
height,
|
||||
&indexes.dateindex_to_first_height,
|
||||
&indexes.dateindex_to_height_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.difficultyepoch.compute(
|
||||
starting_indexes.difficultyepoch,
|
||||
height,
|
||||
&indexes.difficultyepoch_to_first_height,
|
||||
&indexes.difficultyepoch_to_height_count,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
self.weekindex.from_aligned(
|
||||
starting_indexes.weekindex,
|
||||
&self.dateindex,
|
||||
&indexes.weekindex_to_first_dateindex,
|
||||
&indexes.weekindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.from_aligned(
|
||||
starting_indexes.monthindex,
|
||||
&self.dateindex,
|
||||
&indexes.monthindex_to_first_dateindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex.from_aligned(
|
||||
starting_indexes.quarterindex,
|
||||
&self.monthindex,
|
||||
&indexes.quarterindex_to_first_monthindex,
|
||||
&indexes.quarterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.from_aligned(
|
||||
starting_indexes.yearindex,
|
||||
&self.monthindex,
|
||||
&indexes.yearindex_to_first_monthindex,
|
||||
&indexes.yearindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.from_aligned(
|
||||
starting_indexes.decadeindex,
|
||||
&self.yearindex,
|
||||
&indexes.decadeindex_to_first_yearindex,
|
||||
&indexes.decadeindex_to_yearindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.height
|
||||
.as_ref()
|
||||
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
|
||||
self.height_extra.vecs(),
|
||||
self.dateindex.vecs(),
|
||||
self.weekindex.vecs(),
|
||||
self.difficultyepoch.vecs(),
|
||||
self.monthindex.vecs(),
|
||||
self.quarterindex.vecs(),
|
||||
self.yearindex.vecs(),
|
||||
// self.halvingepoch.vecs(),
|
||||
self.decadeindex.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Difficultyepoch, Height};
|
||||
use brk_core::{DifficultyEpoch, Height, Result, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyStoredVec, Compressed, Result, Version};
|
||||
use brk_vec::{AnyCollectableVec, EagerVec, Format};
|
||||
|
||||
use crate::storage::vecs::{Indexes, base::ComputedVec, indexes};
|
||||
use crate::vecs::{indexes, Indexes};
|
||||
|
||||
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
|
||||
|
||||
@@ -14,9 +14,9 @@ pub struct ComputedVecsFromHeightStrict<T>
|
||||
where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub height: ComputedVec<Height, T>,
|
||||
pub height: EagerVec<Height, T>,
|
||||
pub height_extra: ComputedVecBuilder<Height, T>,
|
||||
pub difficultyepoch: ComputedVecBuilder<Difficultyepoch, T>,
|
||||
pub difficultyepoch: ComputedVecBuilder<DifficultyEpoch, T>,
|
||||
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
|
||||
}
|
||||
|
||||
@@ -31,22 +31,17 @@ where
|
||||
path: &Path,
|
||||
name: &str,
|
||||
version: Version,
|
||||
compressed: Compressed,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let version = VERSION + version;
|
||||
|
||||
let height = ComputedVec::forced_import(
|
||||
&path.join(format!("height_to_{name}")),
|
||||
version,
|
||||
compressed,
|
||||
)?;
|
||||
let height =
|
||||
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format)?;
|
||||
|
||||
let height_extra = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version,
|
||||
compressed,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options.copy_self_extra(),
|
||||
)?;
|
||||
|
||||
@@ -56,52 +51,52 @@ where
|
||||
height,
|
||||
height_extra,
|
||||
difficultyepoch: ComputedVecBuilder::forced_import(
|
||||
path, name, version, compressed, options,
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(path, name, version, compressed, options)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(path, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute<F>(
|
||||
&mut self,
|
||||
indexer: &mut Indexer,
|
||||
indexes: &mut indexes::Vecs,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut ComputedVec<Height, T>,
|
||||
&mut Indexer,
|
||||
&mut indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
F: FnMut(&mut EagerVec<Height, T>, &Indexer, &indexes::Vecs, &Indexes, &Exit) -> Result<()>,
|
||||
{
|
||||
compute(&mut self.height, indexer, indexes, starting_indexes, exit)?;
|
||||
|
||||
self.height_extra
|
||||
.extend(starting_indexes.height, self.height.mut_vec(), exit)?;
|
||||
.extend(starting_indexes.height, &self.height, exit)?;
|
||||
|
||||
self.difficultyepoch.compute(
|
||||
starting_indexes.difficultyepoch,
|
||||
self.height.mut_vec(),
|
||||
indexes.difficultyepoch_to_first_height.mut_vec(),
|
||||
indexes.difficultyepoch_to_last_height.mut_vec(),
|
||||
&self.height,
|
||||
&indexes.difficultyepoch_to_first_height,
|
||||
&indexes.difficultyepoch_to_height_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn any_vecs(&self) -> Vec<&dyn AnyStoredVec> {
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
vec![self.height.any_vec()],
|
||||
self.height_extra.any_vecs(),
|
||||
self.difficultyepoch.any_vecs(),
|
||||
// self.halvingepoch.as_any_vecs(),
|
||||
vec![&self.height as &dyn AnyCollectableVec],
|
||||
self.height_extra.vecs(),
|
||||
self.difficultyepoch.vecs(),
|
||||
// self.halvingepoch.vecs(),
|
||||
]
|
||||
.concat()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,629 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{
|
||||
Bitcoin, DateIndex, DecadeIndex, DifficultyEpoch, Dollars, Height, MonthIndex, QuarterIndex,
|
||||
Result, Sats, TxIndex, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{
|
||||
AnyCollectableVec, AnyVec, CollectableVec, EagerVec, Format, StoredIndex, VecIterator,
|
||||
};
|
||||
|
||||
use crate::vecs::{Indexes, fetched, indexes};
|
||||
|
||||
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedVecsFromTxindex<T>
|
||||
where
|
||||
T: ComputedType + PartialOrd,
|
||||
{
|
||||
pub txindex: Option<Box<EagerVec<TxIndex, T>>>,
|
||||
pub height: ComputedVecBuilder<Height, T>,
|
||||
pub dateindex: ComputedVecBuilder<DateIndex, T>,
|
||||
pub weekindex: ComputedVecBuilder<WeekIndex, T>,
|
||||
pub difficultyepoch: ComputedVecBuilder<DifficultyEpoch, T>,
|
||||
pub monthindex: ComputedVecBuilder<MonthIndex, T>,
|
||||
pub quarterindex: ComputedVecBuilder<QuarterIndex, T>,
|
||||
pub yearindex: ComputedVecBuilder<YearIndex, T>,
|
||||
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
|
||||
pub decadeindex: ComputedVecBuilder<DecadeIndex, T>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedVecsFromTxindex<T>
|
||||
where
|
||||
T: ComputedType + Ord + From<f64>,
|
||||
f64: From<T>,
|
||||
{
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let txindex = compute_source.then(|| {
|
||||
Box::new(
|
||||
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format)
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let height = ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?;
|
||||
|
||||
let options = options.remove_percentiles();
|
||||
|
||||
Ok(Self {
|
||||
txindex,
|
||||
height,
|
||||
dateindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
weekindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
difficultyepoch: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
monthindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
quarterindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
yearindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
// halvingepoch: StorableVecGeneator::forced_import(path, name, version + VERSION + Version::ZERO, format, options)?,
|
||||
decadeindex: ComputedVecBuilder::forced_import(
|
||||
path,
|
||||
name,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
// #[allow(unused)]
|
||||
// pub fn compute_all<F>(
|
||||
// &mut self,
|
||||
// indexer: &Indexer,
|
||||
// indexes: &indexes::Vecs,
|
||||
// starting_indexes: &Indexes,
|
||||
// exit: &Exit,
|
||||
// mut compute: F,
|
||||
// ) -> color_eyre::Result<()>
|
||||
// where
|
||||
// F: FnMut(
|
||||
// &mut EagerVec<TxIndex, T>,
|
||||
// &Indexer,
|
||||
// &indexes::Vecs,
|
||||
// &Indexes,
|
||||
// &Exit,
|
||||
// ) -> Result<()>,
|
||||
// {
|
||||
// compute(
|
||||
// self.txindex.as_mut().unwrap(),
|
||||
// indexer,
|
||||
// indexes,
|
||||
// starting_indexes,
|
||||
// exit,
|
||||
// )?;
|
||||
|
||||
// let txindex: Option<&StoredVec<TxIndex, T>> = None;
|
||||
// self.compute_rest(indexer, indexes, starting_indexes, exit, txindex)?;
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
txindex: Option<&impl CollectableVec<TxIndex, T>>,
|
||||
) -> Result<()> {
|
||||
if let Some(txindex) = txindex {
|
||||
self.height.compute(
|
||||
starting_indexes.height,
|
||||
txindex,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
&indexes.height_to_txindex_count,
|
||||
exit,
|
||||
)?;
|
||||
} else {
|
||||
let txindex = self.txindex.as_ref().unwrap().as_ref();
|
||||
|
||||
self.height.compute(
|
||||
starting_indexes.height,
|
||||
txindex,
|
||||
&indexer.vecs.height_to_first_txindex,
|
||||
&indexes.height_to_txindex_count,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
self.compute_after_height(indexes, starting_indexes, exit)
|
||||
}
|
||||
|
||||
fn compute_after_height(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.dateindex.from_aligned(
|
||||
starting_indexes.dateindex,
|
||||
&self.height,
|
||||
&indexes.dateindex_to_first_height,
|
||||
&indexes.dateindex_to_height_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.weekindex.from_aligned(
|
||||
starting_indexes.weekindex,
|
||||
&self.dateindex,
|
||||
&indexes.weekindex_to_first_dateindex,
|
||||
&indexes.weekindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.monthindex.from_aligned(
|
||||
starting_indexes.monthindex,
|
||||
&self.dateindex,
|
||||
&indexes.monthindex_to_first_dateindex,
|
||||
&indexes.monthindex_to_dateindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.quarterindex.from_aligned(
|
||||
starting_indexes.quarterindex,
|
||||
&self.monthindex,
|
||||
&indexes.quarterindex_to_first_monthindex,
|
||||
&indexes.quarterindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.yearindex.from_aligned(
|
||||
starting_indexes.yearindex,
|
||||
&self.monthindex,
|
||||
&indexes.yearindex_to_first_monthindex,
|
||||
&indexes.yearindex_to_monthindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.decadeindex.from_aligned(
|
||||
starting_indexes.decadeindex,
|
||||
&self.yearindex,
|
||||
&indexes.decadeindex_to_first_yearindex,
|
||||
&indexes.decadeindex_to_yearindex_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.difficultyepoch.from_aligned(
|
||||
starting_indexes.difficultyepoch,
|
||||
&self.height,
|
||||
&indexes.difficultyepoch_to_first_height,
|
||||
&indexes.difficultyepoch_to_height_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.txindex
|
||||
.as_ref()
|
||||
.map_or(vec![], |v| vec![v.as_ref() as &dyn AnyCollectableVec]),
|
||||
self.height.vecs(),
|
||||
self.dateindex.vecs(),
|
||||
self.weekindex.vecs(),
|
||||
self.difficultyepoch.vecs(),
|
||||
self.monthindex.vecs(),
|
||||
self.quarterindex.vecs(),
|
||||
self.yearindex.vecs(),
|
||||
// self.halvingepoch.vecs(),
|
||||
self.decadeindex.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputedVecsFromTxindex<Bitcoin> {
|
||||
pub fn compute_rest_from_sats(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
sats: &ComputedVecsFromTxindex<Sats>,
|
||||
txindex: Option<&impl CollectableVec<TxIndex, Bitcoin>>,
|
||||
) -> Result<()> {
|
||||
let txindex_version = if let Some(txindex) = txindex {
|
||||
txindex.version()
|
||||
} else {
|
||||
self.txindex.as_ref().unwrap().as_ref().version()
|
||||
};
|
||||
|
||||
self.height
|
||||
.validate_computed_version_or_reset_file(txindex_version)?;
|
||||
|
||||
let starting_index = self.height.starting_index(starting_indexes.height);
|
||||
|
||||
(starting_index.unwrap_to_usize()..indexer.vecs.height_to_weight.len())
|
||||
.map(Height::from)
|
||||
.try_for_each(|height| -> Result<()> {
|
||||
if let Some(first) = self.height.first.as_mut() {
|
||||
first.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_first()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(average) = self.height.average.as_mut() {
|
||||
average.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_average()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(sum) = self.height.sum.as_mut() {
|
||||
sum.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_sum()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(max) = self.height.max.as_mut() {
|
||||
max.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_max()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(_90p) = self.height._90p.as_mut() {
|
||||
_90p.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_90p()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(_75p) = self.height._75p.as_mut() {
|
||||
_75p.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_75p()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(median) = self.height.median.as_mut() {
|
||||
median.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_median()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(_25p) = self.height._25p.as_mut() {
|
||||
_25p.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_25p()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(_10p) = self.height._10p.as_mut() {
|
||||
_10p.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_10p()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(min) = self.height.min.as_mut() {
|
||||
min.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_min()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(last) = self.height.last.as_mut() {
|
||||
last.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_last()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(cumulative) = self.height.cumulative.as_mut() {
|
||||
cumulative.forced_push_at(
|
||||
height,
|
||||
Bitcoin::from(
|
||||
sats.height
|
||||
.unwrap_cumulative()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.height.safe_flush(exit)?;
|
||||
|
||||
self.compute_after_height(indexes, starting_indexes, exit)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputedVecsFromTxindex<Dollars> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_from_bitcoin(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
bitcoin: &ComputedVecsFromTxindex<Bitcoin>,
|
||||
txindex: Option<&impl CollectableVec<TxIndex, Dollars>>,
|
||||
fetched: &fetched::Vecs,
|
||||
) -> Result<()> {
|
||||
let txindex_version = if let Some(txindex) = txindex {
|
||||
txindex.version()
|
||||
} else {
|
||||
self.txindex.as_ref().unwrap().as_ref().version()
|
||||
};
|
||||
|
||||
self.height
|
||||
.validate_computed_version_or_reset_file(txindex_version)?;
|
||||
|
||||
let starting_index = self.height.starting_index(starting_indexes.height);
|
||||
|
||||
let mut close_iter = fetched.chainindexes_to_close.height.into_iter();
|
||||
|
||||
(starting_index.unwrap_to_usize()..indexer.vecs.height_to_weight.len())
|
||||
.map(Height::from)
|
||||
.try_for_each(|height| -> Result<()> {
|
||||
let price = *close_iter.unwrap_get_inner(height);
|
||||
|
||||
if let Some(first) = self.height.first.as_mut() {
|
||||
first.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_first()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(average) = self.height.average.as_mut() {
|
||||
average.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_average()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(sum) = self.height.sum.as_mut() {
|
||||
sum.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_sum()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(max) = self.height.max.as_mut() {
|
||||
max.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_max()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(_90p) = self.height._90p.as_mut() {
|
||||
_90p.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_90p()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(_75p) = self.height._75p.as_mut() {
|
||||
_75p.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_75p()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(median) = self.height.median.as_mut() {
|
||||
median.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_median()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(_25p) = self.height._25p.as_mut() {
|
||||
_25p.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_25p()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(_10p) = self.height._10p.as_mut() {
|
||||
_10p.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_10p()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(min) = self.height.min.as_mut() {
|
||||
min.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_min()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(last) = self.height.last.as_mut() {
|
||||
last.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_last()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(cumulative) = self.height.cumulative.as_mut() {
|
||||
cumulative.forced_push_at(
|
||||
height,
|
||||
price
|
||||
* bitcoin
|
||||
.height
|
||||
.unwrap_cumulative()
|
||||
.into_iter()
|
||||
.unwrap_get_inner(height),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.height.safe_flush(exit)?;
|
||||
|
||||
self.compute_after_height(indexes, starting_indexes, exit)
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,21 @@ mod from_dateindex;
|
||||
mod from_height;
|
||||
mod from_height_strict;
|
||||
mod from_txindex;
|
||||
mod stored_type;
|
||||
mod ratio_from_dateindex;
|
||||
mod r#type;
|
||||
mod value_from_dateindex;
|
||||
mod value_from_height;
|
||||
mod value_from_txindex;
|
||||
mod value_height;
|
||||
|
||||
pub use builder::*;
|
||||
pub use from_dateindex::*;
|
||||
pub use from_height::*;
|
||||
pub use from_height_strict::*;
|
||||
pub use from_txindex::*;
|
||||
pub use stored_type::*;
|
||||
pub use ratio_from_dateindex::*;
|
||||
use r#type::*;
|
||||
pub use value_from_dateindex::*;
|
||||
pub use value_from_height::*;
|
||||
pub use value_from_txindex::*;
|
||||
pub use value_height::*;
|
||||
@@ -0,0 +1,176 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Bitcoin, DateIndex, Dollars, Result, Sats, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, CollectableVec, EagerVec, Format, StoredVec};
|
||||
|
||||
use crate::vecs::{Indexes, fetched, grouped::ComputedVecsFromDateIndex, indexes};
|
||||
|
||||
use super::StorableVecGeneatorOptions;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedValueVecsFromDateIndex {
|
||||
pub sats: ComputedVecsFromDateIndex<Sats>,
|
||||
pub bitcoin: ComputedVecsFromDateIndex<Bitcoin>,
|
||||
pub dollars: Option<ComputedVecsFromDateIndex<Dollars>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl ComputedValueVecsFromDateIndex {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
compute_dollars: bool,
|
||||
) -> color_eyre::Result<Self> {
|
||||
Ok(Self {
|
||||
sats: ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
name,
|
||||
compute_source,
|
||||
version + VERSION,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
bitcoin: ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
&format!("{name}_in_btc"),
|
||||
true,
|
||||
version + VERSION,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
dollars: compute_dollars.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
&format!("{name}_in_usd"),
|
||||
true,
|
||||
version + VERSION,
|
||||
format,
|
||||
options,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all<F>(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut EagerVec<DateIndex, Sats>,
|
||||
&Indexer,
|
||||
&indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
self.sats.dateindex.as_mut().unwrap(),
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let dateindex: Option<&StoredVec<DateIndex, Sats>> = None;
|
||||
self.compute_rest(indexer, indexes, fetched, starting_indexes, exit, dateindex)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
dateindex: Option<&impl CollectableVec<DateIndex, Sats>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
if let Some(dateindex) = dateindex {
|
||||
self.sats
|
||||
.compute_rest(indexes, starting_indexes, exit, Some(dateindex))?;
|
||||
|
||||
self.bitcoin.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, _, _, starting_indexes, exit| {
|
||||
v.compute_from_sats(starting_indexes.dateindex, dateindex, exit)
|
||||
},
|
||||
)?;
|
||||
} else {
|
||||
let dateindex: Option<&StoredVec<DateIndex, Sats>> = None;
|
||||
|
||||
self.sats
|
||||
.compute_rest(indexes, starting_indexes, exit, dateindex)?;
|
||||
|
||||
self.bitcoin.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, _, _, starting_indexes, exit| {
|
||||
v.compute_from_sats(
|
||||
starting_indexes.dateindex,
|
||||
self.sats.dateindex.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
let dateindex_to_bitcoin = self.bitcoin.dateindex.as_ref().unwrap();
|
||||
let dateindex_to_close = fetched
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.timeindexes_to_close
|
||||
.dateindex
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
if let Some(dollars) = self.dollars.as_mut() {
|
||||
dollars.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, _, _, starting_indexes, exit| {
|
||||
v.compute_from_bitcoin(
|
||||
starting_indexes.dateindex,
|
||||
dateindex_to_bitcoin,
|
||||
dateindex_to_close,
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.sats.vecs(),
|
||||
self.bitcoin.vecs(),
|
||||
self.dollars.as_ref().map_or(vec![], |v| v.vecs()),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Bitcoin, Dollars, Height, Result, Sats, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, CollectableVec, EagerVec, Format, StoredVec};
|
||||
|
||||
use crate::vecs::{Indexes, fetched, indexes};
|
||||
|
||||
use super::{ComputedVecsFromHeight, StorableVecGeneatorOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedValueVecsFromHeight {
|
||||
pub sats: ComputedVecsFromHeight<Sats>,
|
||||
pub bitcoin: ComputedVecsFromHeight<Bitcoin>,
|
||||
pub dollars: Option<ComputedVecsFromHeight<Dollars>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl ComputedValueVecsFromHeight {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
version: Version,
|
||||
format: Format,
|
||||
options: StorableVecGeneatorOptions,
|
||||
compute_dollars: bool,
|
||||
) -> color_eyre::Result<Self> {
|
||||
Ok(Self {
|
||||
sats: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
name,
|
||||
compute_source,
|
||||
version + VERSION,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
bitcoin: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
&format!("{name}_in_btc"),
|
||||
true,
|
||||
version + VERSION,
|
||||
format,
|
||||
options,
|
||||
)?,
|
||||
dollars: compute_dollars.then(|| {
|
||||
ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
&format!("{name}_in_usd"),
|
||||
true,
|
||||
version + VERSION,
|
||||
format,
|
||||
options,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all<F>(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut EagerVec<Height, Sats>,
|
||||
&Indexer,
|
||||
&indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
self.sats.height.as_mut().unwrap(),
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let height: Option<&StoredVec<Height, Sats>> = None;
|
||||
self.compute_rest(indexer, indexes, fetched, starting_indexes, exit, height)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
height: Option<&impl CollectableVec<Height, Sats>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
if let Some(height) = height {
|
||||
self.sats
|
||||
.compute_rest(indexes, starting_indexes, exit, Some(height))?;
|
||||
|
||||
self.bitcoin.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, _, _, starting_indexes, exit| {
|
||||
v.compute_from_sats(starting_indexes.height, height, exit)
|
||||
},
|
||||
)?;
|
||||
} else {
|
||||
let height: Option<&StoredVec<Height, Sats>> = None;
|
||||
|
||||
self.sats
|
||||
.compute_rest(indexes, starting_indexes, exit, height)?;
|
||||
|
||||
self.bitcoin.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, _, _, starting_indexes, exit| {
|
||||
v.compute_from_sats(
|
||||
starting_indexes.height,
|
||||
self.sats.height.as_ref().unwrap(),
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
let height_to_bitcoin = self.bitcoin.height.as_ref().unwrap();
|
||||
let height_to_close = &fetched.as_ref().unwrap().chainindexes_to_close.height;
|
||||
|
||||
if let Some(dollars) = self.dollars.as_mut() {
|
||||
dollars.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|v, _, _, starting_indexes, exit| {
|
||||
v.compute_from_bitcoin(
|
||||
starting_indexes.height,
|
||||
height_to_bitcoin,
|
||||
height_to_close,
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.sats.vecs(),
|
||||
self.bitcoin.vecs(),
|
||||
self.dollars.as_ref().map_or(vec![], |v| v.vecs()),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Bitcoin, Close, Dollars, Height, Sats, TxIndex, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{
|
||||
AnyCollectableVec, BoxedAnyIterableVec, CloneableAnyIterableVec, CollectableVec, Computation,
|
||||
ComputedVecFrom3, Format, LazyVecFrom1, StoredIndex, StoredVec,
|
||||
};
|
||||
|
||||
use crate::vecs::{Indexes, fetched, indexes};
|
||||
|
||||
use super::{ComputedVecsFromTxindex, StorableVecGeneatorOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedValueVecsFromTxindex {
|
||||
pub sats: ComputedVecsFromTxindex<Sats>,
|
||||
pub bitcoin_txindex: LazyVecFrom1<TxIndex, Bitcoin, TxIndex, Sats>,
|
||||
pub bitcoin: ComputedVecsFromTxindex<Bitcoin>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub dollars_txindex: Option<
|
||||
ComputedVecFrom3<
|
||||
TxIndex,
|
||||
Dollars,
|
||||
TxIndex,
|
||||
Bitcoin,
|
||||
TxIndex,
|
||||
Height,
|
||||
Height,
|
||||
Close<Dollars>,
|
||||
>,
|
||||
>,
|
||||
pub dollars: Option<ComputedVecsFromTxindex<Dollars>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl ComputedValueVecsFromTxindex {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
indexes: &indexes::Vecs,
|
||||
source: Option<BoxedAnyIterableVec<TxIndex, Sats>>,
|
||||
version: Version,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
options: StorableVecGeneatorOptions,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let compute_source = source.is_none();
|
||||
let compute_dollars = fetched.is_some();
|
||||
|
||||
let name_in_btc = format!("{name}_in_btc");
|
||||
let name_in_usd = format!("{name}_in_usd");
|
||||
|
||||
let sats = ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
name,
|
||||
compute_source,
|
||||
version + VERSION,
|
||||
format,
|
||||
options,
|
||||
)?;
|
||||
|
||||
let bitcoin_txindex = LazyVecFrom1::init(
|
||||
&name_in_btc,
|
||||
version + VERSION,
|
||||
source.map_or_else(|| sats.txindex.as_ref().unwrap().boxed_clone(), |s| s),
|
||||
|txindex: TxIndex, iter| {
|
||||
iter.next_at(txindex.unwrap_to_usize()).map(|(_, value)| {
|
||||
let sats = value.into_owned();
|
||||
Bitcoin::from(sats)
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let bitcoin = ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
&name_in_btc,
|
||||
false,
|
||||
version + VERSION,
|
||||
format,
|
||||
options,
|
||||
)?;
|
||||
|
||||
let dollars_txindex = fetched.map(|fetched| {
|
||||
ComputedVecFrom3::forced_import_or_init_from_3(
|
||||
computation,
|
||||
path,
|
||||
&name_in_usd,
|
||||
version + VERSION,
|
||||
format,
|
||||
bitcoin_txindex.boxed_clone(),
|
||||
indexes.txindex_to_height.boxed_clone(),
|
||||
fetched.chainindexes_to_close.height.boxed_clone(),
|
||||
|txindex: TxIndex,
|
||||
txindex_to_btc_iter,
|
||||
txindex_to_height_iter,
|
||||
height_to_close_iter| {
|
||||
let txindex = txindex.unwrap_to_usize();
|
||||
txindex_to_btc_iter.next_at(txindex).and_then(|(_, value)| {
|
||||
let btc = value.into_owned();
|
||||
txindex_to_height_iter
|
||||
.next_at(txindex)
|
||||
.and_then(|(_, value)| {
|
||||
let height = value.into_owned();
|
||||
height_to_close_iter
|
||||
.next_at(height.unwrap_to_usize())
|
||||
.map(|(_, close)| *close.into_owned() * btc)
|
||||
})
|
||||
})
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
sats,
|
||||
bitcoin_txindex,
|
||||
bitcoin,
|
||||
dollars_txindex,
|
||||
dollars: compute_dollars.then(|| {
|
||||
ComputedVecsFromTxindex::forced_import(
|
||||
path,
|
||||
&name_in_usd,
|
||||
false,
|
||||
version + VERSION,
|
||||
format,
|
||||
options,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
// pub fn compute_all<F>(
|
||||
// &mut self,
|
||||
// indexer: &Indexer,
|
||||
// indexes: &indexes::Vecs,
|
||||
// fetched: Option<&marketprice::Vecs>,
|
||||
// starting_indexes: &Indexes,
|
||||
// exit: &Exit,
|
||||
// mut compute: F,
|
||||
// ) -> color_eyre::Result<()>
|
||||
// where
|
||||
// F: FnMut(
|
||||
// &mut EagerVec<TxIndex, Sats>,
|
||||
// &Indexer,
|
||||
// &indexes::Vecs,
|
||||
// &Indexes,
|
||||
// &Exit,
|
||||
// ) -> Result<()>,
|
||||
// {
|
||||
// compute(
|
||||
// self.sats.txindex.as_mut().unwrap(),
|
||||
// indexer,
|
||||
// indexes,
|
||||
// starting_indexes,
|
||||
// exit,
|
||||
// )?;
|
||||
|
||||
// let txindex: Option<&StoredVec<TxIndex, Sats>> = None;
|
||||
// self.compute_rest(
|
||||
// indexer,
|
||||
// indexes,
|
||||
// fetched,
|
||||
// starting_indexes,
|
||||
// exit,
|
||||
// txindex,
|
||||
// )?;
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
txindex: Option<&impl CollectableVec<TxIndex, Sats>>,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
) -> color_eyre::Result<()> {
|
||||
if let Some(txindex) = txindex {
|
||||
self.sats
|
||||
.compute_rest(indexer, indexes, starting_indexes, exit, Some(txindex))?;
|
||||
} else {
|
||||
let txindex: Option<&StoredVec<TxIndex, Sats>> = None;
|
||||
self.sats
|
||||
.compute_rest(indexer, indexes, starting_indexes, exit, txindex)?;
|
||||
}
|
||||
|
||||
self.bitcoin.compute_rest_from_sats(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
&self.sats,
|
||||
Some(&self.bitcoin_txindex),
|
||||
)?;
|
||||
|
||||
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,
|
||||
starting_indexes,
|
||||
exit,
|
||||
&self.bitcoin,
|
||||
Some(dollars_txindex),
|
||||
fetched.as_ref().unwrap(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.sats.vecs(),
|
||||
vec![&self.bitcoin_txindex as &dyn AnyCollectableVec],
|
||||
self.bitcoin.vecs(),
|
||||
self.dollars_txindex
|
||||
.as_ref()
|
||||
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
|
||||
self.dollars.as_ref().map_or(vec![], |v| v.vecs()),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{Bitcoin, Dollars, Height, Result, Sats, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, CollectableVec, EagerVec, Format, StoredVec};
|
||||
|
||||
use crate::vecs::{Indexes, fetched, indexes};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComputedHeightValueVecs {
|
||||
pub sats: Option<EagerVec<Height, Sats>>,
|
||||
pub bitcoin: EagerVec<Height, Bitcoin>,
|
||||
pub dollars: Option<EagerVec<Height, Dollars>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl ComputedHeightValueVecs {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
name: &str,
|
||||
compute_source: bool,
|
||||
version: Version,
|
||||
format: Format,
|
||||
compute_dollars: bool,
|
||||
) -> color_eyre::Result<Self> {
|
||||
Ok(Self {
|
||||
sats: compute_source.then(|| {
|
||||
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format)
|
||||
.unwrap()
|
||||
}),
|
||||
bitcoin: EagerVec::forced_import(
|
||||
path,
|
||||
&format!("{name}_in_btc"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
dollars: compute_dollars.then(|| {
|
||||
EagerVec::forced_import(
|
||||
path,
|
||||
&format!("{name}_in_usd"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_all<F>(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
mut compute: F,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
F: FnMut(
|
||||
&mut EagerVec<Height, Sats>,
|
||||
&Indexer,
|
||||
&indexes::Vecs,
|
||||
&Indexes,
|
||||
&Exit,
|
||||
) -> Result<()>,
|
||||
{
|
||||
compute(
|
||||
self.sats.as_mut().unwrap(),
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let height: Option<&StoredVec<Height, Sats>> = None;
|
||||
self.compute_rest(fetched, starting_indexes, exit, height)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
height: Option<&impl CollectableVec<Height, Sats>>,
|
||||
) -> color_eyre::Result<()> {
|
||||
if let Some(height) = height {
|
||||
self.bitcoin
|
||||
.compute_from_sats(starting_indexes.height, height, exit)?;
|
||||
} else {
|
||||
self.bitcoin.compute_from_sats(
|
||||
starting_indexes.height,
|
||||
self.sats.as_ref().unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
let height_to_bitcoin = &self.bitcoin;
|
||||
let height_to_close = &fetched.as_ref().unwrap().chainindexes_to_close.height;
|
||||
|
||||
if let Some(dollars) = self.dollars.as_mut() {
|
||||
dollars.compute_from_bitcoin(
|
||||
starting_indexes.height,
|
||||
height_to_bitcoin,
|
||||
height_to_close,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
vec![&self.bitcoin as &dyn AnyCollectableVec],
|
||||
self.sats.as_ref().map_or(vec![], |v| vec![v]),
|
||||
self.dollars.as_ref().map_or(vec![], |v| vec![v]),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{DifficultyEpoch, HalvingEpoch, StoredF64, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, Computation, Format, VecIterator};
|
||||
|
||||
use super::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, StorableVecGeneatorOptions},
|
||||
indexes,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
pub indexes_to_difficulty: ComputedVecsFromHeight<StoredF64>,
|
||||
pub indexes_to_difficultyepoch: ComputedVecsFromDateIndex<DifficultyEpoch>,
|
||||
pub indexes_to_halvingepoch: ComputedVecsFromDateIndex<HalvingEpoch>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
version: Version,
|
||||
_computation: Computation,
|
||||
format: Format,
|
||||
) -> color_eyre::Result<Self> {
|
||||
Ok(Self {
|
||||
indexes_to_difficulty: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
"difficulty",
|
||||
false,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_difficultyepoch: ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
"difficultyepoch",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
indexes_to_halvingepoch: ComputedVecsFromDateIndex::forced_import(
|
||||
path,
|
||||
"halvingepoch",
|
||||
true,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
let mut height_to_difficultyepoch_iter = indexes.height_to_difficultyepoch.into_iter();
|
||||
self.indexes_to_difficultyepoch.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
let mut height_count_iter = indexes.dateindex_to_height_count.into_iter();
|
||||
vec.compute_transform(
|
||||
starting_indexes.dateindex,
|
||||
&indexes.dateindex_to_first_height,
|
||||
|(di, height, ..)| {
|
||||
(
|
||||
di,
|
||||
height_to_difficultyepoch_iter.unwrap_get_inner(
|
||||
height + (*height_count_iter.unwrap_get_inner(di) - 1),
|
||||
),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
let mut height_to_halvingepoch_iter = indexes.height_to_halvingepoch.into_iter();
|
||||
self.indexes_to_halvingepoch.compute_all(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|vec, _, indexes, starting_indexes, exit| {
|
||||
let mut height_count_iter = indexes.dateindex_to_height_count.into_iter();
|
||||
vec.compute_transform(
|
||||
starting_indexes.dateindex,
|
||||
&indexes.dateindex_to_first_height,
|
||||
|(di, height, ..)| {
|
||||
(
|
||||
di,
|
||||
height_to_halvingepoch_iter.unwrap_get_inner(
|
||||
height + (*height_count_iter.unwrap_get_inner(di) - 1),
|
||||
),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.indexes_to_difficulty.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&indexer.vecs.height_to_difficulty),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.indexes_to_difficulty.vecs(),
|
||||
self.indexes_to_difficultyepoch.vecs(),
|
||||
self.indexes_to_halvingepoch.vecs(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::Version;
|
||||
use brk_exit::Exit;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, Computation, Format};
|
||||
|
||||
pub mod blocks;
|
||||
pub mod cointime;
|
||||
pub mod constants;
|
||||
pub mod fetched;
|
||||
pub mod grouped;
|
||||
pub mod indexes;
|
||||
pub mod market;
|
||||
pub mod mining;
|
||||
pub mod stateful;
|
||||
pub mod transactions;
|
||||
|
||||
pub use indexes::Indexes;
|
||||
use log::info;
|
||||
|
||||
use crate::stores::Stores;
|
||||
|
||||
const VERSION: Version = Version::ONE;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
pub indexes: indexes::Vecs,
|
||||
pub constants: constants::Vecs,
|
||||
pub blocks: blocks::Vecs,
|
||||
pub mining: mining::Vecs,
|
||||
pub market: market::Vecs,
|
||||
pub transactions: transactions::Vecs,
|
||||
pub stateful: stateful::Vecs,
|
||||
pub fetched: Option<fetched::Vecs>,
|
||||
pub cointime: cointime::Vecs,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn import(
|
||||
path: &Path,
|
||||
version: Version,
|
||||
indexer: &Indexer,
|
||||
fetch: bool,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let indexes = indexes::Vecs::forced_import(
|
||||
path,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexer,
|
||||
computation,
|
||||
format,
|
||||
)?;
|
||||
|
||||
let fetched = fetch.then(|| {
|
||||
fetched::Vecs::forced_import(
|
||||
path,
|
||||
version + VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
blocks: blocks::Vecs::forced_import(
|
||||
path,
|
||||
version + VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
)?,
|
||||
mining: mining::Vecs::forced_import(
|
||||
path,
|
||||
version + VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
)?,
|
||||
constants: constants::Vecs::forced_import(
|
||||
path,
|
||||
version + VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
)?,
|
||||
market: market::Vecs::forced_import(
|
||||
path,
|
||||
version + VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
)?,
|
||||
stateful: stateful::Vecs::forced_import(
|
||||
path,
|
||||
version + VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
fetched.as_ref(),
|
||||
)?,
|
||||
transactions: transactions::Vecs::forced_import(
|
||||
path,
|
||||
version + VERSION + Version::ZERO,
|
||||
indexer,
|
||||
&indexes,
|
||||
computation,
|
||||
format,
|
||||
fetched.as_ref(),
|
||||
)?,
|
||||
cointime: cointime::Vecs::forced_import(
|
||||
path,
|
||||
version + VERSION + Version::ZERO,
|
||||
computation,
|
||||
format,
|
||||
fetched.as_ref(),
|
||||
)?,
|
||||
indexes,
|
||||
fetched,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
starting_indexes: brk_indexer::Indexes,
|
||||
fetcher: Option<&mut Fetcher>,
|
||||
exit: &Exit,
|
||||
stores: &mut Stores,
|
||||
) -> color_eyre::Result<()> {
|
||||
info!("Computing indexes...");
|
||||
let mut starting_indexes = self.indexes.compute(indexer, starting_indexes, exit)?;
|
||||
|
||||
info!("Computing constants...");
|
||||
self.constants
|
||||
.compute(indexer, &self.indexes, &starting_indexes, exit)?;
|
||||
|
||||
info!("Computing blocks...");
|
||||
self.blocks
|
||||
.compute(indexer, &self.indexes, &starting_indexes, exit)?;
|
||||
|
||||
info!("Computing mining...");
|
||||
self.mining
|
||||
.compute(indexer, &self.indexes, &starting_indexes, exit)?;
|
||||
|
||||
if let Some(fetched) = self.fetched.as_mut() {
|
||||
info!("Computing fetched...");
|
||||
fetched.compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
&starting_indexes,
|
||||
fetcher.unwrap(),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
info!("Computing transactions...");
|
||||
self.transactions.compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
&starting_indexes,
|
||||
self.fetched.as_ref(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
if let Some(fetched) = self.fetched.as_ref() {
|
||||
info!("Computing market...");
|
||||
self.market.compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
fetched,
|
||||
&mut self.transactions,
|
||||
&starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
info!("Computing stateful...");
|
||||
self.stateful.compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
&self.transactions,
|
||||
self.fetched.as_ref(),
|
||||
&self.market,
|
||||
&mut starting_indexes,
|
||||
exit,
|
||||
stores,
|
||||
)?;
|
||||
|
||||
self.cointime.compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
&starting_indexes,
|
||||
self.fetched.as_ref(),
|
||||
&self.transactions,
|
||||
&self.stateful,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.constants.vecs(),
|
||||
self.indexes.vecs(),
|
||||
self.blocks.vecs(),
|
||||
self.mining.vecs(),
|
||||
self.market.vecs(),
|
||||
self.transactions.vecs(),
|
||||
self.stateful.vecs(),
|
||||
self.cointime.vecs(),
|
||||
self.fetched.as_ref().map_or(vec![], |v| v.vecs()),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
use std::{ops::Deref, path::Path};
|
||||
|
||||
use brk_core::{Bitcoin, DateIndex, Dollars, Height, Result, StoredUsize, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{
|
||||
AnyCollectableVec, AnyIterableVec, AnyVec, Computation, EagerVec, Format, VecIterator,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
states::AddressCohortState,
|
||||
vecs::{
|
||||
Indexes, fetched,
|
||||
grouped::{ComputedVecsFromHeight, StorableVecGeneatorOptions},
|
||||
indexes, market,
|
||||
stateful::{
|
||||
common,
|
||||
r#trait::{CohortVecs, DynCohortVecs},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
starting_height: Height,
|
||||
|
||||
pub state: AddressCohortState,
|
||||
|
||||
pub inner: common::Vecs,
|
||||
|
||||
pub height_to_address_count: EagerVec<Height, StoredUsize>,
|
||||
pub indexes_to_address_count: ComputedVecsFromHeight<StoredUsize>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
cohort_name: Option<&str>,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
version: Version,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
states_path: &Path,
|
||||
compute_relative_to_all: bool,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let compute_dollars = fetched.is_some();
|
||||
|
||||
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,
|
||||
)?,
|
||||
height_to_address_count: EagerVec::forced_import(
|
||||
path,
|
||||
&suffix("address_count"),
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
)?,
|
||||
indexes_to_address_count: ComputedVecsFromHeight::forced_import(
|
||||
path,
|
||||
&suffix("address_count"),
|
||||
false,
|
||||
version + VERSION + Version::ZERO,
|
||||
format,
|
||||
StorableVecGeneatorOptions::default().add_last(),
|
||||
)?,
|
||||
inner: common::Vecs::forced_import(
|
||||
path,
|
||||
cohort_name,
|
||||
computation,
|
||||
format,
|
||||
version,
|
||||
fetched,
|
||||
compute_relative_to_all,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for Vecs {
|
||||
fn starting_height(&self) -> Height {
|
||||
[
|
||||
self.state.height().map_or(Height::MAX, |h| h.incremented()),
|
||||
self.height_to_address_count.len().into(),
|
||||
self.inner.starting_height(),
|
||||
]
|
||||
.into_iter()
|
||||
.min()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn init(&mut self, starting_height: Height) {
|
||||
if starting_height > self.starting_height() {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
self.starting_height = starting_height;
|
||||
|
||||
if let Some(prev_height) = starting_height.decremented() {
|
||||
self.state.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);
|
||||
}
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.height_to_address_count
|
||||
.validate_computed_version_or_reset_file(
|
||||
base_version + self.height_to_address_count.inner_version(),
|
||||
)?;
|
||||
|
||||
self.inner.validate_computed_versions(base_version)
|
||||
}
|
||||
|
||||
fn forced_pushed_at(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
if self.starting_height > height {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.height_to_address_count.forced_push_at(
|
||||
height,
|
||||
self.state.address_count.into(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.inner.forced_pushed_at(height, exit, &self.state.inner)
|
||||
}
|
||||
|
||||
fn compute_then_force_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Option<Dollars>,
|
||||
dateindex: Option<DateIndex>,
|
||||
date_price: Option<Option<Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_then_force_push_unrealized_states(
|
||||
height,
|
||||
height_price,
|
||||
dateindex,
|
||||
date_price,
|
||||
exit,
|
||||
&self.state.inner,
|
||||
)
|
||||
}
|
||||
|
||||
fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
self.height_to_address_count.safe_flush(exit)?;
|
||||
|
||||
self.inner
|
||||
.safe_flush_stateful_vecs(height, exit, &mut self.state.inner)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.indexes_to_address_count.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.height_to_address_count),
|
||||
)?;
|
||||
|
||||
self.inner
|
||||
.compute_rest_part1(indexer, indexes, fetched, starting_indexes, exit)
|
||||
}
|
||||
|
||||
fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
[
|
||||
self.inner.vecs(),
|
||||
self.indexes_to_address_count.vecs(),
|
||||
vec![&self.height_to_address_count],
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
|
||||
impl CohortVecs for Vecs {
|
||||
fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.height_to_address_count.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
others
|
||||
.iter()
|
||||
.map(|v| &v.height_to_address_count)
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice(),
|
||||
exit,
|
||||
)?;
|
||||
self.inner.compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| &v.inner).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
market: &market::Vecs,
|
||||
height_to_supply: &impl AnyIterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl AnyIterableVec<DateIndex, Bitcoin>,
|
||||
height_to_realized_cap: Option<&impl AnyIterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.inner.compute_rest_part2(
|
||||
indexer,
|
||||
indexes,
|
||||
fetched,
|
||||
starting_indexes,
|
||||
market,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Vecs {
|
||||
type Target = common::Vecs;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,508 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_core::{
|
||||
AddressGroups, ByAmountRange, ByGreatEqualAmount, ByLowerThanAmount, GroupFilter, Height,
|
||||
Result, Version,
|
||||
};
|
||||
use brk_exit::Exit;
|
||||
use brk_vec::{Computation, Format};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::vecs::{
|
||||
Indexes, fetched,
|
||||
stateful::{
|
||||
address_cohort,
|
||||
r#trait::{CohortVecs, DynCohortVecs},
|
||||
},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::new(0);
|
||||
|
||||
#[derive(Clone, Deref, DerefMut)]
|
||||
pub struct Vecs(AddressGroups<(GroupFilter, address_cohort::Vecs)>);
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
version: Version,
|
||||
_computation: Computation,
|
||||
format: Format,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
states_path: &Path,
|
||||
) -> color_eyre::Result<Self> {
|
||||
Ok(Self(
|
||||
AddressGroups {
|
||||
amount_range: ByAmountRange {
|
||||
_0sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_with_0sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1sat_to_10sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_1sat_under_10sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10sats_to_100sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_10sats_under_100sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_100sats_to_1k_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_100sats_under_1k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1k_sats_to_10k_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_1k_sats_under_10k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10k_sats_to_100k_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_10k_sats_under_100k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_100k_sats_to_1m_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_100k_sats_under_1m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1m_sats_to_10m_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_1m_sats_under_10m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10m_sats_to_1btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_10m_sats_under_1btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1btc_to_10btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_1btc_under_10btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10btc_to_100btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_10btc_under_100btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_100btc_to_1k_btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_100btc_under_1k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1k_btc_to_10k_btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_1k_btc_under_10k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10k_btc_to_100k_btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_10k_btc_under_100k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_100k_btc_or_more: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_100k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
},
|
||||
lt_amount: ByLowerThanAmount {
|
||||
_10sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_10sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_100sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_100sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1k_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_1k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10k_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_10k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_100k_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_100k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1m_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_1m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10m_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_10m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_1btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_10btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_100btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_100btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1k_btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_1k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10k_btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_10k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_100k_btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_under_100k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
},
|
||||
ge_amount: ByGreatEqualAmount {
|
||||
_1sat: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_1sat"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_10sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_100sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_100sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1k_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_1k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10k_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_10k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_100k_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_100k_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1m_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_1m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10m_sats: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_10m_sats"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_1btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_10btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_100btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_100btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_1k_btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_1k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
_10k_btc: address_cohort::Vecs::forced_import(
|
||||
path,
|
||||
Some("addrs_above_10k_btc"),
|
||||
_computation,
|
||||
format,
|
||||
version + VERSION + Version::ZERO,
|
||||
fetched,
|
||||
states_path,
|
||||
true,
|
||||
)?,
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn compute_overlapping_vecs(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let by_size_range = self.0.amount_range.as_vec();
|
||||
|
||||
[
|
||||
self.0
|
||||
.ge_amount
|
||||
.as_mut_vec()
|
||||
.into_iter()
|
||||
.map(|(filter, vecs)| {
|
||||
(
|
||||
vecs,
|
||||
by_size_range
|
||||
.into_iter()
|
||||
.filter(|(other, _)| filter.includes(other))
|
||||
.map(|(_, v)| v)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
self.0
|
||||
.lt_amount
|
||||
.as_mut_vec()
|
||||
.into_iter()
|
||||
.map(|(filter, vecs)| {
|
||||
(
|
||||
vecs,
|
||||
by_size_range
|
||||
.into_iter()
|
||||
.filter(|(other, _)| filter.includes(other))
|
||||
.map(|(_, v)| v)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
]
|
||||
.into_par_iter()
|
||||
.flatten()
|
||||
.try_for_each(|(vecs, stateful)| {
|
||||
vecs.compute_from_stateful(starting_indexes, &stateful, exit)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
self.as_mut_separate_vecs()
|
||||
.par_iter_mut()
|
||||
.try_for_each(|(_, v)| v.safe_flush_stateful_vecs(height, exit))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use brk_core::{ByAddressType, Height};
|
||||
use brk_vec::VecIterator;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::vecs::stateful::addresstype_to_height_to_addresscount::AddressTypeToHeightToAddressCount;
|
||||
|
||||
#[derive(Debug, Default, Deref, DerefMut)]
|
||||
pub struct AddressTypeToAddressCount(ByAddressType<usize>);
|
||||
|
||||
impl From<(&AddressTypeToHeightToAddressCount, Height)> for AddressTypeToAddressCount {
|
||||
fn from((groups, starting_height): (&AddressTypeToHeightToAddressCount, Height)) -> Self {
|
||||
if let Some(prev_height) = starting_height.decremented() {
|
||||
Self(ByAddressType {
|
||||
p2pk65: groups
|
||||
.p2pk65
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height)
|
||||
.into(),
|
||||
p2pk33: groups
|
||||
.p2pk33
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height)
|
||||
.into(),
|
||||
p2pkh: groups
|
||||
.p2pkh
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height)
|
||||
.into(),
|
||||
p2sh: groups.p2sh.into_iter().unwrap_get_inner(prev_height).into(),
|
||||
p2wpkh: groups
|
||||
.p2wpkh
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height)
|
||||
.into(),
|
||||
p2wsh: groups
|
||||
.p2wsh
|
||||
.into_iter()
|
||||
.unwrap_get_inner(prev_height)
|
||||
.into(),
|
||||
p2tr: groups.p2tr.into_iter().unwrap_get_inner(prev_height).into(),
|
||||
p2a: groups.p2a.into_iter().unwrap_get_inner(prev_height).into(),
|
||||
})
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use brk_core::{ByAddressType, Height, Result, StoredUsize};
|
||||
use brk_exit::Exit;
|
||||
use brk_vec::EagerVec;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::vecs::stateful::addresstype_to_addresscount::AddressTypeToAddressCount;
|
||||
|
||||
#[derive(Debug, Clone, Deref, DerefMut)]
|
||||
pub struct AddressTypeToHeightToAddressCount(ByAddressType<EagerVec<Height, StoredUsize>>);
|
||||
|
||||
impl From<ByAddressType<EagerVec<Height, StoredUsize>>> for AddressTypeToHeightToAddressCount {
|
||||
fn from(value: ByAddressType<EagerVec<Height, StoredUsize>>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressTypeToHeightToAddressCount {
|
||||
pub fn forced_push_at(
|
||||
&mut self,
|
||||
height: Height,
|
||||
addresstype_to_usize: &AddressTypeToAddressCount,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.p2pk65
|
||||
.forced_push_at(height, addresstype_to_usize.p2pk65.into(), exit)?;
|
||||
self.p2pk33
|
||||
.forced_push_at(height, addresstype_to_usize.p2pk33.into(), exit)?;
|
||||
self.p2pkh
|
||||
.forced_push_at(height, addresstype_to_usize.p2pkh.into(), exit)?;
|
||||
self.p2sh
|
||||
.forced_push_at(height, addresstype_to_usize.p2sh.into(), exit)?;
|
||||
self.p2wpkh
|
||||
.forced_push_at(height, addresstype_to_usize.p2wpkh.into(), exit)?;
|
||||
self.p2wsh
|
||||
.forced_push_at(height, addresstype_to_usize.p2wsh.into(), exit)?;
|
||||
self.p2tr
|
||||
.forced_push_at(height, addresstype_to_usize.p2tr.into(), exit)?;
|
||||
self.p2a
|
||||
.forced_push_at(height, addresstype_to_usize.p2a.into(), exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
use brk_core::{ByAddressType, StoredUsize};
|
||||
use brk_exit::Exit;
|
||||
use brk_vec::AnyCollectableVec;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::vecs::{
|
||||
Indexes, grouped::ComputedVecsFromHeight, indexes,
|
||||
stateful::addresstype_to_height_to_addresscount::AddressTypeToHeightToAddressCount,
|
||||
};
|
||||
|
||||
#[derive(Clone, Deref, DerefMut)]
|
||||
pub struct AddressTypeToIndexesToAddressCount(ByAddressType<ComputedVecsFromHeight<StoredUsize>>);
|
||||
|
||||
impl From<ByAddressType<ComputedVecsFromHeight<StoredUsize>>>
|
||||
for AddressTypeToIndexesToAddressCount
|
||||
{
|
||||
fn from(value: ByAddressType<ComputedVecsFromHeight<StoredUsize>>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressTypeToIndexesToAddressCount {
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
// height: Height,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
addresstype_to_height_to_addresscount: &AddressTypeToHeightToAddressCount,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.p2pk65.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2pk65),
|
||||
)?;
|
||||
self.p2pk33.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2pk33),
|
||||
)?;
|
||||
self.p2pkh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2pkh),
|
||||
)?;
|
||||
self.p2sh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2sh),
|
||||
)?;
|
||||
self.p2wpkh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2wpkh),
|
||||
)?;
|
||||
self.p2wsh.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2wsh),
|
||||
)?;
|
||||
self.p2tr.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2tr),
|
||||
)?;
|
||||
self.p2a.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&addresstype_to_height_to_addresscount.p2a),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
//
|
||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
self.0
|
||||
.as_typed_vec()
|
||||
.into_iter()
|
||||
.flat_map(|(_, v)| v.vecs())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
use std::{collections::BTreeMap, mem};
|
||||
|
||||
use brk_core::TypeIndex;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use super::ByAddressType;
|
||||
|
||||
#[derive(Debug, Deref, DerefMut)]
|
||||
pub struct AddressTypeToTypeIndexTree<T>(ByAddressType<BTreeMap<TypeIndex, T>>);
|
||||
|
||||
impl<T> AddressTypeToTypeIndexTree<T> {
|
||||
pub fn merge(&mut self, mut other: Self) {
|
||||
Self::merge_(&mut self.p2pk65, &mut other.p2pk65);
|
||||
Self::merge_(&mut self.p2pk33, &mut other.p2pk33);
|
||||
Self::merge_(&mut self.p2pkh, &mut other.p2pkh);
|
||||
Self::merge_(&mut self.p2sh, &mut other.p2sh);
|
||||
Self::merge_(&mut self.p2wpkh, &mut other.p2wpkh);
|
||||
Self::merge_(&mut self.p2wsh, &mut other.p2wsh);
|
||||
Self::merge_(&mut self.p2tr, &mut other.p2tr);
|
||||
Self::merge_(&mut self.p2a, &mut other.p2a);
|
||||
}
|
||||
|
||||
fn merge_(own: &mut BTreeMap<TypeIndex, T>, other: &mut BTreeMap<TypeIndex, T>) {
|
||||
if own.len() >= other.len() {
|
||||
own.append(other);
|
||||
} else {
|
||||
other.append(own);
|
||||
mem::swap(own, other);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> ByAddressType<BTreeMap<TypeIndex, T>> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for AddressTypeToTypeIndexTree<T> {
|
||||
fn default() -> Self {
|
||||
Self(ByAddressType {
|
||||
p2pk65: BTreeMap::default(),
|
||||
p2pk33: BTreeMap::default(),
|
||||
p2pkh: BTreeMap::default(),
|
||||
p2sh: BTreeMap::default(),
|
||||
p2wpkh: BTreeMap::default(),
|
||||
p2wsh: BTreeMap::default(),
|
||||
p2tr: BTreeMap::default(),
|
||||
p2a: BTreeMap::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use std::mem;
|
||||
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use super::ByAddressType;
|
||||
|
||||
#[derive(Debug, Default, Deref, DerefMut)]
|
||||
pub struct AddressTypeToVec<T>(ByAddressType<Vec<T>>);
|
||||
|
||||
impl<T> AddressTypeToVec<T> {
|
||||
pub fn merge(&mut self, mut other: Self) {
|
||||
Self::merge_(&mut self.p2pk65, &mut other.p2pk65);
|
||||
Self::merge_(&mut self.p2pk33, &mut other.p2pk33);
|
||||
Self::merge_(&mut self.p2pkh, &mut other.p2pkh);
|
||||
Self::merge_(&mut self.p2sh, &mut other.p2sh);
|
||||
Self::merge_(&mut self.p2wpkh, &mut other.p2wpkh);
|
||||
Self::merge_(&mut self.p2wsh, &mut other.p2wsh);
|
||||
Self::merge_(&mut self.p2tr, &mut other.p2tr);
|
||||
Self::merge_(&mut self.p2a, &mut other.p2a);
|
||||
}
|
||||
|
||||
fn merge_(own: &mut Vec<T>, other: &mut Vec<T>) {
|
||||
if own.len() >= other.len() {
|
||||
own.append(other);
|
||||
} else {
|
||||
other.append(own);
|
||||
mem::swap(own, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use brk_core::{Bitcoin, DateIndex, Dollars, Height, Result, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, AnyIterableVec};
|
||||
|
||||
use crate::vecs::{Indexes, fetched, indexes, market};
|
||||
|
||||
pub trait DynCohortVecs: Send + Sync {
|
||||
fn starting_height(&self) -> Height;
|
||||
|
||||
fn init(&mut self, starting_height: Height);
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()>;
|
||||
|
||||
fn forced_pushed_at(&mut self, height: Height, exit: &Exit) -> Result<()>;
|
||||
|
||||
fn compute_then_force_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Option<Dollars>,
|
||||
dateindex: Option<DateIndex>,
|
||||
date_price: Option<Option<Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
|
||||
fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()>;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()>;
|
||||
|
||||
fn vecs(&self) -> Vec<&dyn AnyCollectableVec>;
|
||||
}
|
||||
|
||||
pub trait CohortVecs: DynCohortVecs {
|
||||
fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
market: &market::Vecs,
|
||||
height_to_supply: &impl AnyIterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl AnyIterableVec<DateIndex, Bitcoin>,
|
||||
height_to_realized_cap: Option<&impl AnyIterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()>;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
use std::{ops::Deref, path::Path};
|
||||
|
||||
use brk_core::{Bitcoin, DateIndex, Dollars, Height, Result, Version};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_vec::{AnyCollectableVec, AnyIterableVec, Computation, Format};
|
||||
|
||||
use crate::{
|
||||
UTXOCohortState,
|
||||
vecs::{
|
||||
Indexes, fetched, indexes, market,
|
||||
stateful::{
|
||||
common,
|
||||
r#trait::{CohortVecs, DynCohortVecs},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vecs {
|
||||
starting_height: Height,
|
||||
|
||||
pub state: UTXOCohortState,
|
||||
|
||||
inner: common::Vecs,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
path: &Path,
|
||||
cohort_name: Option<&str>,
|
||||
computation: Computation,
|
||||
format: Format,
|
||||
version: Version,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
states_path: &Path,
|
||||
compute_relative_to_all: bool,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let compute_dollars = fetched.is_some();
|
||||
|
||||
Ok(Self {
|
||||
starting_height: Height::ZERO,
|
||||
|
||||
state: UTXOCohortState::default_and_import(
|
||||
states_path,
|
||||
cohort_name.unwrap_or_default(),
|
||||
compute_dollars,
|
||||
)?,
|
||||
|
||||
inner: common::Vecs::forced_import(
|
||||
path,
|
||||
cohort_name,
|
||||
computation,
|
||||
format,
|
||||
version,
|
||||
fetched,
|
||||
compute_relative_to_all,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 init(&mut self, starting_height: Height) {
|
||||
if starting_height > self.starting_height() {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
self.starting_height = starting_height;
|
||||
|
||||
self.inner.init(&mut self.starting_height, &mut self.state);
|
||||
}
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.inner.validate_computed_versions(base_version)
|
||||
}
|
||||
|
||||
fn forced_pushed_at(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
if self.starting_height > height {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.inner.forced_pushed_at(height, exit, &self.state)
|
||||
}
|
||||
|
||||
fn compute_then_force_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Option<Dollars>,
|
||||
dateindex: Option<DateIndex>,
|
||||
date_price: Option<Option<Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_then_force_push_unrealized_states(
|
||||
height,
|
||||
height_price,
|
||||
dateindex,
|
||||
date_price,
|
||||
exit,
|
||||
&self.state,
|
||||
)
|
||||
}
|
||||
|
||||
fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
|
||||
self.inner
|
||||
.safe_flush_stateful_vecs(height, exit, &mut self.state)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.inner
|
||||
.compute_rest_part1(indexer, indexes, fetched, starting_indexes, exit)
|
||||
}
|
||||
|
||||
fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||
self.inner.vecs()
|
||||
}
|
||||
}
|
||||
|
||||
impl CohortVecs for Vecs {
|
||||
fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| &v.inner).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
fetched: Option<&fetched::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
market: &market::Vecs,
|
||||
height_to_supply: &impl AnyIterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl AnyIterableVec<DateIndex, Bitcoin>,
|
||||
height_to_realized_cap: Option<&impl AnyIterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.inner.compute_rest_part2(
|
||||
indexer,
|
||||
indexes,
|
||||
fetched,
|
||||
starting_indexes,
|
||||
market,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Vecs {
|
||||
type Target = common::Vecs;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
use brk_core::{AddressData, EmptyAddressData};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WithAddressDataSource<T> {
|
||||
New(T),
|
||||
FromAddressDataStore(T),
|
||||
FromEmptyAddressDataStore(T),
|
||||
}
|
||||
|
||||
impl<T> WithAddressDataSource<T> {
|
||||
pub fn is_new(&self) -> bool {
|
||||
matches!(self, Self::New(_))
|
||||
}
|
||||
|
||||
pub fn is_from_addressdata(&self) -> bool {
|
||||
matches!(self, Self::FromAddressDataStore(_))
|
||||
}
|
||||
|
||||
pub fn is_from_emptyaddressdata(&self) -> bool {
|
||||
matches!(self, Self::FromEmptyAddressDataStore(_))
|
||||
}
|
||||
|
||||
pub fn deref(&self) -> &T {
|
||||
match self {
|
||||
Self::New(v) => v,
|
||||
Self::FromAddressDataStore(v) => v,
|
||||
Self::FromEmptyAddressDataStore(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deref_mut(&mut self) -> &mut T {
|
||||
match self {
|
||||
Self::New(v) => v,
|
||||
Self::FromAddressDataStore(v) => v,
|
||||
Self::FromEmptyAddressDataStore(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WithAddressDataSource<EmptyAddressData>> for WithAddressDataSource<AddressData> {
|
||||
fn from(value: WithAddressDataSource<EmptyAddressData>) -> Self {
|
||||
match value {
|
||||
WithAddressDataSource::New(v) => Self::New(v.into()),
|
||||
WithAddressDataSource::FromAddressDataStore(v) => Self::FromAddressDataStore(v.into()),
|
||||
WithAddressDataSource::FromEmptyAddressDataStore(v) => {
|
||||
Self::FromEmptyAddressDataStore(v.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WithAddressDataSource<AddressData>> for WithAddressDataSource<EmptyAddressData> {
|
||||
fn from(value: WithAddressDataSource<AddressData>) -> Self {
|
||||
match value {
|
||||
WithAddressDataSource::New(v) => Self::New(v.into()),
|
||||
WithAddressDataSource::FromAddressDataStore(v) => Self::FromAddressDataStore(v.into()),
|
||||
WithAddressDataSource::FromEmptyAddressDataStore(v) => {
|
||||
Self::FromEmptyAddressDataStore(v.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||