Compare commits

...

149 Commits

Author SHA1 Message Date
nym21 11911c1898 release: v0.0.83 2025-07-26 23:35:43 +02:00
nym21 4814c1971d vecs: add linux punch hole impl 2025-07-26 23:35:23 +02:00
nym21 be9569f3fb release: v0.0.82 2025-07-26 22:09:21 +02:00
nym21 900e72f95a cargo: cleanup deps 2025-07-26 14:28:26 +02:00
nym21 d2827f188b computer: temp remove rayon 2025-07-26 14:24:06 +02:00
nym21 cf9903b759 computer: init file with min length and regions 2025-07-26 08:57:13 +02:00
nym21 23f96461f4 computer: remove libc dep 2025-07-26 08:42:33 +02:00
nym21 9f2fd26e98 computer: fixes 2025-07-26 08:41:19 +02:00
nym21 78d837c080 computer: flush + punch 2025-07-26 01:04:36 +02:00
nym21 241b9312b7 cli: config changes 2025-07-26 00:46:35 +02:00
nym21 ed70ad7378 indexer: take readers before last export 2025-07-25 22:45:41 +02:00
nym21 00213176d8 indexer: small changes 2025-07-25 22:38:15 +02:00
nym21 406650a45a vec: removed 2025-07-25 20:38:57 +02:00
nym21 56750ccf3c vecs: part 11 2025-07-25 20:27:15 +02:00
nym21 dfc286b393 vecs: part 10 2025-07-25 20:22:54 +02:00
nym21 49a66f72fc crates: update rapidhash 2025-07-24 17:32:38 +02:00
nym21 3f237689da vecs: part 9 2025-07-24 17:19:05 +02:00
nym21 cf1fb483b3 vecs: part 8 2025-07-24 16:48:50 +02:00
nym21 b10f5e3f67 vecs: part 7 2025-07-23 23:55:13 +02:00
nym21 c4fc24c513 vecs: part 6 2025-07-23 09:17:26 +02:00
nym21 3ac9c2d95e vecs: part 5 2025-07-22 21:26:50 +02:00
nym21 e5ab4dafc0 vecs: part 4 2025-07-22 17:36:34 +02:00
nym21 10ae1911c3 vecs: part 3 2025-07-22 15:10:07 +02:00
nym21 73ebcdf0d6 vecs: part 2 2025-07-22 13:19:19 +02:00
nym21 5347523921 vecs: init 2025-07-21 11:02:25 +02:00
nym21 7ef70b953b vec: lazy: remove unneeded phantoms 2025-07-19 17:47:25 +02:00
nym21 ccaca524fe computer: libc sync 2025-07-19 10:10:01 +02:00
nym21 dd51f91cab computer: final fix for external disks crashing 2025-07-18 16:29:53 +02:00
nym21 537d98b41b release: v0.0.81 2025-07-17 23:45:01 +02:00
nym21 9c4cadfc04 vec: fix holes export 2025-07-17 17:29:53 +02:00
nym21 2001370441 mcp: use rust-rmcp instead of brk-rmcp 2025-07-17 16:34:29 +02:00
nym21 cc87b22757 computer: perf improvements 2025-07-17 16:17:21 +02:00
nym21 c0a65b30ad indexer: update example 2025-07-17 11:39:25 +02:00
nym21 c07e66c086 computer: fix stateful 2025-07-17 11:35:40 +02:00
nym21 a0cfc1be2b computer: convert stores to vecs part 2 2025-07-16 16:23:40 +02:00
nym21 1505454793 computer: convert stores to vecs part 1 2025-07-15 22:47:46 +02:00
nym21 e1dff66283 pr: merge #21 from deadmanoz/dockerize
Add Docker support
2025-07-15 15:51:32 +00:00
deadmanoz 5be801a086 Merge branch 'main' into dockerize 2025-07-15 08:50:22 -07:00
deadmanoz 94d4b05c29 Address review feedback 2025-07-15 08:48:39 -07:00
nym21 cebb889f7e cargo: update 2025-07-14 16:00:31 +02:00
nym21 c4ed6ed034 store: remove rotate_memtable as could be the root cause of the issue 2025-07-14 15:48:19 +02:00
nym21 ec960bfefa release: v0.0.80 2025-07-13 21:20:40 +02:00
nym21 79f689dde1 mcp: remove claude results examples due to dead links 2025-07-13 21:20:02 +02:00
nym21 3b3654df56 vec: add local and shared stored_len to raw variant 2025-07-13 19:30:50 +02:00
nym21 c66f008f07 release: v0.0.79 2025-07-13 17:18:14 +02:00
nym21 37d9498d90 crates: upgrade 2025-07-13 17:18:02 +02:00
nym21 1ff67093db website: apply datasets changes to charts 2025-07-13 17:14:34 +02:00
nym21 daed37ccb8 stores: forgot some files 2025-07-13 16:52:19 +02:00
nym21 d41d807b4f stores: bloom filters back to default due to slow reads, v3 will bring down the needed RAM 2025-07-13 16:49:45 +02:00
nym21 d6fa5c8a55 vec: fix header reading of existing file 2025-07-13 16:31:22 +02:00
nym21 2dd608dfed vec: don't store mmap in struct anymore 2025-07-13 11:50:34 +02:00
nym21 a98546f605 release: v0.0.78 2025-07-13 02:05:28 +02:00
nym21 3567559d4e release: v0.0.77 2025-07-13 01:54:51 +02:00
nym21 216476ee45 computer: reduce number of ratio datasets for some cohorts 2025-07-13 01:25:45 +02:00
nym21 3fc28c07fb computer: missed a file 2025-07-13 00:23:32 +02:00
nym21 85f6ef063d computer: perf again 2025-07-13 00:21:20 +02:00
nym21 1e71e2d68f computer: perf 2025-07-12 16:17:29 +02:00
nym21 b24a29895f global: perf + resource imprv 2025-07-12 15:07:02 +02:00
nym21 0167a2ae59 global: fixes 2025-07-12 11:18:51 +02:00
nym21 2c867103ca computer: remove dbg 2025-07-11 14:23:26 +02:00
nym21 8c289df336 computer: stateful: perf improvements 2025-07-11 11:43:53 +02:00
nym21 4489920cbf computer: fix coarse lazy indexes 2025-07-11 01:51:04 +02:00
nym21 029a85081b global: snapshot 2025-07-10 22:32:04 +02:00
nym21 1bc739d07f vec + comp: small changes 2025-07-10 18:35:54 +02:00
nym21 c229e218f6 global: adding semester + making coarser intervals computed instead of eager 2025-07-10 17:44:19 +02:00
nym21 a66f4ad4bd release: v0.0.76 2025-07-09 13:41:42 +02:00
nym21 1dd687dab7 bundler: upgrade rolldown dep 2025-07-09 13:41:31 +02:00
nym21 50ff6e2745 release: v0.0.75 2025-07-09 12:33:06 +02:00
nym21 811dec713b computer: reduce even more the number of par threads for compute_rest_part2 2025-07-09 12:32:50 +02:00
nym21 617d6f4bd7 release: v0.0.74 2025-07-09 11:51:24 +02:00
nym21 57cd2d6252 computer: fix par compute_rest_part2 crashing external drives 2025-07-09 11:48:41 +02:00
nym21 ec64f8d048 release: v0.0.73 2025-07-09 08:36:29 +02:00
nym21 ed288a9dba website: make panes the same size + remove saving their height 2025-07-09 00:06:38 +02:00
nym21 27da0a4102 packages: add lightweight-charts v5.0.8 2025-07-08 23:37:06 +02:00
nym21 3c01ba1a76 release: v0.0.72 2025-07-08 22:35:16 +02:00
nym21 252c8833ae global: upgrade deps 2025-07-08 22:34:51 +02:00
nym21 f45fb6efe6 global: renames and fixes 2025-07-08 21:33:18 +02:00
nym21 8cc1f8d691 computer: add more up to and from datasets 2025-07-07 23:53:59 +02:00
nym21 bff22b5182 websites: snapshot + todo: init 2025-07-07 13:16:43 +02:00
nym21 d31d47eb32 computer: store part 10 2025-07-05 17:44:51 +02:00
nym21 5fe984c39d indexer: rename some stores 2025-07-05 00:17:23 +02:00
nym21 7f07b0daa7 global: move addressindex_to_outputindex stores from computer to indexer 2025-07-04 17:30:43 +02:00
deadmanoz 5de9757d46 Remove services from docker 2025-07-04 16:37:39 +08:00
deadmanoz f89276d7b8 Remove redundant services 2025-07-04 15:51:28 +08:00
deadmanoz 30ba034206 Move docker artefacts into /docker directory 2025-07-04 13:00:12 +08:00
deadmanoz fa1e5aaa7f Make Parser::new the only entrypoint 2025-07-04 12:15:32 +08:00
deadmanoz 870c70180f Back to a single image/container setup 2025-07-04 11:40:37 +08:00
nym21 6d35c26b3f computer: store part 9 2025-07-03 19:15:02 +02:00
nym21 be4e693a27 computer: store part 8 2025-07-03 18:19:36 +02:00
nym21 5810276156 computer: store part 7 2025-07-02 23:49:24 +02:00
nym21 d10ac3f87b computer: store part 6 2025-07-02 16:02:18 +02:00
nym21 9810bc09e9 computer: store part 5 2025-07-01 20:03:00 +02:00
nym21 a0a13eb2a8 computer: store part 4 2025-07-01 13:57:48 +02:00
nym21 6e996797b8 computer: store part 3 2025-06-29 17:39:31 +02:00
nym21 663092b501 global: replace Value enum with Cow 2025-06-27 20:39:19 +02:00
nym21 8ea13544de computer: store part 2 2025-06-27 19:38:44 +02:00
nym21 e73daa6214 computer: init store 2025-06-27 10:52:36 +02:00
deadmanoz d83a833b4d Switch to multiple container setup 2025-06-27 12:56:25 +08:00
deadmanoz ec3a2f29f0 Docker functionality, change location of 'blk_index_to_blk_recap.json' 2025-06-27 12:56:03 +08:00
nym21 cf92c60a01 mcp: readme 2025-06-26 17:41:00 +02:00
nym21 b7f51b03bc global: snapshot 2025-06-26 16:40:29 +02:00
nym21 903e69ff77 release: v0.0.71 2025-06-25 17:59:19 +02:00
nym21 c4167ddaad mcp: small changes 2025-06-25 10:44:24 +02:00
nym21 50bfdb0d68 release: v0.0.70 2025-06-24 23:53:30 +02:00
nym21 a6cb09ff1c fetcher: fix brk url 2025-06-24 23:53:10 +02:00
nym21 e4c9f23476 release: v0.0.69 2025-06-24 13:23:14 +02:00
nym21 44e5415d43 vec: undo storing file in struct, can overwhelm system 2025-06-24 13:20:44 +02:00
nym21 1c653693ed release: v0.0.68 2025-06-24 12:19:07 +02:00
nym21 39c470ad7a core: increase max open files limit 2025-06-24 12:18:49 +02:00
nym21 1103e538a5 release: v0.0.67 2025-06-24 11:57:25 +02:00
nym21 c0cd4cba6f global: snapshot 2025-06-24 11:56:54 +02:00
nym21 b91120e8d4 readme: update 2025-06-24 08:42:40 +00:00
nym21 005774a4c2 mcp: update readme 2025-06-24 08:41:08 +00:00
nym21 16bbfebfba changelog: update titles 2025-06-24 08:26:01 +00:00
nym21 15505cd82d web: fix index type imports 2025-06-24 10:23:16 +02:00
nym21 016d80e002 server: use etag for vecs instead of date modified 2025-06-24 10:11:03 +02:00
nym21 0f3c267a48 global: rename some indexes 2025-06-23 22:00:09 +02:00
nym21 589bb02411 vec: single file with header 2025-06-23 20:48:00 +02:00
nym21 c0f4ece17b mcp: part 2 2025-06-21 20:34:14 +02:00
nym21 c3ae3cb768 server: mcp + global: refactor 2025-06-21 12:43:14 +02:00
nym21 c9e0f9d985 query: remove dup 'h' short index 2025-06-19 14:16:24 +02:00
nym21 e3431c2fa3 release: v0.0.66 2025-06-19 12:32:04 +02:00
nym21 5979b9771e global: cointime part 1 2025-06-19 12:29:34 +02:00
nym21 aa61832fb2 web: fix options cmumulative possible vecids type 2025-06-18 10:14:09 +02:00
nym21 2ac6e982b1 computer: cumulative destroyed coinblocks and cointime 2025-06-18 10:13:33 +02:00
nym21 3204ddcf07 pr: merge #18 from StevenBlack/typos
Minor refinements to the brk_cli readme
2025-06-17 23:06:32 +02:00
nym21 c87b1c133c release: v0.0.65 2025-06-17 22:30:32 +02:00
nym21 9b275ecdae web: fix hang when candles fetched are slightly diff than before 2025-06-17 22:30:11 +02:00
Steven Black d6fd7de361 Minor refinements to the brk_cli readme
* Fix typos
* Rephrasing some descriptions
* Links to run command parameters now link to the code as it is in blob  49d66a133e specifically.
* Lightly introduce some nice features of Github flavored markdown (IMPORTANT and TIP) to assess how this nuance is received by maintainers.
2025-06-17 14:07:03 -04:00
nym21 49d66a133e release: v0.0.64 2025-06-17 18:49:17 +02:00
nym21 c559f26d0e global: add a bunch of realized datasets + charts 2025-06-17 18:47:04 +02:00
nym21 bbe9f1bad2 global: 4y zscore + 200d sma + mayer's multiple 2025-06-17 10:01:49 +02:00
nym21 7e1fb6472d release: v0.0.63 2025-06-16 23:46:57 +02:00
nym21 0ff8d20573 web: fix the fix for the stutter + pwa assets 2025-06-16 23:46:39 +02:00
nym21 9c1f9448dc release: v0.0.62 2025-06-16 18:56:59 +02:00
nym21 43a6081dd6 web: fix stutter on update and save default chart settings to url params 2025-06-16 18:56:38 +02:00
nym21 985e961876 web: fix error in lockdown safari + charts: update instead of setData when possible 2025-06-16 18:20:56 +02:00
nym21 098f6de047 release: v0.0.61 2025-06-15 17:30:49 +02:00
nym21 1b0f90fd68 release: v0.0.60 2025-06-15 17:27:41 +02:00
nym21 12252f407b computer: fix open of ohlc if fetched from different API than prev ohlc 2025-06-15 17:27:16 +02:00
nym21 3b6e3f47ab release: v0.0.59 2025-06-15 12:40:46 +02:00
nym21 6a9ac9b025 brk: fix bundler use + bundler: remove minify html crate 2025-06-15 12:39:50 +02:00
nym21 ae6aa4088b release: v0.0.58 2025-06-15 01:50:22 +02:00
nym21 c08f431180 bundler: deploy brk_rolldown + fix edge case 2025-06-15 01:50:01 +02:00
nym21 123c1f56e9 release: v0.0.57 2025-06-14 22:47:57 +02:00
nym21 35ac65a864 server: update cache control for bundled websites 2025-06-14 22:47:26 +02:00
nym21 e9f362cc87 bundler: init working version 2025-06-14 20:17:49 +02:00
nym21 65685c23e1 release: v0.0.56 2025-06-13 18:03:28 +02:00
nym21 2f74748cea computer: stateful: reset when reorg detected 2025-06-13 18:03:09 +02:00
435 changed files with 35287 additions and 24232 deletions
+13
View File
@@ -3,6 +3,8 @@
# Builds
target
dist
vecid-to-indexes.js
# Copies
*\ copy*
@@ -16,3 +18,14 @@ _*
# Logs
.log
# Environment variables/configs
.env
# Profiling
profile.json.gz
flamegraph.svg
*.trace
# AI
CLAUDE.md
+17 -15
View File
@@ -1,15 +1,17 @@
<!--
# v0.X.Y | WIP
![Image of the kibo Web App version 0.X.Y](https://github.com/kibo-money/kibo/blob/main/_assets/v0.X.Y.jpg)
![Image of the kibo Web App version 0.X.Y](https://github.com/bitcoinresearchkit/brk/blob/main/assets/v0.X.Y.jpg)
-->
# v0.X.0 | WIP | A new beginning
![Image of BRK's Web App version 0.1.0](https://github.com/bitcoinresearchkit/brk/blob/main/assets/brk-v0.1.0.png)
Full rewrite
# [kibo-v0.5.0](https://github.com/kibo-money/kibo/tree/eea56d394bf92c62c81da8b78b8c47ea730683f5) | [873199](https://mempool.space/block/0000000000000000000270925aa6a565be92e13164565a3f7994ca1966e48050) - 2024/12/04
# [kibo-v0.5.0](https://github.com/bitcoinresearchkit/brk/tree/eea56d394bf92c62c81da8b78b8c47ea730683f5) | [873199](https://mempool.space/block/0000000000000000000270925aa6a565be92e13164565a3f7994ca1966e48050) - 2024/12/04
![Image of the kibo Web App version 0.5.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.5.0.jpg)
![Image of the kibo Web App version 0.5.0](https://github.com/bitcoinresearchkit/brk/blob/main/assets/kibo-v0.5.0.jpg)
## Datasets
@@ -74,9 +76,9 @@ Full rewrite
- 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
![Image of the kibo Web App version 0.4.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.4.0.jpg)
![Image of the kibo Web App version 0.4.0](https://github.com/bitcoinresearchkit/brk/blob/main/assets/kibo-v0.4.0.jpg)
## Brand
@@ -111,9 +113,9 @@ Full rewrite
- 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
![Image of the Satonomics Web App version 0.3.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.3.0.jpg)
![Image of the Satonomics Web App version 0.3.0](https://github.com/bitcoinresearchkit/brk/blob/main/assets/satonomics-v0.3.0.jpg)
## Parser
@@ -190,9 +192,9 @@ Full rewrite
- 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
![Image of the Satonomics Web App version 0.2.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.2.0.jpg)
![Image of the Satonomics Web App version 0.2.0](https://github.com/bitcoinresearchkit/brk/blob/main/assets/satonomics-v0.2.0.jpg)
## App
@@ -226,9 +228,9 @@ Full rewrite
- 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
![Image of the Satonomics Web App version 0.1.1](https://github.com/kibo-money/kibo/blob/main/_assets/v0.1.1.jpg)
![Image of the Satonomics Web App version 0.1.1](https://github.com/bitcoinresearchkit/brk/blob/main/assets/satonomics-v0.1.1.jpg)
## Parser
@@ -276,10 +278,10 @@ Full rewrite
- 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
![Image of the Satonomics Web App version 0.1.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.1.0.jpg)
![Image of the Satonomics Web App version 0.1.0](https://github.com/bitcoinresearchkit/brk/blob/main/assets/satonomics-v0.1.0.jpg)
# 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
![Image of the Satonomics Web App version 0.0.X](https://github.com/kibo-money/kibo/blob/main/_assets/v0.0.X.jpg)
![Image of the Satonomics Web App version 0.0.X](https://github.com/bitcoinresearchkit/brk/blob/main/assets/satonomics-v0.0.1.jpg)
Generated
+1822 -308
View File
File diff suppressed because it is too large Load Diff
+36 -24
View File
@@ -4,55 +4,67 @@ 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.55"
package.version = "0.0.83"
package.homepage = "https://bitcoinresearchkit.org"
package.repository = "https://github.com/bitcoinresearchkit/brk"
package.readme = "README.md"
[profile.release]
lto = "fat"
codegen-units = 1
panic = "abort"
[profile.profiling]
inherits = "release"
debug = true
[profile.dist]
inherits = "release"
[workspace.dependencies]
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.0.55", path = "crates/brk_cli" }
brk_computer = { version = "0.0.55", path = "crates/brk_computer" }
brk_core = { version = "0.0.55", path = "crates/brk_core" }
brk_exit = { version = "0.0.55", path = "crates/brk_exit" }
brk_fetcher = { version = "0.0.55", path = "crates/brk_fetcher" }
brk_indexer = { version = "0.0.55", path = "crates/brk_indexer" }
brk_logger = { version = "0.0.55", path = "crates/brk_logger" }
brk_parser = { version = "0.0.55", path = "crates/brk_parser" }
brk_query = { version = "0.0.55", path = "crates/brk_query" }
brk_server = { version = "0.0.55", path = "crates/brk_server" }
brk_state = { version = "0.0.55", path = "crates/brk_state" }
brk_store = { version = "0.0.55", path = "crates/brk_store" }
brk_vec = { version = "0.0.55", path = "crates/brk_vec" }
brk_bundler = { version = "0.0.83", path = "crates/brk_bundler" }
brk_cli = { version = "0.0.83", path = "crates/brk_cli" }
brk_computer = { version = "0.0.83", path = "crates/brk_computer" }
brk_core = { version = "0.0.83", path = "crates/brk_core" }
brk_exit = { version = "0.0.83", path = "crates/brk_exit" }
brk_fetcher = { version = "0.0.83", path = "crates/brk_fetcher" }
brk_indexer = { version = "0.0.83", path = "crates/brk_indexer" }
brk_interface = { version = "0.0.83", path = "crates/brk_interface" }
brk_logger = { version = "0.0.83", path = "crates/brk_logger" }
brk_mcp = { version = "0.0.83", path = "crates/brk_mcp" }
brk_parser = { version = "0.0.83", path = "crates/brk_parser" }
brk_server = { version = "0.0.83", path = "crates/brk_server" }
brk_store = { version = "0.0.83", path = "crates/brk_store" }
brk_vecs = { version = "0.0.83", path = "crates/brk_vecs" }
byteview = "=0.6.1"
clap = { version = "4.5.40", features = ["string"] }
clap_derive = "4.5.40"
clap = { version = "4.5.41", features = ["string"] }
clap_derive = "4.5.41"
color-eyre = "0.6.5"
derive_deref = "1.1.1"
fjall = "2.11.0"
jiff = "0.2.14"
fjall = "2.11.2"
jiff = "0.2.15"
libc = "0.2.174"
log = { version = "0.4.27" }
minreq = { version = "2.13.4", features = ["https", "serde_json"] }
minreq = { version = "2.14.0", features = ["https", "serde_json"] }
parking_lot = "0.12.4"
rayon = "1.10.0"
rmcp = { version = "0.3.0", features = [
"transport-worker",
"transport-streamable-http-server",
] }
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"] }
serde_json = { version = "1.0.141", features = ["float_roundtrip"] }
tabled = "0.20.0"
tokio = { version = "1.45.1", features = ["rt-multi-thread"] }
zerocopy = { version = "0.8.25" }
zerocopy-derive = "0.8.25"
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
+13 -9
View File
@@ -40,10 +40,13 @@ The toolkit can be used in various ways to accommodate as many needs as possible
- **[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#endpoints)** \
Researchers and developers are free to use BRK's public API with ![Datasets variant count](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fbitcoinresearchkit.org%2Fapi%2Fvecs%2Fvariant-count&query=%24&style=flat&label=%20&color=white) dataset variants at your disposal. \
Also available at: [brekit.org](https://brekit.org) // [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 ![Datasets variant count](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fbitcoinresearchkit.org%2Fapi%2Fvecs%2Fvec-count&query=%24&style=flat&label=%20&color=white) 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.
- **[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.
@@ -58,19 +61,20 @@ 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_state`](https://crates.io/crates/brk_state): Various states used mainly by the 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_vec`](https://crates.io/crates/brk_vec): A storeable vec
- [`brk_bundler`](https://crates.io/crates/brk_bundler): A thin wrapper around [`rolldown`](https://rolldown.rs/)
## Hosting as a service
@@ -91,7 +95,7 @@ Pricing: `0.01 BTC / month` *or* `0.1 BTC / year`
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.
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
+104
View File
@@ -0,0 +1,104 @@
# 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)**
- remove configurable format (raw/compressed) and chose sane ones instead
- linear reads: compressed (height/date/... + txindex_to_height + txindex_to_version + ...)
- random reads: raw (outputindex_to_value + ...)
- add prices paid by percentile (percentile cost basis) back
- add support for per index computation
- fix min 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
- some datasets in `indexes` can probably be removed
- 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 chopiness datasets
- add utxo count, address count, supply data for by reused addresses in groups by address type
- add more date ranges (3-6 months and more)
- add puell multiple dataset
- add pi cycle dataset
- add emas of price
- add 7d and 30d ema to sell side risk ratio and sopr
- don't compute everything for all cohorts as some datasets combinations are irrelevant
- addresses/utxos by amount don't need mvrvz for example
- 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 dont cap all combinations
- example: from -10,000 count 10, wont work if underlying vec isnt 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
- add support for https (rustls)
- lru cache
- _vec_
- add native lock file support (once it's available in stable rust)
- improve compressed mode (slow reads)
- add ema support
- __docs__
- _README_
- add a comparison table with alternatives
- 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
- separate z-score charts from "realized price" (with their own prices), have 4y, 2y and 1y
- 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
Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

+12 -9
View File
@@ -10,33 +10,36 @@ version.workspace = true
[features]
full = [
"bundler",
"core",
"computer",
"exit",
"fetcher",
"indexer",
"logger",
"mcp",
"parser",
"query",
"interface",
"server",
"state",
"store",
"vec",
"vecs",
]
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"]
state = ["brk_state"]
store = ["brk_store"]
vec = ["brk_vec"]
vecs = ["brk_vecs"]
[dependencies]
brk_bundler = { workspace = true, optional = true }
brk_cli = { workspace = true }
brk_core = { workspace = true, optional = true }
brk_computer = { workspace = true, optional = true }
@@ -44,12 +47,12 @@ 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_state = { workspace = true, optional = true }
brk_store = { workspace = true, optional = true }
brk_vec = { workspace = true, optional = true }
brk_vecs = { workspace = true, optional = true }
[package.metadata.docs.rs]
all-features = true
+1
View File
@@ -0,0 +1 @@
fn main() {}
+1
View File
@@ -0,0 +1 @@
sudo cargo flamegraph --profile profiling --root
+2
View File
@@ -0,0 +1,2 @@
cargo build --profile profiling
samply record ../../target/profiling/brk
+15 -8
View File
@@ -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,26 +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 = "state")]
#[doc(inline)]
pub use brk_state as state;
#[cfg(feature = "store")]
#[doc(inline)]
pub use brk_store as store;
#[cfg(feature = "vec")]
#[cfg(feature = "vecs")]
#[doc(inline)]
pub use brk_vec as vec;
pub use brk_vecs as vecs;
+16
View File
@@ -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.1"
# brk_rolldown = { path = "../../../rolldown/crates/rolldown"}
sugar_path = "1.2.0"
tokio = { workspace = true }
+145
View File
@@ -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(())
}
+3 -5
View File
@@ -1,6 +1,6 @@
[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
@@ -16,17 +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 }
brk_vec = { workspace = true }
brk_vecs = { workspace = true }
clap = { workspace = true }
clap_derive = { workspace = true }
color-eyre = { workspace = true }
log = { workspace = true }
serde = { workspace = true }
tabled = { workspace = true }
tokio = { workspace = true }
toml = "0.8.23"
toml = "0.9.2"
[[bin]]
name = "brk"
+14 -9
View File
@@ -1,4 +1,4 @@
# BRK Cli
# BRK CLI
<p align="left">
<a href="https://github.com/bitcoinresearchkit/brk">
@@ -31,9 +31,11 @@
</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
@@ -54,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
@@ -82,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.
+351
View File
@@ -0,0 +1,351 @@
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_vecs::{Computation, Format};
use clap::Parser;
use clap_derive::Parser;
use color_eyre::eyre::eyre;
use serde::{Deserialize, Serialize};
#[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>,
/// 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(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 harsdir(&self) -> PathBuf {
self.brkdir().join("hars")
}
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)
}
}
+6 -28
View File
@@ -1,30 +1,13 @@
#![doc = include_str!("../README.md")]
use std::{fs, thread};
use brk_core::{dot_brk_log_path, dot_brk_path};
use brk_query::Params as QueryArgs;
use clap::Parser;
use clap_derive::{Parser, Subcommand};
use query::query;
use run::{RunConfig, run};
mod query;
mod config;
mod run;
#[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, use `run -h` for more information
Run(RunConfig),
/// Query generated datasets via the `run` command in a similar fashion as the server's API, use `query -h` for more information
Query(QueryArgs),
}
use run::*;
pub fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
@@ -33,14 +16,9 @@ pub fn main() -> color_eyre::Result<()> {
brk_logger::init(Some(&dot_brk_log_path()));
let cli = Cli::parse();
thread::Builder::new()
.stack_size(128 * 1024 * 1024)
.spawn(|| match cli.command {
Commands::Run(args) => run(args),
Commands::Query(args) => query(args),
})?
.stack_size(256 * 1024 * 1024)
.spawn(run)?
.join()
.unwrap()
}
-55
View File
@@ -1,55 +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 format = config.format();
let mut indexer = Indexer::new(&config.outputsdir(), config.check_collisions())?;
indexer.import_vecs()?;
let mut computer = Computer::new(&config.outputsdir(), config.fetcher(), format);
computer.import_vecs(&indexer, config.computation())?;
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 from = params.from();
let to = params.to();
let format = params.format();
let res = query.search_and_format(index, &ids, from, to, format)?;
if 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(())
}
+40 -424
View File
@@ -1,41 +1,28 @@
use std::{
fs,
path::{Path, PathBuf},
thread::sleep,
time::Duration,
};
use std::{thread::sleep, time::Duration};
use bitcoincore_rpc::{self, Auth, Client, RpcApi};
use bitcoincore_rpc::{self, RpcApi};
use brk_computer::Computer;
use brk_core::{default_bitcoin_path, default_brk_path, default_on_error, dot_brk_path};
use brk_exit::Exit;
use brk_fetcher::Fetcher;
use brk_indexer::Indexer;
use brk_server::{Server, Website};
use brk_vec::{Computation, Format};
use clap_derive::{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()?;
let exit = Exit::new();
let parser = brk_parser::Parser::new(config.blocksdir(), rpc);
let parser = brk_parser::Parser::new(config.blocksdir(), config.brkdir(), rpc);
let format = config.format();
let mut indexer = Indexer::new(&config.outputsdir(), config.check_collisions())?;
indexer.import_stores()?;
indexer.import_vecs()?;
let mut indexer = Indexer::forced_import(&config.brkdir())?;
let wait_for_synced_node = || -> color_eyre::Result<()> {
let wait_for_synced_node = |rpc_client: &bitcoincore_rpc::Client| -> color_eyre::Result<()> {
let is_synced = || -> color_eyre::Result<bool> {
let info = rpc.get_blockchain_info()?;
let info = rpc_client.get_blockchain_info()?;
Ok(info.headers == info.blocks)
};
@@ -49,424 +36,53 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
Ok(())
};
let mut computer = Computer::new(&config.outputsdir(), config.fetcher(), format);
computer.import_stores(&indexer)?;
computer.import_vecs(&indexer, config.computation())?;
let mut computer = Computer::forced_import(
&config.brkdir(),
&indexer,
config.computation(),
config.fetcher(),
format,
)?;
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?
.block_on(async {
let server = if config.serve() {
let served_indexer = indexer.clone();
let served_computer = computer.clone();
let served_indexer = indexer.clone();
let served_computer = computer.clone();
let server = Server::new(served_indexer, served_computer, config.website())?;
let server = Server::new(served_indexer, served_computer, config.website())?;
let opt = Some(tokio::spawn(async move {
server.serve().await.unwrap();
}));
let watch = config.watch();
let mcp = config.mcp();
sleep(Duration::from_secs(1));
tokio::spawn(async move {
server.serve(watch, mcp).await.unwrap();
});
opt
} else {
None
};
sleep(Duration::from_secs(1));
if config.process() {
loop {
wait_for_synced_node()?;
loop {
wait_for_synced_node(rpc)?;
let block_count = rpc.get_block_count()?;
let block_count = rpc.get_block_count()?;
info!("{} blocks found.", block_count + 1);
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)?;
computer.compute(&mut indexer, starting_indexes, &exit)?;
if let Some(delay) = config.delay() {
sleep(Duration::from_secs(delay))
}
if let Some(delay) = config.delay() {
sleep(Duration::from_secs(delay))
}
info!("Waiting for new blocks...");
info!("Waiting for new blocks...");
while block_count == rpc.get_block_count()? {
sleep(Duration::from_secs(1))
}
while block_count == rpc.get_block_count()? {
sleep(Duration::from_secs(1))
}
}
if let Some(handle) = server {
handle.await.unwrap();
}
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
#[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>,
/// 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 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(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 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!(" services: {:?}", config.services);
// 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(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)
}
}
#[derive(
Default,
Debug,
Clone,
Copy,
Parser,
ValueEnum,
Serialize,
Deserialize,
PartialEq,
Eq,
PartialOrd,
Ord,
)]
pub enum Services {
#[default]
All,
Processor,
Server,
}
+9 -4
View File
@@ -8,6 +8,7 @@ homepage.workspace = true
repository.workspace = true
[dependencies]
bincode = { workspace = true }
bitcoin = { workspace = true }
bitcoincore-rpc = { workspace = true }
brk_core = { workspace = true }
@@ -16,10 +17,14 @@ brk_fetcher = { workspace = true }
brk_indexer = { workspace = true }
brk_logger = { workspace = true }
brk_parser = { workspace = true }
brk_state = { workspace = true }
brk_vec = { workspace = true }
brk_vecs = { workspace = true }
color-eyre = { workspace = true }
fjall = { workspace = true }
jiff = { workspace = true }
derive_deref = { workspace = true }
log = { workspace = true }
rayon = { workspace = true }
serde = { workspace = true }
zerocopy = { workspace = true }
zerocopy-derive = { workspace = true }
[package.metadata.cargo-machete]
ignored = ["zerocopy"]
+19 -13
View File
@@ -1,19 +1,19 @@
use std::{path::Path, thread};
use brk_computer::Computer;
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;
use brk_vec::{Computation, Format};
use brk_vecs::{Computation, Format};
pub fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
brk_logger::init(Some(Path::new(".log")));
let bitcoin_dir = default_bitcoin_path();
// let bitcoin_dir = brk_core::default_bitcoin_path();
let bitcoin_dir = Path::new("/Volumes/WD_BLACK/bitcoin");
let rpc = Box::leak(Box::new(bitcoincore_rpc::Client::new(
"http://localhost:8332",
@@ -23,27 +23,33 @@ pub fn main() -> color_eyre::Result<()> {
// Can't increase main thread's stack programatically, thus we need to use another thread
thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.stack_size(256 * 1024 * 1024)
.spawn(move || -> color_eyre::Result<()> {
let parser = Parser::new(bitcoin_dir.join("blocks"), rpc);
let parser = Parser::new(
bitcoin_dir.join("blocks"),
brk_core::default_brk_path(),
rpc,
);
let _outputs_dir = default_brk_path().join("outputs");
let _outputs_dir = Path::new("/Volumes/WD_BLACK/brk").join("outputs");
let outputs_dir = _outputs_dir.as_path();
// let outputs_dir = Path::new("../../_outputs");
let format = Format::Raw;
let mut indexer = Indexer::new(outputs_dir, true)?;
indexer.import_stores()?;
indexer.import_vecs()?;
let mut indexer = Indexer::forced_import(outputs_dir)?;
let fetcher = Fetcher::import(None)?;
let mut computer = Computer::new(outputs_dir, Some(fetcher), format);
computer.import_stores(&indexer)?;
computer.import_vecs(&indexer, Computation::Lazy)?;
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)?;
+2
View File
@@ -0,0 +1,2 @@
cargo build --profile profiling
flamegraph -- ../../target/profiling/examples/main
+2
View File
@@ -0,0 +1,2 @@
cargo build --example main --profile profiling
samply record ../../target/profiling/examples/main
@@ -1,24 +1,16 @@
use std::{fs, path::Path, thread};
use std::{path::Path, sync::Arc};
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 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 brk_vecs::{AnyCollectableVec, Computation, File, Format};
use log::info;
use crate::{blocks, cointime, constants, fetched, indexes, market, mining, transactions};
use super::stateful;
const VERSION: Version = Version::ONE;
#[derive(Clone)]
@@ -31,80 +23,81 @@ pub struct Vecs {
pub transactions: transactions::Vecs,
pub stateful: stateful::Vecs,
pub fetched: Option<fetched::Vecs>,
pub cointime: cointime::Vecs,
}
impl Vecs {
#[allow(clippy::too_many_arguments)]
pub fn import(
path: &Path,
file: &Arc<File>,
version: Version,
indexer: &Indexer,
fetch: bool,
computation: Computation,
format: Format,
fetched_file: &Arc<File>,
states_path: &Path,
) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
let indexes = indexes::Vecs::forced_import(
file,
version + VERSION + Version::ZERO,
indexer,
computation,
format,
)?;
let (indexes, fetched) = thread::scope(|s| {
let indexes_handle = s.spawn(|| {
indexes::Vecs::forced_import(
path,
version + VERSION + Version::ZERO,
indexer,
computation,
format,
)
.unwrap()
});
let fetch_handle = s.spawn(|| {
fetch.then(|| {
fetched::Vecs::forced_import(
path,
version + VERSION + Version::ZERO,
computation,
format,
)
.unwrap()
})
});
(indexes_handle.join().unwrap(), fetch_handle.join().unwrap())
let fetched = fetch.then(|| {
fetched::Vecs::forced_import(
file,
fetched_file,
version + VERSION + Version::ZERO,
computation,
format,
&indexes,
)
.unwrap()
});
Ok(Self {
blocks: blocks::Vecs::forced_import(
path,
file,
version + VERSION + Version::ZERO,
computation,
format,
&indexes,
)?,
mining: mining::Vecs::forced_import(
path,
file,
version + VERSION + Version::ZERO,
computation,
format,
&indexes,
)?,
constants: constants::Vecs::forced_import(
path,
file,
version + VERSION + Version::ZERO,
computation,
format,
&indexes,
)?,
market: market::Vecs::forced_import(
path,
file,
version + VERSION + Version::ZERO,
computation,
format,
&indexes,
)?,
stateful: stateful::Vecs::forced_import(
path,
file,
version + VERSION + Version::ZERO,
computation,
format,
&indexes,
fetched.as_ref(),
states_path,
)?,
transactions: transactions::Vecs::forced_import(
path,
file,
version + VERSION + Version::ZERO,
indexer,
&indexes,
@@ -112,6 +105,14 @@ impl Vecs {
format,
fetched.as_ref(),
)?,
cointime: cointime::Vecs::forced_import(
file,
version + VERSION + Version::ZERO,
computation,
format,
&indexes,
fetched.as_ref(),
)?,
indexes,
fetched,
})
@@ -125,7 +126,7 @@ impl Vecs {
exit: &Exit,
) -> color_eyre::Result<()> {
info!("Computing indexes...");
let starting_indexes = self.indexes.compute(indexer, starting_indexes, exit)?;
let mut starting_indexes = self.indexes.compute(indexer, starting_indexes, exit)?;
info!("Computing constants...");
self.constants
@@ -178,7 +179,17 @@ impl Vecs {
&self.transactions,
self.fetched.as_ref(),
&self.market,
starting_indexes,
&mut starting_indexes,
exit,
)?;
self.cointime.compute(
indexer,
&self.indexes,
&starting_indexes,
self.fetched.as_ref(),
&self.transactions,
&self.stateful,
exit,
)?;
@@ -194,6 +205,7 @@ impl 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()
@@ -1,4 +1,4 @@
use std::{fs, path::Path};
use std::sync::Arc;
use brk_core::{
CheckedSub, DifficultyEpoch, HalvingEpoch, Height, StoredU32, StoredU64, StoredUsize,
@@ -6,11 +6,13 @@ use brk_core::{
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, AnyIterableVec, Computation, EagerVec, Format};
use brk_vecs::{AnyCollectableVec, AnyIterableVec, Computation, EagerVec, File, Format};
use crate::grouped::Source;
use super::{
Indexes,
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, StorableVecGeneatorOptions},
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, VecBuilderOptions},
indexes,
};
@@ -32,93 +34,96 @@ pub struct Vecs {
impl Vecs {
pub fn forced_import(
path: &Path,
file: &Arc<File>,
version: Version,
_computation: Computation,
computation: Computation,
format: Format,
indexes: &indexes::Vecs,
) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
Ok(Self {
height_to_interval: EagerVec::forced_import(
path,
file,
"interval",
version + VERSION + Version::ZERO,
format,
)?,
timeindexes_to_timestamp: ComputedVecsFromDateIndex::forced_import(
path,
file,
"timestamp",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_first(),
computation,
indexes,
VecBuilderOptions::default().add_first(),
)?,
indexes_to_block_interval: ComputedVecsFromHeight::forced_import(
path,
file,
"block_interval",
false,
Source::None,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_percentiles()
.add_minmax()
.add_average(),
)?,
indexes_to_block_count: ComputedVecsFromHeight::forced_import(
path,
file,
"block_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
indexes_to_block_weight: ComputedVecsFromHeight::forced_import(
path,
file,
"block_weight",
false,
Source::None,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
indexes_to_block_size: ComputedVecsFromHeight::forced_import(
path,
file,
"block_size",
false,
Source::None,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
height_to_vbytes: EagerVec::forced_import(
path,
file,
"vbytes",
version + VERSION + Version::ZERO,
format,
)?,
indexes_to_block_vbytes: ComputedVecsFromHeight::forced_import(
path,
file,
"block_vbytes",
false,
Source::None,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
difficultyepoch_to_timestamp: EagerVec::forced_import(
path,
file,
"timestamp",
version + VERSION + Version::ZERO,
format,
)?,
halvingepoch_to_timestamp: EagerVec::forced_import(
path,
file,
"timestamp",
version + VERSION + Version::ZERO,
format,
@@ -154,23 +159,19 @@ impl Vecs {
starting_indexes,
exit,
|v, indexer, _, starting_indexes, exit| {
let indexer_vecs = indexer.vecs();
v.compute_range(
starting_indexes.height,
&indexer_vecs.height_to_weight,
&indexer.vecs.height_to_weight,
|h| (h, StoredU32::from(1_u32)),
exit,
)
},
)?;
let indexer_vecs = indexer.vecs();
let mut height_to_timestamp_iter = indexer_vecs.height_to_timestamp.iter();
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,
&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);
@@ -194,19 +195,19 @@ impl Vecs {
indexes,
starting_indexes,
exit,
Some(&indexer_vecs.height_to_weight),
Some(&indexer.vecs.height_to_weight),
)?;
self.indexes_to_block_size.compute_rest(
indexes,
starting_indexes,
exit,
Some(&indexer_vecs.height_to_total_size),
Some(&indexer.vecs.height_to_total_size),
)?;
self.height_to_vbytes.compute_transform(
starting_indexes.height,
&indexer_vecs.height_to_weight,
&indexer.vecs.height_to_weight,
|(h, w, ..)| {
(
h,
@@ -223,7 +224,7 @@ impl Vecs {
Some(&self.height_to_vbytes),
)?;
let mut height_to_timestamp_iter = indexer_vecs.height_to_timestamp.iter();
let mut height_to_timestamp_iter = indexer.vecs.height_to_timestamp.iter();
self.difficultyepoch_to_timestamp.compute_transform(
starting_indexes.difficultyepoch,
+717
View File
@@ -0,0 +1,717 @@
use std::sync::Arc;
use brk_core::{Bitcoin, CheckedSub, Dollars, StoredF64, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vecs::{AnyCollectableVec, Computation, File, Format, VecIterator};
use super::{
Indexes, fetched,
grouped::{
ComputedRatioVecsFromDateIndex, ComputedValueVecsFromHeight, ComputedVecsFromHeight,
Source, VecBuilderOptions,
},
indexes, stateful, transactions,
};
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(
file: &Arc<File>,
version: Version,
computation: Computation,
format: Format,
indexes: &indexes::Vecs,
fetched: Option<&fetched::Vecs>,
) -> color_eyre::Result<Self> {
let compute_dollars = fetched.is_some();
Ok(Self {
indexes_to_coinblocks_created: ComputedVecsFromHeight::forced_import(
file,
"coinblocks_created",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
indexes_to_coinblocks_stored: ComputedVecsFromHeight::forced_import(
file,
"coinblocks_stored",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
indexes_to_liveliness: ComputedVecsFromHeight::forced_import(
file,
"liveliness",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_vaultedness: ComputedVecsFromHeight::forced_import(
file,
"vaultedness",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_activity_to_vaultedness_ratio: ComputedVecsFromHeight::forced_import(
file,
"activity_to_vaultedness_ratio",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_vaulted_supply: ComputedValueVecsFromHeight::forced_import(
file,
"vaulted_supply",
Source::Compute,
version + VERSION + Version::ONE,
format,
computation,
VecBuilderOptions::default().add_last(),
compute_dollars,
indexes,
)?,
indexes_to_active_supply: ComputedValueVecsFromHeight::forced_import(
file,
"active_supply",
Source::Compute,
version + VERSION + Version::ONE,
format,
computation,
VecBuilderOptions::default().add_last(),
compute_dollars,
indexes,
)?,
indexes_to_thermo_cap: ComputedVecsFromHeight::forced_import(
file,
"thermo_cap",
Source::Compute,
version + VERSION + Version::ONE,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_investor_cap: ComputedVecsFromHeight::forced_import(
file,
"investor_cap",
Source::Compute,
version + VERSION + Version::ONE,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_vaulted_cap: ComputedVecsFromHeight::forced_import(
file,
"vaulted_cap",
Source::Compute,
version + VERSION + Version::ONE,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_active_cap: ComputedVecsFromHeight::forced_import(
file,
"active_cap",
Source::Compute,
version + VERSION + Version::ONE,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_vaulted_price: ComputedVecsFromHeight::forced_import(
file,
"vaulted_price",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_vaulted_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
file,
"vaulted_price",
Source::None,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
true,
)?,
indexes_to_active_price: ComputedVecsFromHeight::forced_import(
file,
"active_price",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_active_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
file,
"active_price",
Source::None,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
true,
)?,
indexes_to_true_market_mean: ComputedVecsFromHeight::forced_import(
file,
"true_market_mean",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_true_market_mean_ratio: ComputedRatioVecsFromDateIndex::forced_import(
file,
"true_market_mean",
Source::None,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
true,
)?,
indexes_to_cointime_value_destroyed: ComputedVecsFromHeight::forced_import(
file,
"cointime_value_destroyed",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
indexes_to_cointime_value_created: ComputedVecsFromHeight::forced_import(
file,
"cointime_value_created",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
indexes_to_cointime_value_stored: ComputedVecsFromHeight::forced_import(
file,
"cointime_value_stored",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
indexes_to_cointime_price: ComputedVecsFromHeight::forced_import(
file,
"cointime_price",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_cointime_cap: ComputedVecsFromHeight::forced_import(
file,
"cointime_cap",
Source::Compute,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_cointime_price_ratio: ComputedRatioVecsFromDateIndex::forced_import(
file,
"cointime_price",
Source::None,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
true,
)?,
})
}
#[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_cohorts.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_cohorts.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_cohorts
.all
.1
.height_to_realized_cap
.as_ref()
.unwrap();
let realized_price = stateful
.utxo_cohorts
.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<_>>()
}
}
@@ -1,13 +1,15 @@
use std::{fs, path::Path};
use std::sync::Arc;
use brk_core::{StoredU8, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, AnyVec, Computation, Format};
use brk_vecs::{AnyCollectableVec, AnyVec, Computation, File, Format};
use crate::grouped::Source;
use super::{
Indexes,
grouped::{ComputedVecsFromHeight, StorableVecGeneatorOptions},
grouped::{ComputedVecsFromHeight, VecBuilderOptions},
indexes,
};
@@ -15,53 +17,60 @@ const VERSION: Version = Version::ZERO;
#[derive(Clone)]
pub struct Vecs {
pub _0: ComputedVecsFromHeight<StoredU8>,
pub _1: ComputedVecsFromHeight<StoredU8>,
pub _50: ComputedVecsFromHeight<StoredU8>,
pub _100: ComputedVecsFromHeight<StoredU8>,
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,
file: &Arc<File>,
version: Version,
_computation: Computation,
computation: Computation,
format: Format,
indexes: &indexes::Vecs,
) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
Ok(Self {
_0: ComputedVecsFromHeight::forced_import(
path,
"0",
true,
constant_0: ComputedVecsFromHeight::forced_import(
file,
"constant_0",
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
_1: ComputedVecsFromHeight::forced_import(
path,
"1",
true,
constant_1: ComputedVecsFromHeight::forced_import(
file,
"constant_1",
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
_50: ComputedVecsFromHeight::forced_import(
path,
"50",
true,
constant_50: ComputedVecsFromHeight::forced_import(
file,
"constant_50",
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
_100: ComputedVecsFromHeight::forced_import(
path,
"100",
true,
constant_100: ComputedVecsFromHeight::forced_import(
file,
"constant_100",
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
})
}
@@ -73,7 +82,7 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> color_eyre::Result<()> {
self._0.compute_all(
self.constant_0.compute_all(
indexer,
indexes,
starting_indexes,
@@ -89,7 +98,7 @@ impl Vecs {
},
)?;
self._1.compute_all(
self.constant_1.compute_all(
indexer,
indexes,
starting_indexes,
@@ -105,7 +114,7 @@ impl Vecs {
},
)?;
self._50.compute_all(
self.constant_50.compute_all(
indexer,
indexes,
starting_indexes,
@@ -121,7 +130,7 @@ impl Vecs {
},
)?;
self._100.compute_all(
self.constant_100.compute_all(
indexer,
indexes,
starting_indexes,
@@ -142,10 +151,10 @@ impl Vecs {
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
self._0.vecs(),
self._1.vecs(),
self._50.vecs(),
self._100.vecs(),
self.constant_0.vecs(),
self.constant_1.vecs(),
self.constant_50.vecs(),
self.constant_100.vecs(),
]
.into_iter()
.flatten()
@@ -1,19 +1,23 @@
use std::{fs, path::Path};
use std::sync::Arc;
use brk_core::{
Cents, Close, DateIndex, DecadeIndex, DifficultyEpoch, Dollars, Height, High, Low, MonthIndex,
OHLCCents, OHLCDollars, OHLCSats, Open, QuarterIndex, Sats, Version, WeekIndex, YearIndex,
OHLCCents, OHLCDollars, OHLCSats, Open, QuarterIndex, Sats, SemesterIndex, Version, WeekIndex,
YearIndex,
};
use brk_exit::Exit;
use brk_fetcher::Fetcher;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, AnyIterableVec, Computation, EagerVec, Format};
use brk_vecs::{
AnyCollectableVec, AnyIterableVec, AnyVec, Computation, EagerVec, File, Format, StoredIndex,
VecIterator,
};
use crate::grouped::Source;
use super::{
Indexes,
grouped::{
ComputedVecsFromDateIndex, ComputedVecsFromHeightStrict, StorableVecGeneatorOptions,
},
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeightStrict, VecBuilderOptions},
indexes,
};
@@ -57,6 +61,8 @@ pub struct Vecs {
pub monthindex_to_ohlc_in_sats: EagerVec<MonthIndex, OHLCSats>,
pub quarterindex_to_ohlc: EagerVec<QuarterIndex, OHLCDollars>,
pub quarterindex_to_ohlc_in_sats: EagerVec<QuarterIndex, OHLCSats>,
pub semesterindex_to_ohlc: EagerVec<SemesterIndex, OHLCDollars>,
pub semesterindex_to_ohlc_in_sats: EagerVec<SemesterIndex, OHLCSats>,
pub yearindex_to_ohlc: EagerVec<YearIndex, OHLCDollars>,
pub yearindex_to_ohlc_in_sats: EagerVec<YearIndex, OHLCSats>,
// pub halvingepoch_to_ohlc: StorableVec<Halvingepoch, OHLCDollars>,
@@ -70,292 +76,316 @@ const VERSION_IN_SATS: Version = Version::ZERO;
impl Vecs {
pub fn forced_import(
path: &Path,
file: &Arc<File>,
fetched_file: &Arc<File>,
version: Version,
_computation: Computation,
computation: Computation,
format: Format,
indexes: &indexes::Vecs,
) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
let mut fetched_path = path.to_owned();
fetched_path.pop();
fetched_path = fetched_path.join("fetched");
Ok(Self {
dateindex_to_ohlc_in_cents: EagerVec::forced_import(
&fetched_path,
fetched_file,
"ohlc_in_cents",
version + VERSION + Version::ZERO,
version + Version::ZERO,
format,
)?,
dateindex_to_ohlc: EagerVec::forced_import(
path,
file,
"ohlc",
version + VERSION + Version::ZERO,
format,
)?,
dateindex_to_ohlc_in_sats: EagerVec::forced_import(
path,
file,
"ohlc_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
)?,
dateindex_to_close_in_cents: EagerVec::forced_import(
path,
file,
"close_in_cents",
version + VERSION + Version::ZERO,
format,
)?,
dateindex_to_high_in_cents: EagerVec::forced_import(
path,
file,
"high_in_cents",
version + VERSION + Version::ZERO,
format,
)?,
dateindex_to_low_in_cents: EagerVec::forced_import(
path,
file,
"low_in_cents",
version + VERSION + Version::ZERO,
format,
)?,
dateindex_to_open_in_cents: EagerVec::forced_import(
path,
file,
"open_in_cents",
version + VERSION + Version::ZERO,
format,
)?,
height_to_ohlc_in_cents: EagerVec::forced_import(
&fetched_path,
fetched_file,
"ohlc_in_cents",
version + VERSION + Version::ZERO,
version + Version::ZERO,
format,
)?,
height_to_ohlc: EagerVec::forced_import(
path,
file,
"ohlc",
version + VERSION + Version::ZERO,
format,
)?,
height_to_ohlc_in_sats: EagerVec::forced_import(
path,
file,
"ohlc_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
)?,
height_to_close_in_cents: EagerVec::forced_import(
path,
file,
"close_in_cents",
version + VERSION + Version::ZERO,
format,
)?,
height_to_high_in_cents: EagerVec::forced_import(
path,
file,
"high_in_cents",
version + VERSION + Version::ZERO,
format,
)?,
height_to_low_in_cents: EagerVec::forced_import(
path,
file,
"low_in_cents",
version + VERSION + Version::ZERO,
format,
)?,
height_to_open_in_cents: EagerVec::forced_import(
path,
file,
"open_in_cents",
version + VERSION + Version::ZERO,
format,
)?,
timeindexes_to_open: ComputedVecsFromDateIndex::forced_import(
path,
file,
"open",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_first(),
computation,
indexes,
VecBuilderOptions::default().add_first(),
)?,
timeindexes_to_high: ComputedVecsFromDateIndex::forced_import(
path,
file,
"high",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_max(),
computation,
indexes,
VecBuilderOptions::default().add_max(),
)?,
timeindexes_to_low: ComputedVecsFromDateIndex::forced_import(
path,
file,
"low",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_min(),
computation,
indexes,
VecBuilderOptions::default().add_min(),
)?,
timeindexes_to_close: ComputedVecsFromDateIndex::forced_import(
path,
file,
"close",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
timeindexes_to_open_in_sats: ComputedVecsFromDateIndex::forced_import(
path,
file,
"open_in_sats",
true,
Source::Compute,
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_first(),
computation,
indexes,
VecBuilderOptions::default().add_first(),
)?,
timeindexes_to_high_in_sats: ComputedVecsFromDateIndex::forced_import(
path,
file,
"high_in_sats",
true,
Source::Compute,
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_max(),
computation,
indexes,
VecBuilderOptions::default().add_max(),
)?,
timeindexes_to_low_in_sats: ComputedVecsFromDateIndex::forced_import(
path,
file,
"low_in_sats",
true,
Source::Compute,
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_min(),
computation,
indexes,
VecBuilderOptions::default().add_min(),
)?,
timeindexes_to_close_in_sats: ComputedVecsFromDateIndex::forced_import(
path,
file,
"close_in_sats",
true,
Source::Compute,
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
chainindexes_to_open: ComputedVecsFromHeightStrict::forced_import(
path,
file,
"open",
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_first(),
VecBuilderOptions::default().add_first(),
)?,
chainindexes_to_high: ComputedVecsFromHeightStrict::forced_import(
path,
file,
"high",
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_max(),
VecBuilderOptions::default().add_max(),
)?,
chainindexes_to_low: ComputedVecsFromHeightStrict::forced_import(
path,
file,
"low",
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_min(),
VecBuilderOptions::default().add_min(),
)?,
chainindexes_to_close: ComputedVecsFromHeightStrict::forced_import(
path,
file,
"close",
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
VecBuilderOptions::default().add_last(),
)?,
chainindexes_to_open_in_sats: ComputedVecsFromHeightStrict::forced_import(
path,
file,
"open_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_first(),
VecBuilderOptions::default().add_first(),
)?,
chainindexes_to_high_in_sats: ComputedVecsFromHeightStrict::forced_import(
path,
file,
"high_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_max(),
VecBuilderOptions::default().add_max(),
)?,
chainindexes_to_low_in_sats: ComputedVecsFromHeightStrict::forced_import(
path,
file,
"low_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_min(),
VecBuilderOptions::default().add_min(),
)?,
chainindexes_to_close_in_sats: ComputedVecsFromHeightStrict::forced_import(
path,
file,
"close_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
VecBuilderOptions::default().add_last(),
)?,
weekindex_to_ohlc: EagerVec::forced_import(
path,
file,
"ohlc",
version + VERSION + Version::ZERO,
format,
)?,
weekindex_to_ohlc_in_sats: EagerVec::forced_import(
path,
file,
"ohlc_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
)?,
difficultyepoch_to_ohlc: EagerVec::forced_import(
path,
file,
"ohlc",
version + VERSION + Version::ZERO,
format,
)?,
difficultyepoch_to_ohlc_in_sats: EagerVec::forced_import(
path,
file,
"ohlc_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
)?,
monthindex_to_ohlc: EagerVec::forced_import(
path,
file,
"ohlc",
version + VERSION + Version::ZERO,
format,
)?,
monthindex_to_ohlc_in_sats: EagerVec::forced_import(
path,
file,
"ohlc_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
)?,
quarterindex_to_ohlc: EagerVec::forced_import(
path,
file,
"ohlc",
version + VERSION + Version::ZERO,
format,
)?,
quarterindex_to_ohlc_in_sats: EagerVec::forced_import(
path,
file,
"ohlc_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
)?,
semesterindex_to_ohlc: EagerVec::forced_import(
file,
"ohlc",
version + VERSION + Version::ZERO,
format,
)?,
semesterindex_to_ohlc_in_sats: EagerVec::forced_import(
file,
"ohlc_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
)?,
yearindex_to_ohlc: EagerVec::forced_import(
path,
file,
"ohlc",
version + VERSION + Version::ZERO,
format,
)?,
yearindex_to_ohlc_in_sats: EagerVec::forced_import(
path,
file,
"ohlc_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
)?,
// halvingepoch_to_ohlc: StorableVec::forced_import(path,
// halvingepoch_to_ohlc: StorableVec::forced_import(file,
// "halvingepoch_to_ohlc"), version + VERSION + Version::ZERO, format)?,
decadeindex_to_ohlc: EagerVec::forced_import(
path,
file,
"ohlc",
version + VERSION + Version::ZERO,
format,
)?,
decadeindex_to_ohlc_in_sats: EagerVec::forced_import(
path,
file,
"ohlc_in_sats",
version + VERSION + VERSION_IN_SATS + Version::ZERO,
format,
@@ -371,12 +401,10 @@ impl Vecs {
fetcher: &mut Fetcher,
exit: &Exit,
) -> color_eyre::Result<()> {
let indexer_vecs = indexer.vecs();
let mut height_to_timestamp_iter = indexer_vecs.height_to_timestamp.iter();
let mut height_to_timestamp_iter = indexer.vecs.height_to_timestamp.iter();
self.height_to_ohlc_in_cents.compute_transform(
starting_indexes.height,
&indexer_vecs.height_to_timestamp,
&indexer.vecs.height_to_timestamp,
|(h, t, ..)| {
let ohlc = fetcher
.get_height(
@@ -426,11 +454,34 @@ impl Vecs {
exit,
)?;
let mut prev = None;
self.dateindex_to_ohlc_in_cents.compute_transform(
starting_indexes.dateindex,
&indexes.dateindex_to_date,
|(di, d, ..)| {
let ohlc = fetcher.get_date(d).unwrap();
|(di, d, this)| {
if prev.is_none() {
let i = di.unwrap_to_usize();
prev.replace(if i > 0 {
this.into_iter().unwrap_get_inner_(i - 1)
} else {
OHLCCents::default()
});
}
let ohlc = if di.unwrap_to_usize() + 100 >= this.len()
&& let Ok(mut ohlc) = fetcher.get_date(d)
{
let prev_open = *prev.as_ref().unwrap().close;
*ohlc.open = prev_open;
*ohlc.high = (*ohlc.high).max(prev_open);
*ohlc.low = (*ohlc.low).min(prev_open);
ohlc
} else {
prev.clone().unwrap()
};
prev.replace(ohlc.clone());
(di, ohlc)
},
exit,
@@ -598,12 +649,15 @@ impl Vecs {
starting_indexes.weekindex,
self.timeindexes_to_close.weekindex.unwrap_last(),
|(i, close, ..)| {
let open = weekindex_first_iter.unwrap_get_inner(i);
let high = weekindex_max_iter.unwrap_get_inner(i);
let low = weekindex_min_iter.unwrap_get_inner(i);
(
i,
OHLCDollars {
open: weekindex_first_iter.unwrap_get_inner(i),
high: weekindex_max_iter.unwrap_get_inner(i),
low: weekindex_min_iter.unwrap_get_inner(i),
open,
high,
low,
close,
},
)
@@ -681,6 +735,27 @@ impl Vecs {
exit,
)?;
let mut semesterindex_first_iter =
self.timeindexes_to_open.semesterindex.unwrap_first().iter();
let mut semesterindex_max_iter = self.timeindexes_to_high.semesterindex.unwrap_max().iter();
let mut semesterindex_min_iter = self.timeindexes_to_low.semesterindex.unwrap_min().iter();
self.semesterindex_to_ohlc.compute_transform(
starting_indexes.semesterindex,
self.timeindexes_to_close.semesterindex.unwrap_last(),
|(i, close, ..)| {
(
i,
OHLCDollars {
open: semesterindex_first_iter.unwrap_get_inner(i),
high: semesterindex_max_iter.unwrap_get_inner(i),
low: semesterindex_min_iter.unwrap_get_inner(i),
close,
},
)
},
exit,
)?;
let mut yearindex_first_iter = self.timeindexes_to_open.yearindex.unwrap_first().iter();
let mut yearindex_max_iter = self.timeindexes_to_high.yearindex.unwrap_max().iter();
let mut yearindex_min_iter = self.timeindexes_to_low.yearindex.unwrap_min().iter();
@@ -1032,6 +1107,40 @@ impl Vecs {
exit,
)?;
let mut semesterindex_first_iter = self
.timeindexes_to_open_in_sats
.semesterindex
.unwrap_first()
.iter();
let mut semesterindex_max_iter = self
.timeindexes_to_high_in_sats
.semesterindex
.unwrap_max()
.iter();
let mut semesterindex_min_iter = self
.timeindexes_to_low_in_sats
.semesterindex
.unwrap_min()
.iter();
self.semesterindex_to_ohlc_in_sats.compute_transform(
starting_indexes.semesterindex,
self.timeindexes_to_close_in_sats
.semesterindex
.unwrap_last(),
|(i, close, ..)| {
(
i,
OHLCSats {
open: semesterindex_first_iter.unwrap_get_inner(i),
high: semesterindex_max_iter.unwrap_get_inner(i),
low: semesterindex_min_iter.unwrap_get_inner(i),
close,
},
)
},
exit,
)?;
let mut yearindex_first_iter = self
.timeindexes_to_open_in_sats
.yearindex
@@ -1121,6 +1230,7 @@ impl Vecs {
&self.difficultyepoch_to_ohlc,
&self.monthindex_to_ohlc,
&self.quarterindex_to_ohlc,
&self.semesterindex_to_ohlc,
&self.yearindex_to_ohlc,
// &self.halvingepoch_to_ohlc,
&self.decadeindex_to_ohlc,
@@ -1130,6 +1240,7 @@ impl Vecs {
&self.difficultyepoch_to_ohlc_in_sats,
&self.monthindex_to_ohlc_in_sats,
&self.quarterindex_to_ohlc_in_sats,
&self.semesterindex_to_ohlc_in_sats,
&self.yearindex_to_ohlc_in_sats,
// &self.halvingepoch_to_ohlc_in_sats,
&self.decadeindex_to_ohlc_in_sats,
@@ -0,0 +1,474 @@
use std::sync::Arc;
use brk_core::{FromCoarserIndex, Result, Version};
use brk_exit::Exit;
use brk_vecs::{
AnyCollectableVec, AnyIterableVec, BoxedAnyIterableVec, CloneableAnyIterableVec, Computation,
ComputedVec, ComputedVecFrom2, File, Format, StoredIndex,
};
use crate::grouped::{EagerVecBuilder, VecBuilderOptions};
use super::ComputedType;
#[allow(clippy::type_complexity)]
#[derive(Clone)]
pub struct ComputedVecBuilder<I, T, S1I, S2T>
where
I: StoredIndex,
T: ComputedType,
S2T: ComputedType,
{
pub first: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
pub average: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
pub sum: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
pub max: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
pub min: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
pub last: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
pub cumulative: Option<Box<ComputedVecFrom2<I, T, S1I, T, I, S2T>>>,
}
const VERSION: Version = Version::ZERO;
impl<I, T, S1I, S2T> ComputedVecBuilder<I, T, S1I, S2T>
where
I: StoredIndex,
T: ComputedType + 'static,
S1I: StoredIndex + 'static + FromCoarserIndex<I>,
S2T: ComputedType,
{
#[allow(clippy::too_many_arguments)]
pub fn forced_import(
file: &Arc<File>,
name: &str,
version: Version,
format: Format,
computation: Computation,
source: Option<BoxedAnyIterableVec<S1I, T>>,
source_extra: &EagerVecBuilder<S1I, T>,
len_source: BoxedAnyIterableVec<I, S2T>,
options: ComputedVecBuilderOptions,
) -> 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)
}
};
Ok(Self {
first: options.first.then(|| {
Box::new(
ComputedVec::forced_import_or_init_from_2(
computation,
file,
&maybe_suffix("first"),
version + VERSION + Version::ZERO,
format,
source_extra
.first
.as_ref()
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
len_source.clone(),
|i: I, source, len_source| {
if i.unwrap_to_usize() >= len_source.len() {
return None;
}
source
.next_at(S1I::min_from(i))
.map(|(_, cow)| cow.into_owned())
},
)
.unwrap(),
)
}),
last: options.last.then(|| {
Box::new(
ComputedVec::forced_import_or_init_from_2(
computation,
file,
name,
version + VERSION + Version::ZERO,
format,
source_extra.last.as_ref().map_or_else(
|| {
source
.as_ref()
.unwrap_or_else(|| {
dbg!(file, name, I::to_string());
panic!()
})
.clone()
},
|v| v.clone(),
),
len_source.clone(),
|i: I, source, len_source| {
if i.unwrap_to_usize() >= len_source.len() {
return None;
}
source
.next_at(S1I::max_from(i, source.len()))
.map(|(_, cow)| cow.into_owned())
},
)
.unwrap(),
)
}),
min: options.min.then(|| {
Box::new(
ComputedVec::forced_import_or_init_from_2(
computation,
file,
&maybe_suffix("min"),
version + VERSION + Version::ZERO,
format,
source_extra
.min
.as_ref()
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
len_source.clone(),
|i: I, source, len_source| {
if i.unwrap_to_usize() >= len_source.len() {
return None;
}
S1I::inclusive_range_from(i, source.len())
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
.min()
},
)
.unwrap(),
)
}),
max: options.max.then(|| {
Box::new(
ComputedVec::forced_import_or_init_from_2(
computation,
file,
&maybe_suffix("max"),
version + VERSION + Version::ZERO,
format,
source_extra
.max
.as_ref()
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
len_source.clone(),
|i: I, source, len_source| {
if i.unwrap_to_usize() >= len_source.len() {
return None;
}
S1I::inclusive_range_from(i, source.len())
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
.max()
},
)
.unwrap(),
)
}),
average: options.average.then(|| {
Box::new(
ComputedVec::forced_import_or_init_from_2(
computation,
file,
&maybe_suffix("average"),
version + VERSION + Version::ZERO,
format,
source_extra
.average
.as_ref()
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
len_source.clone(),
|i: I, source, len_source| {
if i.unwrap_to_usize() >= len_source.len() {
return None;
}
let vec = S1I::inclusive_range_from(i, source.len())
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
.collect::<Vec<_>>();
if vec.is_empty() {
return None;
}
let mut sum = T::from(0);
let len = vec.len();
vec.into_iter().for_each(|v| sum += v);
Some(sum / len)
},
)
.unwrap(),
)
}),
sum: options.sum.then(|| {
Box::new(
ComputedVec::forced_import_or_init_from_2(
computation,
file,
&(if !options.last && !options.average && !options.min && !options.max {
name.to_string()
} else {
maybe_suffix("sum")
}),
version + VERSION + Version::ZERO,
format,
source_extra
.sum
.as_ref()
.map_or_else(|| source.as_ref().unwrap().clone(), |v| v.clone()),
len_source.clone(),
|i: I, source, len_source| {
if i.unwrap_to_usize() >= len_source.len() {
return None;
}
let vec = S1I::inclusive_range_from(i, source.len())
.flat_map(|i| source.next_at(i).map(|(_, cow)| cow.into_owned()))
.collect::<Vec<_>>();
if vec.is_empty() {
return None;
}
let mut sum = T::from(0);
vec.into_iter().for_each(|v| sum += v);
Some(sum)
},
)
.unwrap(),
)
}),
cumulative: options.cumulative.then(|| {
Box::new(
ComputedVec::forced_import_or_init_from_2(
computation,
file,
&suffix("cumulative"),
version + VERSION + Version::ZERO,
format,
source_extra.cumulative.as_ref().unwrap().boxed_clone(),
len_source.clone(),
|i: I, source, len_source| {
if i.unwrap_to_usize() >= len_source.len() {
return None;
}
source
.next_at(S1I::max_from(i, source.len()))
.map(|(_, cow)| cow.into_owned())
},
)
.unwrap(),
)
}),
})
}
pub fn compute_if_necessary<T2>(
&mut self,
max_from: I,
len_source: &impl AnyIterableVec<I, T2>,
exit: &Exit,
) -> Result<()> {
if let Some(first) = self.first.as_mut() {
first.compute_if_necessary(max_from, len_source, exit)?;
}
if let Some(last) = self.last.as_mut() {
last.compute_if_necessary(max_from, len_source, exit)?;
}
if let Some(min) = self.min.as_mut() {
min.compute_if_necessary(max_from, len_source, exit)?;
}
if let Some(max) = self.max.as_mut() {
max.compute_if_necessary(max_from, len_source, exit)?;
}
if let Some(average) = self.average.as_mut() {
average.compute_if_necessary(max_from, len_source, exit)?;
}
if let Some(sum) = self.sum.as_mut() {
sum.compute_if_necessary(max_from, len_source, exit)?;
}
if let Some(cumulative) = self.cumulative.as_mut() {
cumulative.compute_if_necessary(max_from, len_source, 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) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
self.first.as_ref().unwrap()
}
#[allow(unused)]
pub fn unwrap_average(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
self.average.as_ref().unwrap()
}
pub fn unwrap_sum(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
self.sum.as_ref().unwrap()
}
pub fn unwrap_max(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
self.max.as_ref().unwrap()
}
pub fn unwrap_min(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
self.min.as_ref().unwrap()
}
pub fn unwrap_last(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
self.last.as_ref().unwrap()
}
#[allow(unused)]
pub fn unwrap_cumulative(&self) -> &ComputedVecFrom2<I, T, S1I, T, I, S2T> {
self.cumulative.as_ref().unwrap()
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
let mut v: Vec<&dyn AnyCollectableVec> = vec![];
if let Some(first) = self.first.as_ref() {
v.push(first.as_ref());
}
if let Some(last) = self.last.as_ref() {
v.push(last.as_ref());
}
if let Some(min) = self.min.as_ref() {
v.push(min.as_ref());
}
if let Some(max) = self.max.as_ref() {
v.push(max.as_ref());
}
if let Some(average) = self.average.as_ref() {
v.push(average.as_ref());
}
if let Some(sum) = self.sum.as_ref() {
v.push(sum.as_ref());
}
if let Some(cumulative) = self.cumulative.as_ref() {
v.push(cumulative.as_ref());
}
v
}
}
#[derive(Default, Clone, Copy)]
pub struct ComputedVecBuilderOptions {
average: bool,
sum: bool,
max: bool,
min: bool,
first: bool,
last: bool,
cumulative: bool,
}
impl From<VecBuilderOptions> for ComputedVecBuilderOptions {
fn from(value: VecBuilderOptions) -> Self {
Self {
average: value.average(),
sum: value.sum(),
max: value.max(),
min: value.min(),
first: value.first(),
last: value.last(),
cumulative: value.cumulative(),
}
}
}
impl ComputedVecBuilderOptions {
pub fn add_first(mut self) -> Self {
self.first = true;
self
}
pub fn add_last(mut self) -> Self {
self.last = true;
self
}
pub fn add_min(mut self) -> Self {
self.min = true;
self
}
pub fn add_max(mut self) -> Self {
self.max = true;
self
}
pub fn add_average(mut self) -> Self {
self.average = true;
self
}
pub fn add_sum(mut self) -> Self {
self.sum = true;
self
}
pub fn add_cumulative(mut self) -> Self {
self.cumulative = true;
self
}
#[allow(unused)]
pub fn rm_min(mut self) -> Self {
self.min = false;
self
}
#[allow(unused)]
pub fn rm_max(mut self) -> Self {
self.max = false;
self
}
#[allow(unused)]
pub fn rm_average(mut self) -> Self {
self.average = false;
self
}
#[allow(unused)]
pub fn rm_sum(mut self) -> Self {
self.sum = false;
self
}
#[allow(unused)]
pub fn rm_cumulative(mut self) -> Self {
self.cumulative = false;
self
}
pub fn add_minmax(mut self) -> Self {
self.min = true;
self.max = true;
self
}
pub fn is_only_one_active(&self) -> bool {
[
self.average,
self.sum,
self.max,
self.min,
self.first,
self.last,
self.cumulative,
]
.iter()
.filter(|b| **b)
.count()
== 1
}
pub fn copy_self_extra(&self) -> Self {
Self {
cumulative: self.cumulative,
..Self::default()
}
}
}
@@ -1,8 +1,10 @@
use std::path::Path;
use std::sync::Arc;
use brk_core::{CheckedSub, Result, StoredUsize, Version};
use brk_exit::Exit;
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format, StoredIndex, StoredType};
use brk_vecs::{
AnyCollectableVec, AnyIterableVec, AnyVec, EagerVec, File, Format, StoredIndex, StoredType,
};
use color_eyre::eyre::ContextCompat;
use crate::utils::get_percentile;
@@ -10,7 +12,7 @@ use crate::utils::get_percentile;
use super::ComputedType;
#[derive(Clone, Debug)]
pub struct ComputedVecBuilder<I, T>
pub struct EagerVecBuilder<I, T>
where
I: StoredIndex,
T: ComputedType,
@@ -31,30 +33,20 @@ where
const VERSION: Version = Version::ZERO;
impl<I, T> ComputedVecBuilder<I, T>
impl<I, T> EagerVecBuilder<I, T>
where
I: StoredIndex,
T: ComputedType,
{
pub fn forced_import(
path: &Path,
file: &Arc<File>,
name: &str,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
options: VecBuilderOptions,
) -> color_eyre::Result<Self> {
let only_one_active = options.is_only_one_active();
let prefix = |s: &str| format!("{s}_{name}");
let maybe_prefix = |s: &str| {
if only_one_active {
name.to_string()
} else {
prefix(s)
}
};
let suffix = |s: &str| format!("{name}_{s}");
let maybe_suffix = |s: &str| {
@@ -69,8 +61,8 @@ where
first: options.first.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_prefix("first"),
file,
&maybe_suffix("first"),
version + VERSION + Version::ZERO,
format,
)
@@ -79,13 +71,13 @@ where
}),
last: options.last.then(|| {
Box::new(
EagerVec::forced_import(path, name, version + Version::ZERO, format).unwrap(),
EagerVec::forced_import(file, name, version + Version::ZERO, format).unwrap(),
)
}),
min: options.min.then(|| {
Box::new(
EagerVec::forced_import(
path,
file,
&maybe_suffix("min"),
version + VERSION + Version::ZERO,
format,
@@ -96,7 +88,7 @@ where
max: options.max.then(|| {
Box::new(
EagerVec::forced_import(
path,
file,
&maybe_suffix("max"),
version + VERSION + Version::ZERO,
format,
@@ -107,7 +99,7 @@ where
median: options.median.then(|| {
Box::new(
EagerVec::forced_import(
path,
file,
&maybe_suffix("median"),
version + VERSION + Version::ZERO,
format,
@@ -118,7 +110,7 @@ where
average: options.average.then(|| {
Box::new(
EagerVec::forced_import(
path,
file,
&maybe_suffix("average"),
version + VERSION + Version::ZERO,
format,
@@ -129,8 +121,12 @@ where
sum: options.sum.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_suffix("sum"),
file,
&(if !options.last && !options.average && !options.min && !options.max {
name.to_string()
} else {
maybe_suffix("sum")
}),
version + VERSION + Version::ZERO,
format,
)
@@ -140,8 +136,8 @@ where
cumulative: options.cumulative.then(|| {
Box::new(
EagerVec::forced_import(
path,
&prefix("cumulative"),
file,
&suffix("cumulative"),
version + VERSION + Version::ZERO,
format,
)
@@ -151,7 +147,7 @@ where
_90p: options._90p.then(|| {
Box::new(
EagerVec::forced_import(
path,
file,
&maybe_suffix("90p"),
version + VERSION + Version::ZERO,
format,
@@ -162,7 +158,7 @@ where
_75p: options._75p.then(|| {
Box::new(
EagerVec::forced_import(
path,
file,
&maybe_suffix("75p"),
version + VERSION + Version::ZERO,
format,
@@ -173,7 +169,7 @@ where
_25p: options._25p.then(|| {
Box::new(
EagerVec::forced_import(
path,
file,
&maybe_suffix("25p"),
version + VERSION + Version::ZERO,
format,
@@ -184,7 +180,7 @@ where
_10p: options._10p.then(|| {
Box::new(
EagerVec::forced_import(
path,
file,
&maybe_suffix("10p"),
version + VERSION + Version::ZERO,
format,
@@ -217,7 +213,7 @@ where
cumulative_vec.iter().unwrap_get_inner(index)
});
source.iter_at(index).try_for_each(|(i, v)| -> Result<()> {
cumulative = cumulative.clone() + v.into_inner();
cumulative = cumulative.clone() + v.into_owned();
cumulative_vec.forced_push_at(i, cumulative.clone(), exit)
})?;
@@ -257,7 +253,7 @@ where
first_indexes
.iter_at(index)
.try_for_each(|(i, first_index)| -> Result<()> {
let first_index = first_index.into_inner();
let first_index = first_index.into_owned();
let count_index = count_indexes_iter.unwrap_get_inner(i);
@@ -280,7 +276,7 @@ where
// dbg!(first_index, count_index, last_index);
// })
// .unwrap()
// .into_inner();
// .into_owned();
last.forced_push_at(index, v, exit)?;
}
@@ -300,7 +296,7 @@ where
source_iter.set(first_index);
let mut values = (&mut source_iter)
.take(*count_index)
.map(|(_, v)| v.into_inner())
.map(|(_, v)| v.into_owned())
.collect::<Vec<_>>();
if needs_sorted {
@@ -315,7 +311,7 @@ where
.inspect_err(|_| {
dbg!(
&values,
max.path(),
max.name(),
first_indexes.name(),
first_index,
count_indexes.name(),
@@ -390,7 +386,7 @@ where
pub fn from_aligned<I2>(
&mut self,
max_from: I,
source: &ComputedVecBuilder<I2, T>,
source: &EagerVecBuilder<I2, T>,
first_indexes: &impl AnyIterableVec<I, I2>,
count_indexes: &impl AnyIterableVec<I, StoredUsize>,
exit: &Exit,
@@ -431,7 +427,7 @@ where
first_indexes
.iter_at(index)
.try_for_each(|(i, first_index, ..)| -> Result<()> {
let first_index = first_index.into_inner();
let first_index = first_index.into_owned();
let count_index = count_indexes_iter.unwrap_get_inner(i);
@@ -469,7 +465,7 @@ where
source_max_iter.set(first_index);
let mut values = source_max_iter
.take(*count_index)
.map(|(_, v)| v.into_inner())
.map(|(_, v)| v.into_owned())
.collect::<Vec<_>>();
values.sort_unstable();
max.forced_push_at(i, values.last().unwrap().clone(), exit)?;
@@ -480,7 +476,7 @@ where
source_min_iter.set(first_index);
let mut values = source_min_iter
.take(*count_index)
.map(|(_, v)| v.into_inner())
.map(|(_, v)| v.into_owned())
.collect::<Vec<_>>();
values.sort_unstable();
min.forced_push_at(i, values.first().unwrap().clone(), exit)?;
@@ -493,7 +489,7 @@ where
source_average_iter.set(first_index);
let values = source_average_iter
.take(*count_index)
.map(|(_, v)| v.into_inner())
.map(|(_, v)| v.into_owned())
.collect::<Vec<_>>();
let len = values.len();
@@ -509,7 +505,7 @@ where
source_sum_iter.set(first_index);
let values = source_sum_iter
.take(*count_index)
.map(|(_, v)| v.into_inner())
.map(|(_, v)| v.into_owned())
.collect::<Vec<_>>();
let sum = values.into_iter().fold(T::from(0), |a, b| a + b);
@@ -712,7 +708,7 @@ where
}
#[derive(Default, Clone, Copy)]
pub struct StorableVecGeneatorOptions {
pub struct VecBuilderOptions {
average: bool,
sum: bool,
max: bool,
@@ -727,7 +723,55 @@ pub struct StorableVecGeneatorOptions {
cumulative: bool,
}
impl StorableVecGeneatorOptions {
impl VecBuilderOptions {
pub fn average(&self) -> bool {
self.average
}
pub fn sum(&self) -> bool {
self.sum
}
pub fn max(&self) -> bool {
self.max
}
pub fn _90p(&self) -> bool {
self._90p
}
pub fn _75p(&self) -> bool {
self._75p
}
pub fn median(&self) -> bool {
self.median
}
pub fn _25p(&self) -> bool {
self._25p
}
pub fn _10p(&self) -> bool {
self._10p
}
pub fn min(&self) -> bool {
self.min
}
pub fn first(&self) -> bool {
self.first
}
pub fn last(&self) -> bool {
self.last
}
pub fn cumulative(&self) -> bool {
self.cumulative
}
pub fn add_first(mut self) -> Self {
self.first = true;
self
@@ -1,15 +1,18 @@
use std::path::Path;
use std::sync::Arc;
use brk_core::{
DateIndex, DecadeIndex, MonthIndex, QuarterIndex, Result, Version, WeekIndex, YearIndex,
DateIndex, DecadeIndex, MonthIndex, QuarterIndex, Result, SemesterIndex, Version, WeekIndex,
YearIndex,
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format};
use brk_vecs::{
AnyCollectableVec, AnyIterableVec, CloneableAnyIterableVec, Computation, EagerVec, File, Format,
};
use crate::vecs::{Indexes, indexes};
use crate::{Indexes, grouped::ComputedVecBuilder, indexes};
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
use super::{ComputedType, EagerVecBuilder, Source, VecBuilderOptions};
#[derive(Clone)]
pub struct ComputedVecsFromDateIndex<T>
@@ -17,34 +20,38 @@ 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>,
pub dateindex_extra: EagerVecBuilder<DateIndex, T>,
pub weekindex: ComputedVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
pub monthindex: ComputedVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
pub quarterindex: ComputedVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
pub semesterindex: ComputedVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
pub yearindex: ComputedVecBuilder<YearIndex, T, DateIndex, YearIndex>,
pub decadeindex: ComputedVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromDateIndex<T>
where
T: ComputedType,
T: ComputedType + 'static,
{
#[allow(clippy::too_many_arguments)]
pub fn forced_import(
path: &Path,
file: &Arc<File>,
name: &str,
compute_source: bool,
source: Source<DateIndex, T>,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
computation: Computation,
indexes: &indexes::Vecs,
options: VecBuilderOptions,
) -> color_eyre::Result<Self> {
let dateindex = compute_source.then(|| {
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format).unwrap()
let dateindex = source.is_compute().then(|| {
EagerVec::forced_import(file, name, version + VERSION + Version::ZERO, format).unwrap()
});
let dateindex_extra = ComputedVecBuilder::forced_import(
path,
let dateindex_extra = EagerVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
@@ -53,44 +60,77 @@ where
let options = options.remove_percentiles();
let dateindex_source = source.vec().or(dateindex.as_ref().map(|v| v.boxed_clone()));
Ok(Self {
dateindex,
dateindex_extra,
weekindex: ComputedVecBuilder::forced_import(
path,
file,
name,
version + VERSION + Version::ZERO,
format,
options,
computation,
dateindex_source.clone(),
&dateindex_extra,
indexes.weekindex_to_weekindex.boxed_clone(),
options.into(),
)?,
monthindex: ComputedVecBuilder::forced_import(
path,
file,
name,
version + VERSION + Version::ZERO,
format,
options,
Computation::Lazy,
dateindex_source.clone(),
&dateindex_extra,
indexes.monthindex_to_monthindex.boxed_clone(),
options.into(),
)?,
quarterindex: ComputedVecBuilder::forced_import(
path,
file,
name,
version + VERSION + Version::ZERO,
format,
options,
Computation::Lazy,
dateindex_source.clone(),
&dateindex_extra,
indexes.quarterindex_to_quarterindex.boxed_clone(),
options.into(),
)?,
semesterindex: ComputedVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
Computation::Lazy,
dateindex_source.clone(),
&dateindex_extra,
indexes.semesterindex_to_semesterindex.boxed_clone(),
options.into(),
)?,
yearindex: ComputedVecBuilder::forced_import(
path,
file,
name,
version + VERSION + Version::ZERO,
format,
options,
Computation::Lazy,
dateindex_source.clone(),
&dateindex_extra,
indexes.yearindex_to_yearindex.boxed_clone(),
options.into(),
)?,
decadeindex: ComputedVecBuilder::forced_import(
path,
file,
name,
version + VERSION + Version::ZERO,
format,
options,
Computation::Lazy,
dateindex_source.clone(),
&dateindex_extra,
indexes.decadeindex_to_decadeindex.boxed_clone(),
options.into(),
)?,
dateindex,
dateindex_extra,
})
}
@@ -133,65 +173,45 @@ where
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(
self.weekindex.compute_if_necessary(
starting_indexes.weekindex,
&indexes.weekindex_to_dateindex_count,
exit,
)?;
self.monthindex.compute_if_necessary(
starting_indexes.monthindex,
&indexes.monthindex_to_dateindex_count,
exit,
)?;
self.quarterindex.compute_if_necessary(
starting_indexes.quarterindex,
&self.monthindex,
&indexes.quarterindex_to_first_monthindex,
&indexes.quarterindex_to_monthindex_count,
exit,
)?;
self.yearindex.from_aligned(
self.semesterindex.compute_if_necessary(
starting_indexes.semesterindex,
&indexes.semesterindex_to_monthindex_count,
exit,
)?;
self.yearindex.compute_if_necessary(
starting_indexes.yearindex,
&self.monthindex,
&indexes.yearindex_to_first_monthindex,
&indexes.yearindex_to_monthindex_count,
exit,
)?;
self.decadeindex.from_aligned(
self.decadeindex.compute_if_necessary(
starting_indexes.decadeindex,
&self.yearindex,
&indexes.decadeindex_to_first_yearindex,
&indexes.decadeindex_to_yearindex_count,
exit,
)?;
@@ -208,6 +228,7 @@ where
self.weekindex.vecs(),
self.monthindex.vecs(),
self.quarterindex.vecs(),
self.semesterindex.vecs(),
self.yearindex.vecs(),
self.decadeindex.vecs(),
]
@@ -1,16 +1,22 @@
use std::path::Path;
use std::sync::Arc;
use brk_core::{
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, Result, Version,
WeekIndex, YearIndex,
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, Result,
SemesterIndex, Version, WeekIndex, YearIndex,
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format};
use brk_vecs::{
AnyCollectableVec, AnyIterableVec, CloneableAnyIterableVec, Computation, EagerVec, File, Format,
};
use crate::vecs::{Indexes, indexes};
use crate::{
Indexes,
grouped::{ComputedVecBuilder, Source},
indexes,
};
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
use super::{ComputedType, EagerVecBuilder, VecBuilderOptions};
#[derive(Clone)]
pub struct ComputedVecsFromHeight<T>
@@ -18,46 +24,50 @@ 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>,
pub yearindex: ComputedVecBuilder<YearIndex, T>,
pub height_extra: EagerVecBuilder<Height, T>,
pub dateindex: EagerVecBuilder<DateIndex, T>,
pub weekindex: ComputedVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
pub difficultyepoch: EagerVecBuilder<DifficultyEpoch, T>,
pub monthindex: ComputedVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
pub quarterindex: ComputedVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
pub semesterindex: ComputedVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
pub yearindex: ComputedVecBuilder<YearIndex, T, DateIndex, YearIndex>,
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
pub decadeindex: ComputedVecBuilder<DecadeIndex, T>,
pub decadeindex: ComputedVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromHeight<T>
where
T: ComputedType + Ord + From<f64>,
T: ComputedType + Ord + From<f64> + 'static,
f64: From<T>,
{
#[allow(clippy::too_many_arguments)]
pub fn forced_import(
path: &Path,
file: &Arc<File>,
name: &str,
compute_source: bool,
source: Source<Height, T>,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
computation: Computation,
indexes: &indexes::Vecs,
options: VecBuilderOptions,
) -> color_eyre::Result<Self> {
let height = compute_source.then(|| {
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format).unwrap()
let height = source.is_compute().then(|| {
EagerVec::forced_import(file, name, version + VERSION + Version::ZERO, format).unwrap()
});
let height_extra = ComputedVecBuilder::forced_import(
path,
let height_extra = EagerVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
options.copy_self_extra(),
)?;
let dateindex = ComputedVecBuilder::forced_import(
path,
let dateindex = EagerVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
@@ -67,47 +77,78 @@ where
let options = options.remove_percentiles();
Ok(Self {
weekindex: ComputedVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
computation,
None,
&dateindex,
indexes.weekindex_to_weekindex.boxed_clone(),
options.into(),
)?,
monthindex: ComputedVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
Computation::Lazy,
None,
&dateindex,
indexes.monthindex_to_monthindex.boxed_clone(),
options.into(),
)?,
quarterindex: ComputedVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
Computation::Lazy,
None,
&dateindex,
indexes.quarterindex_to_quarterindex.boxed_clone(),
options.into(),
)?,
semesterindex: ComputedVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
Computation::Lazy,
None,
&dateindex,
indexes.semesterindex_to_semesterindex.boxed_clone(),
options.into(),
)?,
yearindex: ComputedVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
Computation::Lazy,
None,
&dateindex,
indexes.yearindex_to_yearindex.boxed_clone(),
options.into(),
)?,
decadeindex: ComputedVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
Computation::Lazy,
None,
&dateindex,
indexes.decadeindex_to_decadeindex.boxed_clone(),
options.into(),
)?,
// halvingepoch: StorableVecGeneator::forced_import(file, name, version + VERSION + Version::ZERO, format, options)?,
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,
difficultyepoch: EagerVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
@@ -144,9 +185,9 @@ where
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
height: Option<&impl AnyIterableVec<Height, T>>,
height_vec: Option<&impl AnyIterableVec<Height, T>>,
) -> color_eyre::Result<()> {
if let Some(height) = height {
if let Some(height) = height_vec {
self.height_extra
.extend(starting_indexes.height, height, exit)?;
@@ -188,42 +229,38 @@ where
)?;
}
self.weekindex.from_aligned(
self.weekindex.compute_if_necessary(
starting_indexes.weekindex,
&self.dateindex,
&indexes.weekindex_to_first_dateindex,
&indexes.weekindex_to_dateindex_count,
exit,
)?;
self.monthindex.from_aligned(
self.monthindex.compute_if_necessary(
starting_indexes.monthindex,
&self.dateindex,
&indexes.monthindex_to_first_dateindex,
&indexes.monthindex_to_dateindex_count,
exit,
)?;
self.quarterindex.from_aligned(
self.quarterindex.compute_if_necessary(
starting_indexes.quarterindex,
&self.monthindex,
&indexes.quarterindex_to_first_monthindex,
&indexes.quarterindex_to_monthindex_count,
exit,
)?;
self.yearindex.from_aligned(
self.semesterindex.compute_if_necessary(
starting_indexes.semesterindex,
&indexes.semesterindex_to_monthindex_count,
exit,
)?;
self.yearindex.compute_if_necessary(
starting_indexes.yearindex,
&self.monthindex,
&indexes.yearindex_to_first_monthindex,
&indexes.yearindex_to_monthindex_count,
exit,
)?;
self.decadeindex.from_aligned(
self.decadeindex.compute_if_necessary(
starting_indexes.decadeindex,
&self.yearindex,
&indexes.decadeindex_to_first_yearindex,
&indexes.decadeindex_to_yearindex_count,
exit,
)?;
@@ -242,6 +279,7 @@ where
self.difficultyepoch.vecs(),
self.monthindex.vecs(),
self.quarterindex.vecs(),
self.semesterindex.vecs(),
self.yearindex.vecs(),
// self.halvingepoch.vecs(),
self.decadeindex.vecs(),
@@ -1,13 +1,13 @@
use std::path::Path;
use std::sync::Arc;
use brk_core::{DifficultyEpoch, Height, Result, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, EagerVec, Format};
use brk_vecs::{AnyCollectableVec, EagerVec, File, Format};
use crate::vecs::{Indexes, indexes};
use crate::{Indexes, indexes};
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
use super::{ComputedType, EagerVecBuilder, VecBuilderOptions};
#[derive(Clone)]
pub struct ComputedVecsFromHeightStrict<T>
@@ -15,8 +15,8 @@ where
T: ComputedType + PartialOrd,
{
pub height: EagerVec<Height, T>,
pub height_extra: ComputedVecBuilder<Height, T>,
pub difficultyepoch: ComputedVecBuilder<DifficultyEpoch, T>,
pub height_extra: EagerVecBuilder<Height, T>,
pub difficultyepoch: EagerVecBuilder<DifficultyEpoch, T>,
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
}
@@ -28,17 +28,17 @@ where
f64: From<T>,
{
pub fn forced_import(
path: &Path,
file: &Arc<File>,
name: &str,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
options: VecBuilderOptions,
) -> color_eyre::Result<Self> {
let height =
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format)?;
EagerVec::forced_import(file, name, version + VERSION + Version::ZERO, format)?;
let height_extra = ComputedVecBuilder::forced_import(
path,
let height_extra = EagerVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
@@ -50,14 +50,14 @@ where
Ok(Self {
height,
height_extra,
difficultyepoch: ComputedVecBuilder::forced_import(
path,
difficultyepoch: EagerVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
// halvingepoch: StorableVecGeneator::forced_import(path, name, version + VERSION + Version::ZERO, format, options)?,
// halvingepoch: StorableVecGeneator::forced_import(file, name, version + VERSION + Version::ZERO, format, options)?,
})
}
@@ -1,18 +1,23 @@
use std::path::Path;
use std::sync::Arc;
use brk_core::{
Bitcoin, DateIndex, DecadeIndex, DifficultyEpoch, Dollars, Height, MonthIndex, QuarterIndex,
Result, Sats, TxIndex, Version, WeekIndex, YearIndex,
Result, Sats, SemesterIndex, TxIndex, Version, WeekIndex, YearIndex,
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{
AnyCollectableVec, AnyVec, CollectableVec, EagerVec, Format, StoredIndex, VecIterator,
use brk_vecs::{
AnyCollectableVec, AnyVec, CloneableAnyIterableVec, CollectableVec, Computation, EagerVec,
File, Format, StoredIndex, VecIterator,
};
use crate::vecs::{Indexes, fetched, indexes};
use crate::{
Indexes, fetched,
grouped::{ComputedVecBuilder, Source},
indexes,
};
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
use super::{ComputedType, EagerVecBuilder, VecBuilderOptions};
#[derive(Clone)]
pub struct ComputedVecsFromTxindex<T>
@@ -20,41 +25,45 @@ 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>,
pub height: EagerVecBuilder<Height, T>,
pub dateindex: EagerVecBuilder<DateIndex, T>,
pub weekindex: ComputedVecBuilder<WeekIndex, T, DateIndex, WeekIndex>,
pub difficultyepoch: EagerVecBuilder<DifficultyEpoch, T>,
pub monthindex: ComputedVecBuilder<MonthIndex, T, DateIndex, MonthIndex>,
pub quarterindex: ComputedVecBuilder<QuarterIndex, T, DateIndex, QuarterIndex>,
pub semesterindex: ComputedVecBuilder<SemesterIndex, T, DateIndex, SemesterIndex>,
pub yearindex: ComputedVecBuilder<YearIndex, T, DateIndex, YearIndex>,
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
pub decadeindex: ComputedVecBuilder<DecadeIndex, T>,
pub decadeindex: ComputedVecBuilder<DecadeIndex, T, DateIndex, DecadeIndex>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromTxindex<T>
where
T: ComputedType + Ord + From<f64>,
T: ComputedType + Ord + From<f64> + 'static,
f64: From<T>,
{
#[allow(clippy::too_many_arguments)]
pub fn forced_import(
path: &Path,
file: &Arc<File>,
name: &str,
compute_source: bool,
source: Source<TxIndex, T>,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
computation: Computation,
indexes: &indexes::Vecs,
options: VecBuilderOptions,
) -> color_eyre::Result<Self> {
let txindex = compute_source.then(|| {
let txindex = source.is_compute().then(|| {
Box::new(
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format)
EagerVec::forced_import(file, name, version + VERSION + Version::ZERO, format)
.unwrap(),
)
});
let height = ComputedVecBuilder::forced_import(
path,
let height = EagerVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
@@ -63,59 +72,93 @@ where
let options = options.remove_percentiles();
let dateindex = EagerVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
options,
)?;
Ok(Self {
txindex,
height,
dateindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
weekindex: ComputedVecBuilder::forced_import(
path,
file,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
difficultyepoch: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
computation,
None,
&dateindex,
indexes.weekindex_to_weekindex.boxed_clone(),
options.into(),
)?,
monthindex: ComputedVecBuilder::forced_import(
path,
file,
name,
version + VERSION + Version::ZERO,
format,
options,
Computation::Lazy,
None,
&dateindex,
indexes.monthindex_to_monthindex.boxed_clone(),
options.into(),
)?,
quarterindex: ComputedVecBuilder::forced_import(
path,
file,
name,
version + VERSION + Version::ZERO,
format,
options,
Computation::Lazy,
None,
&dateindex,
indexes.quarterindex_to_quarterindex.boxed_clone(),
options.into(),
)?,
semesterindex: ComputedVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
Computation::Lazy,
None,
&dateindex,
indexes.semesterindex_to_semesterindex.boxed_clone(),
options.into(),
)?,
yearindex: ComputedVecBuilder::forced_import(
path,
file,
name,
version + VERSION + Version::ZERO,
format,
options,
Computation::Lazy,
None,
&dateindex,
indexes.yearindex_to_yearindex.boxed_clone(),
options.into(),
)?,
// halvingepoch: StorableVecGeneator::forced_import(path, name, version + VERSION + Version::ZERO, format, options)?,
decadeindex: ComputedVecBuilder::forced_import(
path,
file,
name,
version + VERSION + Version::ZERO,
format,
Computation::Lazy,
None,
&dateindex,
indexes.decadeindex_to_decadeindex.boxed_clone(),
options.into(),
)?,
txindex,
height,
dateindex,
difficultyepoch: EagerVecBuilder::forced_import(
file,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
// halvingepoch: StorableVecGeneator::forced_import(file, name, version + VERSION + Version::ZERO, format, options)?,
})
}
@@ -163,7 +206,7 @@ where
self.height.compute(
starting_indexes.height,
txindex,
&indexer.vecs().height_to_first_txindex,
&indexer.vecs.height_to_first_txindex,
&indexes.height_to_txindex_count,
exit,
)?;
@@ -173,7 +216,7 @@ where
self.height.compute(
starting_indexes.height,
txindex,
&indexer.vecs().height_to_first_txindex,
&indexer.vecs.height_to_first_txindex,
&indexes.height_to_txindex_count,
exit,
)?;
@@ -196,42 +239,38 @@ where
exit,
)?;
self.weekindex.from_aligned(
self.weekindex.compute_if_necessary(
starting_indexes.weekindex,
&self.dateindex,
&indexes.weekindex_to_first_dateindex,
&indexes.weekindex_to_dateindex_count,
exit,
)?;
self.monthindex.from_aligned(
self.monthindex.compute_if_necessary(
starting_indexes.monthindex,
&self.dateindex,
&indexes.monthindex_to_first_dateindex,
&indexes.monthindex_to_dateindex_count,
exit,
)?;
self.quarterindex.from_aligned(
self.quarterindex.compute_if_necessary(
starting_indexes.quarterindex,
&self.monthindex,
&indexes.quarterindex_to_first_monthindex,
&indexes.quarterindex_to_monthindex_count,
exit,
)?;
self.yearindex.from_aligned(
self.semesterindex.compute_if_necessary(
starting_indexes.semesterindex,
&indexes.semesterindex_to_monthindex_count,
exit,
)?;
self.yearindex.compute_if_necessary(
starting_indexes.yearindex,
&self.monthindex,
&indexes.yearindex_to_first_monthindex,
&indexes.yearindex_to_monthindex_count,
exit,
)?;
self.decadeindex.from_aligned(
self.decadeindex.compute_if_necessary(
starting_indexes.decadeindex,
&self.yearindex,
&indexes.decadeindex_to_first_yearindex,
&indexes.decadeindex_to_yearindex_count,
exit,
)?;
@@ -258,6 +297,7 @@ where
self.difficultyepoch.vecs(),
self.monthindex.vecs(),
self.quarterindex.vecs(),
self.semesterindex.vecs(),
self.yearindex.vecs(),
// self.halvingepoch.vecs(),
self.decadeindex.vecs(),
@@ -289,7 +329,7 @@ impl ComputedVecsFromTxindex<Bitcoin> {
let starting_index = self.height.starting_index(starting_indexes.height);
(starting_index.unwrap_to_usize()..indexer.vecs().height_to_weight.len())
(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() {
@@ -470,7 +510,7 @@ impl ComputedVecsFromTxindex<Dollars> {
let mut close_iter = fetched.chainindexes_to_close.height.into_iter();
(starting_index.unwrap_to_usize()..indexer.vecs().height_to_weight.len())
(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);
@@ -1,21 +1,25 @@
mod builder;
mod builder_computed;
mod builder_eager;
mod from_dateindex;
mod from_height;
mod from_height_strict;
mod from_txindex;
mod ratio_from_dateindex;
mod source;
mod r#type;
mod value_from_dateindex;
mod value_from_height;
mod value_from_txindex;
mod value_height;
pub use builder::*;
pub use builder_computed::*;
pub use builder_eager::*;
pub use from_dateindex::*;
pub use from_height::*;
pub use from_height_strict::*;
pub use from_txindex::*;
pub use ratio_from_dateindex::*;
pub use source::*;
use r#type::*;
pub use value_from_dateindex::*;
pub use value_from_height::*;
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
use brk_vecs::BoxedAnyIterableVec;
#[derive(Clone)]
pub enum Source<I, T> {
Compute,
None,
Vec(BoxedAnyIterableVec<I, T>),
}
impl<I, T> Source<I, T> {
pub fn is_compute(&self) -> bool {
matches!(self, Self::Compute)
}
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
pub fn is_vec(&self) -> bool {
matches!(self, Self::Vec(_))
}
pub fn vec(self) -> Option<BoxedAnyIterableVec<I, T>> {
match self {
Self::Vec(v) => Some(v),
_ => None,
}
}
}
impl<I, T> From<bool> for Source<I, T> {
fn from(value: bool) -> Self {
if value { Self::Compute } else { Self::None }
}
}
impl<I, T> From<BoxedAnyIterableVec<I, T>> for Source<I, T> {
fn from(value: BoxedAnyIterableVec<I, T>) -> Self {
Self::Vec(value)
}
}
impl<I, T> From<Option<BoxedAnyIterableVec<I, T>>> for Source<I, T> {
fn from(value: Option<BoxedAnyIterableVec<I, T>>) -> Self {
if let Some(v) = value {
Self::Vec(v)
} else {
Self::None
}
}
}
+14
View File
@@ -0,0 +1,14 @@
use std::ops::{Add, AddAssign, Div};
use brk_vecs::StoredType;
pub trait ComputedType
where
Self:
StoredType + From<usize> + Div<usize, Output = Self> + Add<Output = Self> + AddAssign + Ord,
{
}
impl<T> ComputedType for T where
T: StoredType + From<usize> + Div<usize, Output = Self> + Add<Output = Self> + AddAssign + Ord
{
}
@@ -1,13 +1,13 @@
use std::path::Path;
use std::sync::Arc;
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 brk_vecs::{AnyCollectableVec, CollectableVec, Computation, EagerVec, File, Format, StoredVec};
use crate::vecs::{Indexes, fetched, grouped::ComputedVecsFromDateIndex, indexes};
use crate::{Indexes, fetched, grouped::ComputedVecsFromDateIndex, indexes};
use super::StorableVecGeneatorOptions;
use super::{Source, VecBuilderOptions};
#[derive(Clone)]
pub struct ComputedValueVecsFromDateIndex {
@@ -19,39 +19,48 @@ pub struct ComputedValueVecsFromDateIndex {
const VERSION: Version = Version::ZERO;
impl ComputedValueVecsFromDateIndex {
#[allow(clippy::too_many_arguments)]
pub fn forced_import(
path: &Path,
file: &Arc<File>,
name: &str,
compute_source: bool,
source: Source<DateIndex, Sats>,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
computation: Computation,
options: VecBuilderOptions,
compute_dollars: bool,
indexes: &indexes::Vecs,
) -> color_eyre::Result<Self> {
Ok(Self {
sats: ComputedVecsFromDateIndex::forced_import(
path,
file,
name,
compute_source,
source,
version + VERSION,
format,
computation,
indexes,
options,
)?,
bitcoin: ComputedVecsFromDateIndex::forced_import(
path,
file,
&format!("{name}_in_btc"),
true,
Source::Compute,
version + VERSION,
format,
computation,
indexes,
options,
)?,
dollars: compute_dollars.then(|| {
ComputedVecsFromDateIndex::forced_import(
path,
file,
&format!("{name}_in_usd"),
true,
Source::Compute,
version + VERSION,
format,
computation,
indexes,
options,
)
.unwrap()
@@ -1,13 +1,13 @@
use std::path::Path;
use std::sync::Arc;
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 brk_vecs::{AnyCollectableVec, CollectableVec, Computation, EagerVec, File, Format, StoredVec};
use crate::vecs::{Indexes, fetched, indexes};
use crate::{Indexes, fetched, grouped::Source, indexes};
use super::{ComputedVecsFromHeight, StorableVecGeneatorOptions};
use super::{ComputedVecsFromHeight, VecBuilderOptions};
#[derive(Clone)]
pub struct ComputedValueVecsFromHeight {
@@ -19,39 +19,48 @@ pub struct ComputedValueVecsFromHeight {
const VERSION: Version = Version::ZERO;
impl ComputedValueVecsFromHeight {
#[allow(clippy::too_many_arguments)]
pub fn forced_import(
path: &Path,
file: &Arc<File>,
name: &str,
compute_source: bool,
source: Source<Height, Sats>,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
computation: Computation,
options: VecBuilderOptions,
compute_dollars: bool,
indexes: &indexes::Vecs,
) -> color_eyre::Result<Self> {
Ok(Self {
sats: ComputedVecsFromHeight::forced_import(
path,
file,
name,
compute_source,
source,
version + VERSION,
format,
computation,
indexes,
options,
)?,
bitcoin: ComputedVecsFromHeight::forced_import(
path,
file,
&format!("{name}_in_btc"),
true,
Source::Compute,
version + VERSION,
format,
computation,
indexes,
options,
)?,
dollars: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
file,
&format!("{name}_in_usd"),
true,
Source::Compute,
version + VERSION,
format,
computation,
indexes,
options,
)
.unwrap()
@@ -1,16 +1,16 @@
use std::path::Path;
use std::sync::Arc;
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 brk_vecs::{
AnyCollectableVec, CloneableAnyIterableVec, CollectableVec, Computation, ComputedVecFrom3,
File, Format, LazyVecFrom1, StoredIndex, StoredVec,
};
use crate::vecs::{Indexes, fetched, indexes};
use crate::{Indexes, fetched, grouped::Source, indexes};
use super::{ComputedVecsFromTxindex, StorableVecGeneatorOptions};
use super::{ComputedVecsFromTxindex, VecBuilderOptions};
#[derive(Clone)]
pub struct ComputedValueVecsFromTxindex {
@@ -38,56 +38,61 @@ const VERSION: Version = Version::ZERO;
impl ComputedValueVecsFromTxindex {
#[allow(clippy::too_many_arguments)]
pub fn forced_import(
path: &Path,
file: &Arc<File>,
name: &str,
indexes: &indexes::Vecs,
source: Option<BoxedAnyIterableVec<TxIndex, Sats>>,
source: Source<TxIndex, Sats>,
version: Version,
computation: Computation,
format: Format,
fetched: Option<&fetched::Vecs>,
options: StorableVecGeneatorOptions,
options: VecBuilderOptions,
) -> 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,
file,
name,
compute_source,
source.clone(),
version + VERSION,
format,
computation,
indexes,
options,
)?;
let source_vec = source.vec();
let bitcoin_txindex = LazyVecFrom1::init(
&name_in_btc,
version + VERSION,
source.map_or_else(|| sats.txindex.as_ref().unwrap().boxed_clone(), |s| s),
source_vec.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_inner();
let sats = value.into_owned();
Bitcoin::from(sats)
})
},
);
let bitcoin = ComputedVecsFromTxindex::forced_import(
path,
file,
&name_in_btc,
false,
Source::None,
version + VERSION,
format,
computation,
indexes,
options,
)?;
let dollars_txindex = fetched.map(|fetched| {
ComputedVecFrom3::forced_import_or_init_from_3(
computation,
path,
file,
&name_in_usd,
version + VERSION,
format,
@@ -100,14 +105,14 @@ impl ComputedValueVecsFromTxindex {
height_to_close_iter| {
let txindex = txindex.unwrap_to_usize();
txindex_to_btc_iter.next_at(txindex).and_then(|(_, value)| {
let btc = value.into_inner();
let btc = value.into_owned();
txindex_to_height_iter
.next_at(txindex)
.and_then(|(_, value)| {
let height = value.into_inner();
let height = value.into_owned();
height_to_close_iter
.next_at(height.unwrap_to_usize())
.map(|(_, close)| *close.into_inner() * btc)
.map(|(_, close)| *close.into_owned() * btc)
})
})
},
@@ -122,11 +127,13 @@ impl ComputedValueVecsFromTxindex {
dollars_txindex,
dollars: compute_dollars.then(|| {
ComputedVecsFromTxindex::forced_import(
path,
file,
&name_in_usd,
false,
Source::None,
version + VERSION,
format,
computation,
indexes,
options,
)
.unwrap()
@@ -205,7 +212,7 @@ impl ComputedValueVecsFromTxindex {
dollars_txindex.compute_if_necessary(
starting_indexes.txindex,
&indexer.vecs().txindex_to_txid,
&indexer.vecs.txindex_to_txid,
exit,
)?;
@@ -1,11 +1,11 @@
use std::path::Path;
use std::sync::Arc;
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 brk_vecs::{AnyCollectableVec, CollectableVec, EagerVec, File, Format, StoredVec};
use crate::vecs::{Indexes, fetched, indexes};
use crate::{Indexes, fetched, grouped::Source, indexes};
#[derive(Clone)]
pub struct ComputedHeightValueVecs {
@@ -18,27 +18,27 @@ const VERSION: Version = Version::ZERO;
impl ComputedHeightValueVecs {
pub fn forced_import(
path: &Path,
file: &Arc<File>,
name: &str,
compute_source: bool,
source: Source<Height, Sats>,
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)
sats: source.is_compute().then(|| {
EagerVec::forced_import(file, name, version + VERSION + Version::ZERO, format)
.unwrap()
}),
bitcoin: EagerVec::forced_import(
path,
file,
&format!("{name}_in_btc"),
version + VERSION + Version::ZERO,
format,
)?,
dollars: compute_dollars.then(|| {
EagerVec::forced_import(
path,
file,
&format!("{name}_in_usd"),
version + VERSION + Version::ZERO,
format,
File diff suppressed because it is too large Load Diff
+54 -67
View File
@@ -3,71 +3,74 @@
#![doc = include_str!("../examples/main.rs")]
#![doc = "```"]
use std::path::{Path, PathBuf};
use std::{path::Path, sync::Arc};
use brk_core::Version;
use brk_exit::Exit;
use brk_fetcher::Fetcher;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, Computation, Format};
mod stores;
mod utils;
mod vecs;
use brk_vecs::{Computation, File, Format, PAGE_SIZE};
use log::info;
use stores::Stores;
use vecs::Vecs;
mod all;
mod blocks;
mod cointime;
mod constants;
mod fetched;
mod grouped;
mod indexes;
mod market;
mod mining;
mod stateful;
mod states;
mod transactions;
mod utils;
use indexes::Indexes;
use states::*;
#[derive(Clone)]
pub struct Computer {
path: PathBuf,
file: Arc<File>,
fetcher: Option<Fetcher>,
vecs: Option<Vecs>,
stores: Option<Stores>,
format: Format,
pub vecs: all::Vecs,
}
const VERSION: Version = Version::ONE;
impl Computer {
pub fn new(outputs_dir: &Path, fetcher: Option<Fetcher>, format: Format) -> Self {
Self {
path: outputs_dir.to_owned(),
fetcher,
vecs: None,
stores: None,
format,
}
}
pub fn import_vecs(
&mut self,
/// Do NOT import multiple times or things will break !!!
pub fn forced_import(
outputs_dir: &Path,
indexer: &Indexer,
computation: Computation,
) -> color_eyre::Result<()> {
self.vecs = Some(Vecs::import(
// TODO: Give self.path, join inside import
&self.path.join("vecs/computed"),
VERSION + Version::ZERO,
indexer,
self.fetcher.is_some(),
computation,
self.format,
)?);
Ok(())
}
fetcher: Option<Fetcher>,
format: Format,
) -> color_eyre::Result<Self> {
let computed_path = outputs_dir.join("computed");
let states_path = computed_path.join("states");
/// Do NOT import multiple times or things will break !!!
/// Clone struct instead
pub fn import_stores(&mut self, indexer: &Indexer) -> color_eyre::Result<()> {
self.stores = Some(Stores::import(
// TODO: Give self.path, join inside import
&self.path.join("stores"),
VERSION + Version::ZERO,
indexer.keyspace(),
)?);
Ok(())
let file = Arc::new(File::open(&computed_path.join("vecs"))?);
file.set_min_len(PAGE_SIZE * 100_000_000)?;
file.set_min_regions(50_000)?;
let file_fetched = Arc::new(File::open(&outputs_dir.join("fetched/vecs"))?);
Ok(Self {
vecs: all::Vecs::import(
&file,
VERSION + Version::ZERO,
indexer,
fetcher.is_some(),
computation,
format,
&file_fetched,
&states_path,
)?,
fetcher,
file,
})
}
}
@@ -80,25 +83,9 @@ impl Computer {
) -> color_eyre::Result<()> {
info!("Computing...");
self.vecs
.as_mut()
.unwrap()
.compute(indexer, starting_indexes, self.fetcher.as_mut(), exit)
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
// pub fn vecs(&self) -> &Vecs {
self.vecs.as_ref().unwrap().vecs()
}
// 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()
.compute(indexer, starting_indexes, self.fetcher.as_mut(), exit)?;
self.file.flush()?;
self.file.punch_holes()?;
Ok(())
}
}
File diff suppressed because it is too large Load Diff
@@ -1,13 +1,15 @@
use std::{fs, path::Path};
use std::sync::Arc;
use brk_core::{DifficultyEpoch, HalvingEpoch, StoredF64, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, Computation, Format, VecIterator};
use brk_vecs::{AnyCollectableVec, Computation, File, Format, VecIterator};
use crate::grouped::Source;
use super::{
Indexes,
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, StorableVecGeneatorOptions},
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, VecBuilderOptions},
indexes,
};
@@ -22,37 +24,42 @@ pub struct Vecs {
impl Vecs {
pub fn forced_import(
path: &Path,
file: &Arc<File>,
version: Version,
_computation: Computation,
computation: Computation,
format: Format,
indexes: &indexes::Vecs,
) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
Ok(Self {
indexes_to_difficulty: ComputedVecsFromHeight::forced_import(
path,
file,
"difficulty",
false,
Source::None,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_difficultyepoch: ComputedVecsFromDateIndex::forced_import(
path,
file,
"difficultyepoch",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
indexes_to_halvingepoch: ComputedVecsFromDateIndex::forced_import(
path,
file,
"halvingepoch",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
})
}
@@ -116,7 +123,7 @@ impl Vecs {
indexes,
starting_indexes,
exit,
Some(&indexer.vecs().height_to_difficulty),
Some(&indexer.vecs.height_to_difficulty),
)?;
Ok(())
@@ -0,0 +1,255 @@
use std::{ops::Deref, path::Path, sync::Arc};
use brk_core::{Bitcoin, DateIndex, Dollars, Height, Result, StoredUsize, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vecs::{
AnyCollectableVec, AnyIterableVec, AnyVec, Computation, EagerVec, File, Format, VecIterator,
};
use crate::{
Indexes, fetched,
grouped::{ComputedVecsFromHeight, Source, VecBuilderOptions},
indexes, market,
stateful::{
common,
r#trait::{CohortVecs, DynCohortVecs},
},
states::AddressCohortState,
};
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(
file: &Arc<File>,
cohort_name: Option<&str>,
computation: Computation,
format: Format,
version: Version,
indexes: &indexes::Vecs,
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(
file,
&suffix("address_count"),
version + VERSION + Version::ZERO,
format,
)?,
indexes_to_address_count: ComputedVecsFromHeight::forced_import(
file,
&suffix("address_count"),
Source::None,
version + VERSION + Version::ZERO,
format,
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
inner: common::Vecs::forced_import(
file,
cohort_name,
computation,
format,
version,
indexes,
fetched,
compute_relative_to_all,
false,
)?,
})
}
}
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,595 @@
use std::{path::Path, sync::Arc};
use brk_core::{
AddressGroups, Bitcoin, ByAmountRange, ByGreatEqualAmount, ByLowerThanAmount, DateIndex,
Dollars, GroupFilter, Height, Result, Version,
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vecs::{AnyIterableVec, Computation, File, Format};
use derive_deref::{Deref, DerefMut};
use crate::{
Indexes, fetched, indexes, market,
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(
file: &Arc<File>,
version: Version,
_computation: Computation,
format: Format,
indexes: &indexes::Vecs,
fetched: Option<&fetched::Vecs>,
states_path: &Path,
) -> color_eyre::Result<Self> {
Ok(Self(
AddressGroups {
amount_range: ByAmountRange {
_0sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_with_0sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1sat_to_10sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_1sat_under_10sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10sats_to_100sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_10sats_under_100sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_100sats_to_1k_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_100sats_under_1k_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1k_sats_to_10k_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_1k_sats_under_10k_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10k_sats_to_100k_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_10k_sats_under_100k_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_100k_sats_to_1m_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_100k_sats_under_1m_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1m_sats_to_10m_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_1m_sats_under_10m_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10m_sats_to_1btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_10m_sats_under_1btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1btc_to_10btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_1btc_under_10btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10btc_to_100btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_10btc_under_100btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_100btc_to_1k_btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_100btc_under_1k_btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1k_btc_to_10k_btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_1k_btc_under_10k_btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10k_btc_to_100k_btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_10k_btc_under_100k_btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_100k_btc_or_more: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_100k_btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
},
lt_amount: ByLowerThanAmount {
_10sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_10sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_100sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_100sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1k_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_1k_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10k_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_10k_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_100k_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_100k_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1m_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_1m_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10m_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_10m_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_1btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_10btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_100btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_100btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1k_btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_1k_btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10k_btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_10k_btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_100k_btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_under_100k_btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
},
ge_amount: ByGreatEqualAmount {
_1sat: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_1sat"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_10sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_100sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_100sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1k_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_1k_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10k_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_10k_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_100k_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_100k_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1m_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_1m_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10m_sats: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_10m_sats"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_1btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_10btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_100btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_100btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_1k_btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_1k_btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
fetched,
states_path,
true,
)?,
_10k_btc: address_cohort::Vecs::forced_import(
file,
Some("addrs_above_10k_btc"),
_computation,
format,
version + VERSION + Version::ZERO,
indexes,
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_iter()
.flatten()
.try_for_each(|(vecs, stateful)| {
vecs.compute_from_stateful(starting_indexes, &stateful, exit)
})
}
pub fn compute_rest_part1(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
fetched: Option<&fetched::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
) -> color_eyre::Result<()> {
self.as_mut_vecs().into_iter().try_for_each(|(_, v)| {
v.compute_rest_part1(indexer, indexes, fetched, starting_indexes, exit)
})
}
#[allow(clippy::too_many_arguments)]
pub 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.0.as_boxed_mut_vecs().into_iter().try_for_each(|v| {
v.into_iter().try_for_each(|(_, v)| {
v.compute_rest_part2(
indexer,
indexes,
fetched,
starting_indexes,
market,
height_to_supply,
dateindex_to_supply,
height_to_realized_cap,
dateindex_to_realized_cap,
exit,
)
})
})
}
pub fn safe_flush_stateful_vecs(&mut self, height: Height, exit: &Exit) -> Result<()> {
self.as_mut_separate_vecs()
.into_iter()
.try_for_each(|(_, v)| v.safe_flush_stateful_vecs(height, exit))
}
}
@@ -0,0 +1,47 @@
use brk_core::{ByAddressType, Height};
use brk_vecs::VecIterator;
use derive_deref::{Deref, DerefMut};
use crate::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_vecs::EagerVec;
use derive_deref::{Deref, DerefMut};
use crate::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_vecs::AnyCollectableVec;
use derive_deref::{Deref, DerefMut};
use crate::{
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,24 @@
use std::collections::BTreeSet;
use brk_core::TypeIndex;
use derive_deref::{Deref, DerefMut};
use super::ByAddressType;
#[derive(Debug, Deref, DerefMut)]
pub struct AddressTypeToTypeIndexSet(ByAddressType<BTreeSet<TypeIndex>>);
impl Default for AddressTypeToTypeIndexSet {
fn default() -> Self {
Self(ByAddressType {
p2pk65: BTreeSet::default(),
p2pk33: BTreeSet::default(),
p2pkh: BTreeSet::default(),
p2sh: BTreeSet::default(),
p2wpkh: BTreeSet::default(),
p2wsh: BTreeSet::default(),
p2tr: BTreeSet::default(),
p2a: BTreeSet::default(),
})
}
}
@@ -0,0 +1,47 @@
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 {
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);
self
}
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);
}
}
}
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,57 @@
use std::mem;
use derive_deref::{Deref, DerefMut};
use super::ByAddressType;
#[derive(Debug, Deref, DerefMut)]
pub struct AddressTypeToVec<T>(ByAddressType<Vec<T>>);
impl<T> AddressTypeToVec<T> {
pub fn merge(mut self, mut other: Self) -> 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);
self
}
pub fn merge_mut(&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);
}
}
}
impl<T> Default for AddressTypeToVec<T> {
fn default() -> Self {
Self(ByAddressType {
p2pk65: vec![],
p2pk33: vec![],
p2pkh: vec![],
p2sh: vec![],
p2wpkh: vec![],
p2wsh: vec![],
p2tr: vec![],
p2a: vec![],
})
}
}
@@ -0,0 +1,9 @@
use std::collections::BTreeMap;
use brk_core::Height;
use derive_deref::{Deref, DerefMut};
use crate::stateful::AddressTypeToVec;
#[derive(Debug, Default, Deref, DerefMut)]
pub struct HeightToAddressTypeToVec<T>(pub BTreeMap<Height, AddressTypeToVec<T>>);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,35 @@
use std::collections::BTreeMap;
use brk_vecs::{StampedVec, StoredIndex, StoredType};
#[derive(Debug)]
pub struct RangeMap<I, T>(BTreeMap<I, T>);
impl<I, T> RangeMap<I, T>
where
I: StoredIndex,
T: StoredIndex,
{
pub fn get(&self, key: I) -> Option<&T> {
self.0.range(..=key).next_back().map(|(&min, value)| {
if min > key {
unreachable!()
}
value
})
}
}
impl<I, T> From<&StampedVec<I, T>> for RangeMap<T, I>
where
I: StoredIndex,
T: StoredIndex + StoredType,
{
fn from(vec: &StampedVec<I, T>) -> Self {
Self(
vec.into_iter()
.map(|(i, v)| (v.into_owned(), i))
.collect::<BTreeMap<_, _>>(),
)
}
}
+63
View File
@@ -0,0 +1,63 @@
use brk_core::{Bitcoin, DateIndex, Dollars, Height, Result, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vecs::{AnyCollectableVec, AnyIterableVec};
use crate::{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,187 @@
use std::{ops::Deref, path::Path, sync::Arc};
use brk_core::{Bitcoin, DateIndex, Dollars, Height, Result, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vecs::{AnyCollectableVec, AnyIterableVec, Computation, File, Format};
use crate::{
Indexes, UTXOCohortState, 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(
file: &Arc<File>,
cohort_name: Option<&str>,
computation: Computation,
format: Format,
version: Version,
indexes: &indexes::Vecs,
fetched: Option<&fetched::Vecs>,
states_path: &Path,
compute_relative_to_all: bool,
ratio_extended: 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(
file,
cohort_name,
computation,
format,
version,
indexes,
fetched,
compute_relative_to_all,
ratio_extended,
)?,
})
}
}
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
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,54 @@
use brk_core::{EmptyAddressData, EmptyAddressIndex, LoadedAddressData, LoadedAddressIndex};
#[derive(Debug)]
pub enum WithAddressDataSource<T> {
New(T),
FromLoadedAddressDataVec((LoadedAddressIndex, T)),
FromEmptyAddressDataVec((EmptyAddressIndex, T)),
}
impl<T> WithAddressDataSource<T> {
pub fn is_new(&self) -> bool {
matches!(self, Self::New(_))
}
pub fn is_from_emptyaddressdata(&self) -> bool {
matches!(self, Self::FromEmptyAddressDataVec(_))
}
pub fn deref_mut(&mut self) -> &mut T {
match self {
Self::New(v) => v,
Self::FromLoadedAddressDataVec((_, v)) => v,
Self::FromEmptyAddressDataVec((_, v)) => v,
}
}
}
impl From<WithAddressDataSource<EmptyAddressData>> for WithAddressDataSource<LoadedAddressData> {
fn from(value: WithAddressDataSource<EmptyAddressData>) -> Self {
match value {
WithAddressDataSource::New(v) => Self::New(v.into()),
WithAddressDataSource::FromLoadedAddressDataVec((i, v)) => {
Self::FromLoadedAddressDataVec((i, v.into()))
}
WithAddressDataSource::FromEmptyAddressDataVec((i, v)) => {
Self::FromEmptyAddressDataVec((i, v.into()))
}
}
}
}
impl From<WithAddressDataSource<LoadedAddressData>> for WithAddressDataSource<EmptyAddressData> {
fn from(value: WithAddressDataSource<LoadedAddressData>) -> Self {
match value {
WithAddressDataSource::New(v) => Self::New(v.into()),
WithAddressDataSource::FromLoadedAddressDataVec((i, v)) => {
Self::FromLoadedAddressDataVec((i, v.into()))
}
WithAddressDataSource::FromEmptyAddressDataVec((i, v)) => {
Self::FromEmptyAddressDataVec((i, v.into()))
}
}
}
}
@@ -0,0 +1,125 @@
use std::path::Path;
use brk_core::{Dollars, Height, LoadedAddressData, 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 LoadedAddressData,
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 LoadedAddressData,
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: &LoadedAddressData) {
self.address_count += 1;
self.inner.increment_(
&addressdata.into(),
addressdata.realized_cap,
addressdata.realized_price(),
);
}
pub fn subtract(&mut self, addressdata: &LoadedAddressData) {
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)
}
}
@@ -2,9 +2,7 @@ use std::{cmp::Ordering, path::Path};
use brk_core::{CheckedSub, Dollars, Height, Result, Sats};
use crate::{PriceToAmount, UnrealizedState};
use super::{RealizedState, SupplyState};
use crate::{PriceToAmount, RealizedState, SupplyState, UnrealizedState};
#[derive(Clone)]
pub struct CohortState {
@@ -12,7 +10,8 @@ pub struct CohortState {
pub realized: Option<RealizedState>,
pub satblocks_destroyed: Sats,
pub satdays_destroyed: Sats,
pub price_to_amount: PriceToAmount,
price_to_amount: PriceToAmount,
}
impl CohortState {
@@ -26,6 +25,22 @@ impl CohortState {
})
}
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;
@@ -41,7 +56,23 @@ impl CohortState {
if let Some(realized) = self.realized.as_mut() {
let price = price.unwrap();
realized.increment(supply_state, price);
*self.price_to_amount.entry(price).or_default() += supply_state.value;
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);
}
}
}
@@ -53,27 +84,60 @@ impl CohortState {
if let Some(realized) = self.realized.as_mut() {
let price = price.unwrap();
realized.decrement(supply_state, price);
self.decrement_price_to_amount(supply_state, price);
self.price_to_amount.decrement(price, supply_state);
}
}
}
fn decrement_price_to_amount(&mut self, supply_state: &SupplyState, price: Dollars) {
let amount = self.price_to_amount.get_mut(&price).unwrap();
*amount -= supply_state.value;
if *amount == Sats::ZERO {
self.price_to_amount.remove(&price);
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);
*self.price_to_amount.entry(price).or_default() += supply_state.value;
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);
}
}
}
}
@@ -87,6 +151,34 @@ impl CohortState {
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 {
@@ -99,7 +191,16 @@ impl CohortState {
let current_price = current_price.unwrap();
let prev_price = prev_price.unwrap();
realized.send(supply_state, current_price, prev_price, older_than_hour);
self.decrement_price_to_amount(supply_state, prev_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);
}
}
}
}
@@ -122,17 +223,15 @@ impl CohortState {
let update_state =
|price: Dollars, current_price: Dollars, sats: Sats, state: &mut UnrealizedState| {
match price.cmp(&current_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!();
}
// Add back once in a while to verify, but generally not needed
// if diff <= Dollars::ZERO {
// dbg!(price, current_price, diff, sats);
// panic!();
// }
state.unrealized_profit += diff * sats;
}
}
@@ -140,13 +239,17 @@ impl CohortState {
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!();
}
// Add back once in a while to verify, but generally not needed
// if diff <= Dollars::ZERO {
// dbg!(price, current_price, diff, sats);
// panic!();
// }
state.unrealized_loss += diff * sats;
}
}
Ordering::Equal => {
state.supply_even += sats;
}
}
};
@@ -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()
}
}
@@ -1,19 +1,15 @@
mod block;
mod cohort;
mod outputs;
mod realized;
// mod hot;
mod cohorts;
mod price_to_amount;
mod realized;
mod supply;
mod transacted;
mod unrealized;
pub use block::*;
pub use cohort::*;
pub use outputs::*;
pub use realized::*;
pub use unrealized::*;
// pub use hot::*;
pub use cohorts::*;
pub use price_to_amount::*;
pub use realized::*;
pub use supply::*;
pub use transacted::*;
pub use unrealized::*;
@@ -2,7 +2,6 @@ use std::{
collections::BTreeMap,
fs::{self, File},
io::{BufReader, BufWriter},
ops::{Deref, DerefMut},
path::{Path, PathBuf},
};
@@ -11,6 +10,8 @@ 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,
@@ -46,8 +47,36 @@ impl PriceToAmount {
})
}
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.clear();
self.state.clear();
self.height = None;
fs::remove_dir_all(&self.pathbuf)?;
fs::create_dir_all(&self.pathbuf)?;
@@ -58,12 +87,12 @@ impl PriceToAmount {
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)?;
encode_into_std_write(&self.state, &mut writer, config::standard())?;
Ok(())
}
@@ -89,16 +118,3 @@ impl PriceToAmount {
path.join("height")
}
}
impl Deref for PriceToAmount {
type Target = BTreeMap<Dollars, Sats>;
fn deref(&self) -> &Self::Target {
&self.state
}
}
impl DerefMut for PriceToAmount {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.state
}
}
@@ -42,6 +42,10 @@ impl RealizedState {
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;
@@ -52,13 +56,15 @@ impl RealizedState {
self.adj_value_destroyed = Dollars::ZERO;
}
let value = price * supply_state.value;
self.cap += value;
self.cap += realized_cap;
}
pub fn decrement(&mut self, supply_state: &SupplyState, price: Dollars) {
let value = price * supply_state.value;
self.cap = self.cap.checked_sub(value).unwrap();
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) {
@@ -1,6 +1,6 @@
use std::ops::{Add, AddAssign, SubAssign};
use brk_core::{CheckedSub, Sats};
use brk_core::{CheckedSub, LoadedAddressData, Sats};
use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
@@ -39,3 +39,12 @@ impl SubAssign<&SupplyState> for SupplyState {
self.value = self.value.checked_sub(rhs.value).unwrap();
}
}
impl From<&LoadedAddressData> for SupplyState {
fn from(value: &LoadedAddressData) -> 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;
}
}
-34
View File
@@ -1,34 +0,0 @@
use std::path::Path;
use brk_core::Version;
use fjall::TransactionalKeyspace;
const _VERSION: Version = Version::ZERO;
#[derive(Clone)]
pub struct Stores {
// pub address_to_utxos_received: Store<AddressIndexOutputIndex, Unit>,
// pub address_to_utxos_spent: Store<AddressIndexOutputIndex, Unit>,
}
impl Stores {
pub fn import(_: &Path, _: Version, _: &TransactionalKeyspace) -> color_eyre::Result<Self> {
// let address_to_utxos_received = Store::import(
// keyspace.clone(),
// path,
// "address_to_utxos_received",
// version + VERSION + Version::ZERO,
// )?;
// let address_to_utxos_spent = Store::import(
// keyspace.clone(),
// path,
// "address_to_utxos_spent",
// version + VERSION + Version::ZERO,
// )?;
Ok(Self {
// address_to_utxos_received,
// address_to_utxos_spent,
})
}
}
@@ -1,4 +1,4 @@
use std::{fs, path::Path};
use std::sync::Arc;
use brk_core::{
CheckedSub, Feerate, HalvingEpoch, Height, InputIndex, OutputIndex, Sats, StoredU32,
@@ -6,20 +6,18 @@ use brk_core::{
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{
use brk_vecs::{
AnyCollectableVec, AnyIterableVec, CloneableAnyIterableVec, Computation, ComputedVec,
ComputedVecFrom1, ComputedVecFrom2, ComputedVecFrom3, Format, StoredIndex, VecIterator,
ComputedVecFrom1, ComputedVecFrom2, ComputedVecFrom3, File, Format, StoredIndex, VecIterator,
};
use super::{
Indexes, fetched,
grouped::{
ComputedValueVecsFromHeight, ComputedValueVecsFromTxindex, ComputedVecsFromHeight,
ComputedVecsFromTxindex, StorableVecGeneatorOptions,
},
indexes,
use crate::grouped::{
ComputedValueVecsFromHeight, ComputedValueVecsFromTxindex, ComputedVecsFromHeight,
ComputedVecsFromTxindex, Source, VecBuilderOptions,
};
use super::{Indexes, fetched, indexes};
const VERSION: Version = Version::ZERO;
#[derive(Clone)]
@@ -88,7 +86,7 @@ pub struct Vecs {
impl Vecs {
pub fn forced_import(
path: &Path,
file: &Arc<File>,
version: Version,
indexer: &Indexer,
indexes: &indexes::Vecs,
@@ -98,27 +96,25 @@ impl Vecs {
) -> color_eyre::Result<Self> {
let compute_dollars = fetched.is_some();
fs::create_dir_all(path)?;
let inputindex_to_value = ComputedVec::forced_import_or_init_from_2(
computation,
path,
file,
"value",
version + VERSION + Version::ZERO,
format,
indexer.vecs().inputindex_to_outputindex.boxed_clone(),
indexer.vecs().outputindex_to_value.boxed_clone(),
indexer.vecs.inputindex_to_outputindex.boxed_clone(),
indexer.vecs.outputindex_to_value.boxed_clone(),
|index: InputIndex, inputindex_to_outputindex_iter, outputindex_to_value_iter| {
inputindex_to_outputindex_iter
.next_at(index.unwrap_to_usize())
.map(|(inputindex, outputindex)| {
let outputindex = outputindex.into_inner();
let outputindex = outputindex.into_owned();
if outputindex == OutputIndex::COINBASE {
Sats::ZERO
} else if let Some((_, value)) =
outputindex_to_value_iter.next_at(outputindex.unwrap_to_usize())
{
value.into_inner()
value.into_owned()
} else {
dbg!(inputindex, outputindex);
panic!()
@@ -129,23 +125,23 @@ impl Vecs {
let txindex_to_weight = ComputedVec::forced_import_or_init_from_2(
computation,
path,
file,
"weight",
version + VERSION + Version::ZERO,
format,
indexer.vecs().txindex_to_base_size.boxed_clone(),
indexer.vecs().txindex_to_total_size.boxed_clone(),
indexer.vecs.txindex_to_base_size.boxed_clone(),
indexer.vecs.txindex_to_total_size.boxed_clone(),
|index: TxIndex, txindex_to_base_size_iter, txindex_to_total_size_iter| {
let index = index.unwrap_to_usize();
txindex_to_base_size_iter
.next_at(index)
.map(|(_, base_size)| {
let base_size = base_size.into_inner();
let base_size = base_size.into_owned();
let total_size = txindex_to_total_size_iter
.next_at(index)
.unwrap()
.1
.into_inner();
.into_owned();
// This is the exact definition of a weight unit, as defined by BIP-141 (quote above).
let wu = usize::from(base_size) * 3 + usize::from(total_size);
@@ -157,7 +153,7 @@ impl Vecs {
let txindex_to_vsize = ComputedVec::forced_import_or_init_from_1(
computation,
path,
file,
"vsize",
version + VERSION + Version::ZERO,
format,
@@ -166,7 +162,7 @@ impl Vecs {
let index = index.unwrap_to_usize();
iter.next_at(index).map(|(_, weight)| {
StoredUsize::from(
bitcoin::Weight::from(weight.into_inner()).to_vbytes_ceil() as usize
bitcoin::Weight::from(weight.into_owned()).to_vbytes_ceil() as usize
)
})
},
@@ -174,22 +170,22 @@ impl Vecs {
let txindex_to_is_coinbase = ComputedVec::forced_import_or_init_from_2(
computation,
path,
file,
"is_coinbase",
version + VERSION + Version::ZERO,
format,
indexes.txindex_to_height.boxed_clone(),
indexer.vecs().height_to_first_txindex.boxed_clone(),
indexer.vecs.height_to_first_txindex.boxed_clone(),
|index: TxIndex, txindex_to_height_iter, height_to_first_txindex_iter| {
txindex_to_height_iter
.next_at(index.unwrap_to_usize())
.map(|(_, height)| {
let height = height.into_inner();
let height = height.into_owned();
let txindex = height_to_first_txindex_iter
.next_at(height.unwrap_to_usize())
.unwrap()
.1
.into_inner();
.into_owned();
index == txindex
})
@@ -198,11 +194,11 @@ impl Vecs {
let txindex_to_input_value = ComputedVec::forced_import_or_init_from_3(
computation,
path,
file,
"input_value",
version + VERSION + Version::ZERO,
format,
indexer.vecs().txindex_to_first_inputindex.boxed_clone(),
indexer.vecs.txindex_to_first_inputindex.boxed_clone(),
indexes.txindex_to_input_count.boxed_clone(),
inputindex_to_value.boxed_clone(),
|index: TxIndex,
@@ -213,12 +209,12 @@ impl Vecs {
txindex_to_first_inputindex_iter
.next_at(txindex)
.map(|(_, first_index)| {
let first_index = usize::from(first_index.into_inner());
let first_index = usize::from(first_index.into_owned());
let count = *txindex_to_input_count_iter
.next_at(txindex)
.unwrap()
.1
.into_inner();
.into_owned();
let range = first_index..first_index + count;
range.into_iter().fold(Sats::ZERO, |total, inputindex| {
total
@@ -226,7 +222,7 @@ impl Vecs {
.next_at(inputindex)
.unwrap()
.1
.into_inner()
.into_owned()
})
})
},
@@ -234,12 +230,13 @@ impl Vecs {
// let indexes_to_input_value: ComputedVecsFromTxindex<Sats> =
// ComputedVecsFromTxindex::forced_import(
// path,
// file,
// "input_value",
// true,
// version + VERSION + Version::ZERO,
// format,
// StorableVecGeneatorOptions::default()
// computation,
// StorableVecGeneatorOptions::default()
// .add_average()
// .add_sum()
// .add_cumulative(),
@@ -247,13 +244,13 @@ impl Vecs {
let txindex_to_output_value = ComputedVec::forced_import_or_init_from_3(
computation,
path,
file,
"output_value",
version + VERSION + Version::ZERO,
format,
indexer.vecs().txindex_to_first_outputindex.boxed_clone(),
indexer.vecs.txindex_to_first_outputindex.boxed_clone(),
indexes.txindex_to_output_count.boxed_clone(),
indexer.vecs().outputindex_to_value.boxed_clone(),
indexer.vecs.outputindex_to_value.boxed_clone(),
|index: TxIndex,
txindex_to_first_outputindex_iter,
txindex_to_output_count_iter,
@@ -262,12 +259,12 @@ impl Vecs {
txindex_to_first_outputindex_iter
.next_at(txindex)
.map(|(_, first_index)| {
let first_index = usize::from(first_index.into_inner());
let first_index = usize::from(first_index.into_owned());
let count = *txindex_to_output_count_iter
.next_at(txindex)
.unwrap()
.1
.into_inner();
.into_owned();
let range = first_index..first_index + count;
range.into_iter().fold(Sats::ZERO, |total, outputindex| {
total
@@ -275,7 +272,7 @@ impl Vecs {
.next_at(outputindex)
.unwrap()
.1
.into_inner()
.into_owned()
})
})
},
@@ -283,12 +280,13 @@ impl Vecs {
// let indexes_to_output_value: ComputedVecsFromTxindex<Sats> =
// ComputedVecsFromTxindex::forced_import(
// path,
// file,
// "output_value",
// true,
// version + VERSION + Version::ZERO,
// format,
// StorableVecGeneatorOptions::default()
// computation,
// StorableVecGeneatorOptions::default()
// .add_average()
// .add_sum()
// .add_cumulative(),
@@ -296,7 +294,7 @@ impl Vecs {
let txindex_to_fee = ComputedVecFrom2::forced_import_or_init_from_2(
computation,
path,
file,
"fee",
version + VERSION + Version::ZERO,
format,
@@ -305,12 +303,12 @@ impl Vecs {
|txindex: TxIndex, input_iter, output_iter| {
let txindex = txindex.unwrap_to_usize();
input_iter.next_at(txindex).and_then(|(_, value)| {
let input = value.into_inner();
let input = value.into_owned();
if input.is_zero() {
return Some(Sats::ZERO);
}
output_iter.next_at(txindex).map(|(_, value)| {
let output = value.into_inner();
let output = value.into_owned();
input.checked_sub(output).unwrap()
})
})
@@ -319,7 +317,7 @@ impl Vecs {
let txindex_to_feerate = ComputedVecFrom2::forced_import_or_init_from_2(
computation,
path,
file,
"feerate",
version + VERSION + Version::ZERO,
format,
@@ -328,9 +326,9 @@ impl Vecs {
|txindex: TxIndex, fee_iter, vsize_iter| {
let txindex = txindex.unwrap_to_usize();
fee_iter.next_at(txindex).and_then(|(_, value)| {
let fee = value.into_inner();
let fee = value.into_owned();
vsize_iter.next_at(txindex).map(|(_, value)| {
let vsize = value.into_inner();
let vsize = value.into_owned();
Feerate::from((fee, vsize))
})
})
@@ -339,12 +337,14 @@ impl Vecs {
Ok(Self {
indexes_to_tx_count: ComputedVecsFromHeight::forced_import(
path,
file,
"tx_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -352,12 +352,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_input_count: ComputedVecsFromTxindex::forced_import(
path,
file,
"input_count",
false,
Source::None,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -365,12 +367,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_output_count: ComputedVecsFromTxindex::forced_import(
path,
file,
"output_count",
false,
Source::None,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -378,45 +382,45 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_tx_v1: ComputedVecsFromHeight::forced_import(
path,
file,
"tx_v1",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
indexes_to_tx_v2: ComputedVecsFromHeight::forced_import(
path,
file,
"tx_v2",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
indexes_to_tx_v3: ComputedVecsFromHeight::forced_import(
path,
file,
"tx_v3",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
computation,
indexes,
VecBuilderOptions::default().add_sum().add_cumulative(),
)?,
indexes_to_fee: ComputedValueVecsFromTxindex::forced_import(
path,
file,
"fee",
indexes,
Some(txindex_to_fee.boxed_clone()),
Source::Vec(txindex_to_fee.boxed_clone()),
version + VERSION + Version::ZERO,
computation,
format,
fetched,
StorableVecGeneatorOptions::default()
VecBuilderOptions::default()
.add_sum()
.add_cumulative()
.add_percentiles()
@@ -424,84 +428,96 @@ impl Vecs {
.add_average(),
)?,
indexes_to_feerate: ComputedVecsFromTxindex::forced_import(
path,
file,
"feerate",
false,
Source::None,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_percentiles()
.add_minmax()
.add_average(),
)?,
indexes_to_tx_vsize: ComputedVecsFromTxindex::forced_import(
path,
file,
"tx_vsize",
false,
Source::None,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_percentiles()
.add_minmax()
.add_average(),
)?,
indexes_to_tx_weight: ComputedVecsFromTxindex::forced_import(
path,
file,
"tx_weight",
false,
Source::None,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_percentiles()
.add_minmax()
.add_average(),
)?,
indexes_to_subsidy: ComputedValueVecsFromHeight::forced_import(
path,
file,
"subsidy",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
VecBuilderOptions::default()
.add_percentiles()
.add_sum()
.add_cumulative()
.add_minmax()
.add_average(),
compute_dollars,
indexes,
)?,
indexes_to_coinbase: ComputedValueVecsFromHeight::forced_import(
path,
file,
"coinbase",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
VecBuilderOptions::default()
.add_sum()
.add_cumulative()
.add_percentiles()
.add_minmax()
.add_average(),
compute_dollars,
indexes,
)?,
indexes_to_unclaimed_rewards: ComputedValueVecsFromHeight::forced_import(
path,
file,
"unclaimed_rewards",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
computation,
VecBuilderOptions::default().add_sum().add_cumulative(),
compute_dollars,
indexes,
)?,
indexes_to_p2a_count: ComputedVecsFromHeight::forced_import(
path,
file,
"p2a_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -509,12 +525,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_p2ms_count: ComputedVecsFromHeight::forced_import(
path,
file,
"p2ms_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -522,12 +540,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_p2pk33_count: ComputedVecsFromHeight::forced_import(
path,
file,
"p2pk33_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -535,12 +555,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_p2pk65_count: ComputedVecsFromHeight::forced_import(
path,
file,
"p2pk65_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -548,12 +570,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_p2pkh_count: ComputedVecsFromHeight::forced_import(
path,
file,
"p2pkh_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -561,12 +585,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_p2sh_count: ComputedVecsFromHeight::forced_import(
path,
file,
"p2sh_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -574,12 +600,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_p2tr_count: ComputedVecsFromHeight::forced_import(
path,
file,
"p2tr_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -587,12 +615,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_p2wpkh_count: ComputedVecsFromHeight::forced_import(
path,
file,
"p2wpkh_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -600,12 +630,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_p2wsh_count: ComputedVecsFromHeight::forced_import(
path,
file,
"p2wsh_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -613,12 +645,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_opreturn_count: ComputedVecsFromHeight::forced_import(
path,
file,
"opreturn_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -626,12 +660,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_unknownoutput_count: ComputedVecsFromHeight::forced_import(
path,
file,
"unknownoutput_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -639,12 +675,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_emptyoutput_count: ComputedVecsFromHeight::forced_import(
path,
file,
"emptyoutput_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
computation,
indexes,
VecBuilderOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
@@ -652,12 +690,14 @@ impl Vecs {
.add_cumulative(),
)?,
indexes_to_exact_utxo_count: ComputedVecsFromHeight::forced_import(
path,
file,
"exact_utxo_count",
true,
Source::Compute,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
computation,
indexes,
VecBuilderOptions::default().add_last(),
)?,
txindex_to_is_coinbase,
inputindex_to_value,
@@ -688,8 +728,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_txindex,
&indexer.vecs().txindex_to_txid,
&indexer.vecs.height_to_first_txindex,
&indexer.vecs.txindex_to_txid,
exit,
)
},
@@ -713,7 +753,7 @@ impl Vecs {
let compute_indexes_to_tx_vany =
|indexes_to_tx_vany: &mut ComputedVecsFromHeight<StoredUsize>, txversion| {
let mut txindex_to_txversion_iter = indexer.vecs().txindex_to_txversion.iter();
let mut txindex_to_txversion_iter = indexer.vecs.txindex_to_txversion.iter();
indexes_to_tx_vany.compute_all(
indexer,
indexes,
@@ -722,8 +762,8 @@ impl Vecs {
|vec, indexer, _, starting_indexes, exit| {
vec.compute_filtered_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_txindex,
&indexer.vecs().txindex_to_txid,
&indexer.vecs.height_to_first_txindex,
&indexer.vecs.txindex_to_txid,
|txindex| {
let v = txindex_to_txversion_iter.unwrap_get_inner(txindex);
v == txversion
@@ -739,31 +779,31 @@ impl Vecs {
self.txindex_to_is_coinbase.compute_if_necessary(
starting_indexes.txindex,
&indexer.vecs().txindex_to_txid,
&indexer.vecs.txindex_to_txid,
exit,
)?;
self.txindex_to_weight.compute_if_necessary(
starting_indexes.txindex,
&indexer.vecs().txindex_to_txid,
&indexer.vecs.txindex_to_txid,
exit,
)?;
self.txindex_to_vsize.compute_if_necessary(
starting_indexes.txindex,
&indexer.vecs().txindex_to_txid,
&indexer.vecs.txindex_to_txid,
exit,
)?;
self.inputindex_to_value.compute_if_necessary(
starting_indexes.inputindex,
&indexer.vecs().inputindex_to_outputindex,
&indexer.vecs.inputindex_to_outputindex,
exit,
)?;
self.txindex_to_output_value.compute_if_necessary(
starting_indexes.txindex,
&indexer.vecs().txindex_to_txid,
&indexer.vecs.txindex_to_txid,
exit,
)?;
@@ -775,9 +815,9 @@ impl Vecs {
// |vec, indexer, _, starting_indexes, exit| {
// vec.compute_sum_from_indexes(
// starting_indexes.txindex,
// &indexer.vecs().txindex_to_first_outputindex,
// &indexer.vecs.txindex_to_first_outputindex,
// self.indexes_to_output_count.txindex.as_ref().unwrap(),
// &indexer.vecs().outputindex_to_value,
// &indexer.vecs.outputindex_to_value,
// exit,
// )
// },
@@ -785,7 +825,7 @@ impl Vecs {
self.txindex_to_input_value.compute_if_necessary(
starting_indexes.txindex,
&indexer.vecs().txindex_to_txid,
&indexer.vecs.txindex_to_txid,
exit,
)?;
@@ -797,7 +837,7 @@ impl Vecs {
// |vec, indexer, _, starting_indexes, exit| {
// vec.compute_sum_from_indexes(
// starting_indexes.txindex,
// &indexer.vecs().txindex_to_first_inputindex,
// &indexer.vecs.txindex_to_first_inputindex,
// self.indexes_to_input_count.txindex.as_ref().unwrap(),
// &self.inputindex_to_value,
// exit,
@@ -807,13 +847,13 @@ impl Vecs {
self.txindex_to_fee.compute_if_necessary(
starting_indexes.txindex,
&indexer.vecs().txindex_to_txid,
&indexer.vecs.txindex_to_txid,
exit,
)?;
self.txindex_to_feerate.compute_if_necessary(
starting_indexes.txindex,
&indexer.vecs().txindex_to_txid,
&indexer.vecs.txindex_to_txid,
exit,
)?;
@@ -858,12 +898,12 @@ impl Vecs {
exit,
|vec, indexer, _, starting_indexes, exit| {
let mut txindex_to_first_outputindex_iter =
indexer.vecs().txindex_to_first_outputindex.iter();
indexer.vecs.txindex_to_first_outputindex.iter();
let mut txindex_to_output_count_iter = indexes.txindex_to_output_count.iter();
let mut outputindex_to_value_iter = indexer.vecs().outputindex_to_value.iter();
let mut outputindex_to_value_iter = indexer.vecs.outputindex_to_value.iter();
vec.compute_transform(
starting_indexes.height,
&indexer.vecs().height_to_first_txindex,
&indexer.vecs.height_to_first_txindex,
|(height, txindex, ..)| {
let first_outputindex = txindex_to_first_outputindex_iter
.unwrap_get_inner(txindex)
@@ -933,8 +973,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_p2aindex,
&indexer.vecs().p2aindex_to_p2abytes,
&indexer.vecs.height_to_first_p2aaddressindex,
&indexer.vecs.p2aaddressindex_to_p2abytes,
exit,
)
},
@@ -948,8 +988,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_p2msindex,
&indexer.vecs().p2msindex_to_txindex,
&indexer.vecs.height_to_first_p2msoutputindex,
&indexer.vecs.p2msoutputindex_to_txindex,
exit,
)
},
@@ -963,8 +1003,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_p2pk33index,
&indexer.vecs().p2pk33index_to_p2pk33bytes,
&indexer.vecs.height_to_first_p2pk33addressindex,
&indexer.vecs.p2pk33addressindex_to_p2pk33bytes,
exit,
)
},
@@ -978,8 +1018,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_p2pk65index,
&indexer.vecs().p2pk65index_to_p2pk65bytes,
&indexer.vecs.height_to_first_p2pk65addressindex,
&indexer.vecs.p2pk65addressindex_to_p2pk65bytes,
exit,
)
},
@@ -993,8 +1033,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_p2pkhindex,
&indexer.vecs().p2pkhindex_to_p2pkhbytes,
&indexer.vecs.height_to_first_p2pkhaddressindex,
&indexer.vecs.p2pkhaddressindex_to_p2pkhbytes,
exit,
)
},
@@ -1008,8 +1048,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_p2shindex,
&indexer.vecs().p2shindex_to_p2shbytes,
&indexer.vecs.height_to_first_p2shaddressindex,
&indexer.vecs.p2shaddressindex_to_p2shbytes,
exit,
)
},
@@ -1023,8 +1063,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_p2trindex,
&indexer.vecs().p2trindex_to_p2trbytes,
&indexer.vecs.height_to_first_p2traddressindex,
&indexer.vecs.p2traddressindex_to_p2trbytes,
exit,
)
},
@@ -1038,8 +1078,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_p2wpkhindex,
&indexer.vecs().p2wpkhindex_to_p2wpkhbytes,
&indexer.vecs.height_to_first_p2wpkhaddressindex,
&indexer.vecs.p2wpkhaddressindex_to_p2wpkhbytes,
exit,
)
},
@@ -1053,8 +1093,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_p2wshindex,
&indexer.vecs().p2wshindex_to_p2wshbytes,
&indexer.vecs.height_to_first_p2wshaddressindex,
&indexer.vecs.p2wshaddressindex_to_p2wshbytes,
exit,
)
},
@@ -1068,8 +1108,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_opreturnindex,
&indexer.vecs().opreturnindex_to_txindex,
&indexer.vecs.height_to_first_opreturnindex,
&indexer.vecs.opreturnindex_to_txindex,
exit,
)
},
@@ -1083,8 +1123,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_unknownoutputindex,
&indexer.vecs().unknownoutputindex_to_txindex,
&indexer.vecs.height_to_first_unknownoutputindex,
&indexer.vecs.unknownoutputindex_to_txindex,
exit,
)
},
@@ -1098,8 +1138,8 @@ impl Vecs {
|v, indexer, _, starting_indexes, exit| {
v.compute_count_from_indexes(
starting_indexes.height,
&indexer.vecs().height_to_first_emptyoutputindex,
&indexer.vecs().emptyoutputindex_to_txindex,
&indexer.vecs.height_to_first_emptyoutputindex,
&indexer.vecs.emptyoutputindex_to_txindex,
exit,
)
},
File diff suppressed because it is too large Load Diff
@@ -1,13 +0,0 @@
use std::ops::{Add, Div};
use brk_vec::StoredType;
pub trait ComputedType
where
Self: StoredType + From<usize> + Div<usize, Output = Self> + Add<Output = Self> + Ord,
{
}
impl<T> ComputedType for T where
T: StoredType + From<usize> + Div<usize, Output = Self> + Add<Output = Self> + Ord
{
}
File diff suppressed because it is too large Load Diff
@@ -1,258 +0,0 @@
use std::{collections::BTreeMap, ops::ControlFlow};
use brk_core::{CheckedSub, Dollars, HalvingEpoch, Height, Result, Timestamp};
use brk_exit::Exit;
use brk_state::{BlockState, OutputFilter, Outputs, Transacted};
use brk_vec::StoredIndex;
use rayon::prelude::*;
use crate::vecs::Indexes;
use super::cohort;
pub trait OutputCohorts {
fn tick_tock_next_block(&mut self, chain_state: &[BlockState], timestamp: Timestamp);
fn send(&mut self, height_to_sent: BTreeMap<Height, Transacted>, chain_state: &[BlockState]);
fn receive(&mut self, received: Transacted, height: Height, price: Option<Dollars>);
fn compute_overlaping_vecs(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()>;
}
impl OutputCohorts for Outputs<(OutputFilter, cohort::Vecs)> {
fn tick_tock_next_block(&mut self, chain_state: &[BlockState], timestamp: Timestamp) {
if chain_state.is_empty() {
return;
}
let prev_timestamp = chain_state.last().unwrap().timestamp;
self.by_date_range
.as_mut_vec()
.into_par_iter()
.for_each(|(filter, v)| {
let state = &mut v.state;
let _ = chain_state
.iter()
.try_for_each(|block_state| -> ControlFlow<()> {
let prev_days_old = block_state
.timestamp
.difference_in_days_between(prev_timestamp);
let days_old = block_state.timestamp.difference_in_days_between(timestamp);
if prev_days_old == days_old {
return ControlFlow::Continue(());
}
let is = filter.contains(days_old);
let was = filter.contains(prev_days_old);
if is && !was {
state.increment(&block_state.supply, block_state.price);
} else if was && !is {
state.decrement(&block_state.supply, block_state.price);
}
ControlFlow::Continue(())
});
});
}
fn send(&mut self, height_to_sent: BTreeMap<Height, Transacted>, chain_state: &[BlockState]) {
let mut time_based_vecs = self
.by_date_range
.as_mut_vec()
.into_iter()
.chain(self.by_epoch.as_mut_vec())
.collect::<Vec<_>>();
let last_timestamp = chain_state.last().unwrap().timestamp;
let current_price = chain_state.last().unwrap().price;
// dbg!(&height_to_sent);
height_to_sent.into_iter().for_each(|(height, sent)| {
let block_state = chain_state.get(height.unwrap_to_usize()).unwrap();
let prev_price = block_state.price;
let blocks_old = chain_state.len() - 1 - height.unwrap_to_usize();
let days_old = block_state
.timestamp
.difference_in_days_between(last_timestamp);
let days_old_foat = block_state
.timestamp
.difference_in_days_between_float(last_timestamp);
let older_than_hour =
jiff::Timestamp::from(last_timestamp.checked_sub(block_state.timestamp).unwrap())
.as_second()
>= 60 * 60;
time_based_vecs
.iter_mut()
.filter(|(filter, _)| match filter {
OutputFilter::From(from) => *from <= days_old,
OutputFilter::To(to) => *to > days_old,
OutputFilter::Range(range) => range.contains(&days_old),
OutputFilter::Epoch(epoch) => *epoch == HalvingEpoch::from(height),
_ => unreachable!(),
})
.for_each(|(_, vecs)| {
vecs.state.send(
&sent.spendable_supply,
current_price,
prev_price,
blocks_old,
days_old_foat,
older_than_hour,
);
});
sent.by_type.spendable.as_typed_vec().into_iter().for_each(
|(output_type, supply_state)| {
self.by_type.get_mut(output_type).1.state.send(
supply_state,
current_price,
prev_price,
blocks_old,
days_old_foat,
older_than_hour,
)
},
);
sent.by_size_group
.into_iter()
.for_each(|(group, supply_state)| {
self.by_size_range.get_mut(group).1.state.send(
&supply_state,
current_price,
prev_price,
blocks_old,
days_old_foat,
older_than_hour,
);
});
});
}
fn receive(&mut self, received: Transacted, height: Height, price: Option<Dollars>) {
let supply_state = received.spendable_supply;
[
&mut self.by_date_range.start_to_1d.1,
&mut self.by_epoch.mut_vec_from_height(height).1,
]
.into_iter()
.for_each(|v| {
v.state.receive(&supply_state, price);
});
self.by_type
.as_mut_vec()
.into_iter()
.for_each(|(filter, vecs)| {
let output_type = match filter {
OutputFilter::Type(output_type) => *output_type,
_ => unreachable!(),
};
vecs.state.receive(received.by_type.get(output_type), price)
});
received
.by_size_group
.into_iter()
.for_each(|(group, supply_state)| {
self.by_size_range
.get_mut(group)
.1
.state
.receive(&supply_state, price);
});
}
fn compute_overlaping_vecs(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
let by_date_range = self.by_date_range.as_vec();
let by_size_range = self.by_size_range.as_vec();
[
vec![(&mut self.all.1, self.by_epoch.vecs().to_vec())],
self.by_from_date
.as_mut_vec()
.into_iter()
.map(|(filter, vecs)| {
(
vecs,
by_date_range
.into_iter()
.filter(|(other, _)| filter.includes(other))
.map(|(_, v)| v)
.collect::<Vec<_>>(),
)
})
.collect::<Vec<_>>(),
self.by_up_to_date
.as_mut_vec()
.into_iter()
.map(|(filter, vecs)| {
(
vecs,
by_date_range
.into_iter()
.filter(|(other, _)| filter.includes(other))
.map(|(_, v)| v)
.collect::<Vec<_>>(),
)
})
.collect::<Vec<_>>(),
self.by_term
.as_mut_vec()
.into_iter()
.map(|(filter, vecs)| {
(
vecs,
by_date_range
.into_iter()
.filter(|(other, _)| filter.includes(other))
.map(|(_, v)| v)
.collect::<Vec<_>>(),
)
})
.collect::<Vec<_>>(),
self.by_from_size
.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.by_up_to_size
.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)
})
}
}
+1 -1
View File
@@ -16,7 +16,7 @@ derive_deref = { workspace = true }
fjall = { workspace = true }
jiff = { workspace = true }
log = { workspace = true }
rapidhash = "1.4.0"
rapidhash = "2.0.2"
rlimit = "0.10.2"
serde = { workspace = true }
serde_bytes = { workspace = true }
-5
View File
@@ -1,5 +0,0 @@
mod error;
mod value;
pub use error::*;
pub use value::*;
-39
View File
@@ -1,39 +0,0 @@
use std::{fmt::Debug, ops::Deref};
#[derive(Debug, Clone)]
pub enum Value<'a, T> {
Ref(&'a T),
Owned(T),
}
impl<T> Value<'_, T>
where
T: Sized + Debug + Clone,
{
pub fn into_inner(self) -> T {
match self {
Self::Ref(t) => t.to_owned(),
Self::Owned(t) => t,
}
}
}
impl<T> Deref for Value<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::Ref(t) => t,
Self::Owned(t) => t,
}
}
}
impl<T> AsRef<T> for Value<'_, T>
where
T: Sized + Debug + Clone,
{
fn as_ref(&self) -> &T {
match self {
Self::Ref(t) => t,
Self::Owned(t) => t,
}
}
}
@@ -20,6 +20,7 @@ pub enum Error {
WrongEndian,
DifferentVersion { found: Version, expected: Version },
UnexpectedData,
MmapsVecIsTooSmall,
IndexTooHigh,
EmptyVec,
@@ -34,7 +35,8 @@ pub enum Error {
WrongAddressType,
UnindexableDate,
String(&'static str),
Str(&'static str),
String(String),
}
impl From<time::SystemTimeError> for Error {
@@ -102,12 +104,13 @@ impl fmt::Display for Error {
Error::BincodeDecodeError(error) => Debug::fmt(&error, f),
Error::BincodeEncodeError(error) => Debug::fmt(&error, f),
Error::ZeroCopyError => write!(f, "ZeroCopy error"),
Error::UnexpectedData => write!(f, "Unexpected data"),
Error::WrongEndian => write!(f, "Wrong endian"),
Error::DifferentVersion { found, expected } => {
write!(
f,
"Different version; found: {found:?}, expected: {expected:?}"
"Different version found: {found:?}, expected: {expected:?}"
)
}
Error::MmapsVecIsTooSmall => write!(f, "Mmaps vec is too small"),
@@ -132,6 +135,7 @@ impl fmt::Display for Error {
"Date cannot be indexed, must be 2009-01-03, 2009-01-09 or greater"
),
Error::Str(s) => write!(f, "{s}"),
Error::String(s) => write!(f, "{s}"),
}
}
+63
View File
@@ -0,0 +1,63 @@
use super::{ByAmountRange, ByGreatEqualAmount, ByLowerThanAmount, GroupFilter};
#[derive(Default, Clone)]
pub struct AddressGroups<T> {
pub ge_amount: ByGreatEqualAmount<T>,
pub amount_range: ByAmountRange<T>,
pub lt_amount: ByLowerThanAmount<T>,
}
impl<T> AddressGroups<T> {
pub fn as_boxed_mut_vecs(&mut self) -> Vec<Box<[&mut T]>> {
vec![
Box::new(self.ge_amount.as_mut_vec()),
Box::new(self.amount_range.as_mut_vec()),
Box::new(self.lt_amount.as_mut_vec()),
]
}
pub fn as_mut_vecs(&mut self) -> Vec<&mut T> {
self.ge_amount
.as_mut_vec()
.into_iter()
.chain(self.amount_range.as_mut_vec())
.chain(self.lt_amount.as_mut_vec())
.collect::<Vec<_>>()
}
pub fn as_mut_separate_vecs(&mut self) -> Vec<&mut T> {
self.amount_range
.as_mut_vec()
.into_iter()
.collect::<Vec<_>>()
}
pub fn as_mut_overlapping_vecs(&mut self) -> Vec<&mut T> {
self.lt_amount
.as_mut_vec()
.into_iter()
.chain(self.ge_amount.as_mut_vec())
.collect::<Vec<_>>()
}
}
impl<T> AddressGroups<(GroupFilter, T)> {
pub fn vecs(&self) -> Vec<&T> {
self.amount_range
.vecs()
.into_iter()
.chain(self.lt_amount.vecs())
.chain(self.ge_amount.vecs())
.collect::<Vec<_>>()
}
}
impl<T> From<AddressGroups<T>> for AddressGroups<(GroupFilter, T)> {
fn from(value: AddressGroups<T>) -> Self {
Self {
amount_range: ByAmountRange::from(value.amount_range),
lt_amount: ByLowerThanAmount::from(value.lt_amount),
ge_amount: ByGreatEqualAmount::from(value.ge_amount),
}
}
}
@@ -0,0 +1,181 @@
use std::{
mem,
ops::{Add, AddAssign},
};
use super::GroupFilter;
use crate::OutputType;
#[derive(Default, Clone, Debug)]
pub struct ByAddressType<T> {
pub p2pk65: T,
pub p2pk33: T,
pub p2pkh: T,
pub p2sh: T,
pub p2wpkh: T,
pub p2wsh: T,
pub p2tr: T,
pub p2a: T,
}
impl<T> ByAddressType<T> {
pub fn get_unwrap(&self, address_type: OutputType) -> &T {
self.get(address_type).unwrap()
}
pub fn get(&self, address_type: OutputType) -> Option<&T> {
match address_type {
OutputType::P2PK65 => Some(&self.p2pk65),
OutputType::P2PK33 => Some(&self.p2pk33),
OutputType::P2PKH => Some(&self.p2pkh),
OutputType::P2SH => Some(&self.p2sh),
OutputType::P2WPKH => Some(&self.p2wpkh),
OutputType::P2WSH => Some(&self.p2wsh),
OutputType::P2TR => Some(&self.p2tr),
OutputType::P2A => Some(&self.p2a),
_ => None,
}
}
pub fn get_mut(&mut self, address_type: OutputType) -> Option<&mut T> {
match address_type {
OutputType::P2PK65 => Some(&mut self.p2pk65),
OutputType::P2PK33 => Some(&mut self.p2pk33),
OutputType::P2PKH => Some(&mut self.p2pkh),
OutputType::P2SH => Some(&mut self.p2sh),
OutputType::P2WPKH => Some(&mut self.p2wpkh),
OutputType::P2WSH => Some(&mut self.p2wsh),
OutputType::P2TR => Some(&mut self.p2tr),
OutputType::P2A => Some(&mut self.p2a),
_ => None,
}
}
pub fn as_mut_vec(&mut self) -> [&mut T; 8] {
[
&mut self.p2pk65,
&mut self.p2pk33,
&mut self.p2pkh,
&mut self.p2sh,
&mut self.p2wpkh,
&mut self.p2wsh,
&mut self.p2tr,
&mut self.p2a,
]
}
pub fn as_typed_vec(&self) -> [(OutputType, &T); 8] {
[
(OutputType::P2PK65, &self.p2pk65),
(OutputType::P2PK33, &self.p2pk33),
(OutputType::P2PKH, &self.p2pkh),
(OutputType::P2SH, &self.p2sh),
(OutputType::P2WPKH, &self.p2wpkh),
(OutputType::P2WSH, &self.p2wsh),
(OutputType::P2TR, &self.p2tr),
(OutputType::P2A, &self.p2a),
]
}
pub fn as_mut_typed_vec(&mut self) -> [(OutputType, &mut T); 8] {
[
(OutputType::P2PK65, &mut self.p2pk65),
(OutputType::P2PK33, &mut self.p2pk33),
(OutputType::P2PKH, &mut self.p2pkh),
(OutputType::P2SH, &mut self.p2sh),
(OutputType::P2WPKH, &mut self.p2wpkh),
(OutputType::P2WSH, &mut self.p2wsh),
(OutputType::P2TR, &mut self.p2tr),
(OutputType::P2A, &mut self.p2a),
]
}
pub fn into_typed_vec(&mut self) -> [(OutputType, T); 8]
where
T: Default,
{
[
(OutputType::P2PK65, mem::take(&mut self.p2pk65)),
(OutputType::P2PK33, mem::take(&mut self.p2pk33)),
(OutputType::P2PKH, mem::take(&mut self.p2pkh)),
(OutputType::P2SH, mem::take(&mut self.p2sh)),
(OutputType::P2WPKH, mem::take(&mut self.p2wpkh)),
(OutputType::P2WSH, mem::take(&mut self.p2wsh)),
(OutputType::P2TR, mem::take(&mut self.p2tr)),
(OutputType::P2A, mem::take(&mut self.p2a)),
]
}
}
impl<T> ByAddressType<(GroupFilter, T)> {
pub fn vecs(&self) -> [&T; 8] {
[
&self.p2pk65.1,
&self.p2pk33.1,
&self.p2pkh.1,
&self.p2sh.1,
&self.p2wpkh.1,
&self.p2wsh.1,
&self.p2tr.1,
&self.p2a.1,
]
}
}
impl<T> From<ByAddressType<T>> for ByAddressType<(GroupFilter, T)> {
fn from(value: ByAddressType<T>) -> Self {
Self {
p2pk65: (GroupFilter::Type(OutputType::P2PK65), value.p2pk65),
p2pk33: (GroupFilter::Type(OutputType::P2PK33), value.p2pk33),
p2pkh: (GroupFilter::Type(OutputType::P2PKH), value.p2pkh),
p2sh: (GroupFilter::Type(OutputType::P2SH), value.p2sh),
p2wpkh: (GroupFilter::Type(OutputType::P2WPKH), value.p2wpkh),
p2wsh: (GroupFilter::Type(OutputType::P2WSH), value.p2wsh),
p2tr: (GroupFilter::Type(OutputType::P2TR), value.p2tr),
p2a: (GroupFilter::Type(OutputType::P2A), value.p2a),
}
}
}
impl<T> Add for ByAddressType<T>
where
T: Add<Output = T>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
p2pk65: self.p2pk65 + rhs.p2pk65,
p2pk33: self.p2pk33 + rhs.p2pk33,
p2pkh: self.p2pkh + rhs.p2pkh,
p2sh: self.p2sh + rhs.p2sh,
p2wpkh: self.p2wpkh + rhs.p2wpkh,
p2wsh: self.p2wsh + rhs.p2wsh,
p2tr: self.p2tr + rhs.p2tr,
p2a: self.p2a + rhs.p2a,
}
}
}
impl<T> AddAssign for ByAddressType<T>
where
T: AddAssign,
{
fn add_assign(&mut self, rhs: Self) {
self.p2pk65 += rhs.p2pk65;
self.p2pk33 += rhs.p2pk33;
self.p2pkh += rhs.p2pkh;
self.p2sh += rhs.p2sh;
self.p2wpkh += rhs.p2wpkh;
self.p2wsh += rhs.p2wsh;
self.p2tr += rhs.p2tr;
self.p2a += rhs.p2a;
}
}
impl<T> ByAddressType<Option<T>> {
pub fn take(&mut self) {
self.as_mut_vec().into_iter().for_each(|opt| {
opt.take();
});
}
}
+131
View File
@@ -0,0 +1,131 @@
use super::GroupFilter;
#[derive(Default, Clone)]
pub struct ByAgeRange<T> {
pub up_to_1d: T,
pub _1d_to_1w: T,
pub _1w_to_1m: T,
pub _1m_to_2m: T,
pub _2m_to_3m: T,
pub _3m_to_4m: T,
pub _4m_to_5m: T,
pub _5m_to_6m: T,
pub _6m_to_1y: T,
pub _1y_to_2y: T,
pub _2y_to_3y: T,
pub _3y_to_4y: T,
pub _4y_to_5y: T,
pub _5y_to_6y: T,
pub _6y_to_7y: T,
pub _7y_to_8y: T,
pub _8y_to_10y: T,
pub _10y_to_12y: T,
pub _12y_to_15y: T,
pub from_15y: T,
}
impl<T> From<ByAgeRange<T>> for ByAgeRange<(GroupFilter, T)> {
fn from(value: ByAgeRange<T>) -> Self {
Self {
up_to_1d: (GroupFilter::LowerThan(1), value.up_to_1d),
_1d_to_1w: (GroupFilter::Range(1..7), value._1d_to_1w),
_1w_to_1m: (GroupFilter::Range(7..30), value._1w_to_1m),
_1m_to_2m: (GroupFilter::Range(30..2 * 30), value._1m_to_2m),
_2m_to_3m: (GroupFilter::Range(2 * 30..3 * 30), value._2m_to_3m),
_3m_to_4m: (GroupFilter::Range(3 * 30..4 * 30), value._3m_to_4m),
_4m_to_5m: (GroupFilter::Range(4 * 30..5 * 30), value._4m_to_5m),
_5m_to_6m: (GroupFilter::Range(5 * 30..6 * 30), value._5m_to_6m),
_6m_to_1y: (GroupFilter::Range(6 * 30..365), value._6m_to_1y),
_1y_to_2y: (GroupFilter::Range(365..2 * 365), value._1y_to_2y),
_2y_to_3y: (GroupFilter::Range(2 * 365..3 * 365), value._2y_to_3y),
_3y_to_4y: (GroupFilter::Range(3 * 365..4 * 365), value._3y_to_4y),
_4y_to_5y: (GroupFilter::Range(4 * 365..5 * 365), value._4y_to_5y),
_5y_to_6y: (GroupFilter::Range(5 * 365..6 * 365), value._5y_to_6y),
_6y_to_7y: (GroupFilter::Range(6 * 365..7 * 365), value._6y_to_7y),
_7y_to_8y: (GroupFilter::Range(7 * 365..8 * 365), value._7y_to_8y),
_8y_to_10y: (GroupFilter::Range(8 * 365..10 * 365), value._8y_to_10y),
_10y_to_12y: (GroupFilter::Range(10 * 365..12 * 365), value._10y_to_12y),
_12y_to_15y: (GroupFilter::Range(12 * 365..15 * 365), value._12y_to_15y),
from_15y: (GroupFilter::GreaterOrEqual(15 * 365), value.from_15y),
}
}
}
impl<T> ByAgeRange<T> {
pub fn as_vec(&mut self) -> [&T; 20] {
[
&self.up_to_1d,
&self._1d_to_1w,
&self._1w_to_1m,
&self._1m_to_2m,
&self._2m_to_3m,
&self._3m_to_4m,
&self._4m_to_5m,
&self._5m_to_6m,
&self._6m_to_1y,
&self._1y_to_2y,
&self._2y_to_3y,
&self._3y_to_4y,
&self._4y_to_5y,
&self._5y_to_6y,
&self._6y_to_7y,
&self._7y_to_8y,
&self._8y_to_10y,
&self._10y_to_12y,
&self._12y_to_15y,
&self.from_15y,
]
}
pub fn as_mut_vec(&mut self) -> [&mut T; 20] {
[
&mut self.up_to_1d,
&mut self._1d_to_1w,
&mut self._1w_to_1m,
&mut self._1m_to_2m,
&mut self._2m_to_3m,
&mut self._3m_to_4m,
&mut self._4m_to_5m,
&mut self._5m_to_6m,
&mut self._6m_to_1y,
&mut self._1y_to_2y,
&mut self._2y_to_3y,
&mut self._3y_to_4y,
&mut self._4y_to_5y,
&mut self._5y_to_6y,
&mut self._6y_to_7y,
&mut self._7y_to_8y,
&mut self._8y_to_10y,
&mut self._10y_to_12y,
&mut self._12y_to_15y,
&mut self.from_15y,
]
}
}
impl<T> ByAgeRange<(GroupFilter, T)> {
pub fn vecs(&self) -> [&T; 20] {
[
&self.up_to_1d.1,
&self._1d_to_1w.1,
&self._1w_to_1m.1,
&self._1m_to_2m.1,
&self._2m_to_3m.1,
&self._3m_to_4m.1,
&self._4m_to_5m.1,
&self._5m_to_6m.1,
&self._6m_to_1y.1,
&self._1y_to_2y.1,
&self._2y_to_3y.1,
&self._3y_to_4y.1,
&self._4y_to_5y.1,
&self._5y_to_6y.1,
&self._6y_to_7y.1,
&self._7y_to_8y.1,
&self._8y_to_10y.1,
&self._10y_to_12y.1,
&self._12y_to_15y.1,
&self.from_15y.1,
]
}
}
@@ -0,0 +1,257 @@
use std::ops::{Add, AddAssign};
use crate::Sats;
use super::GroupFilter;
#[derive(Debug, Default, Clone)]
pub struct ByAmountRange<T> {
pub _0sats: T,
pub _1sat_to_10sats: T,
pub _10sats_to_100sats: T,
pub _100sats_to_1k_sats: T,
pub _1k_sats_to_10k_sats: T,
pub _10k_sats_to_100k_sats: T,
pub _100k_sats_to_1m_sats: T,
pub _1m_sats_to_10m_sats: T,
pub _10m_sats_to_1btc: T,
pub _1btc_to_10btc: T,
pub _10btc_to_100btc: T,
pub _100btc_to_1k_btc: T,
pub _1k_btc_to_10k_btc: T,
pub _10k_btc_to_100k_btc: T,
pub _100k_btc_or_more: T,
}
impl<T> From<ByAmountRange<T>> for ByAmountRange<(GroupFilter, T)> {
fn from(value: ByAmountRange<T>) -> Self {
#[allow(clippy::inconsistent_digit_grouping)]
Self {
_0sats: (GroupFilter::LowerThan(Sats::_1.into()), value._0sats),
_1sat_to_10sats: (
GroupFilter::Range(Sats::_1.into()..Sats::_10.into()),
value._1sat_to_10sats,
),
_10sats_to_100sats: (
GroupFilter::Range(Sats::_10.into()..Sats::_100.into()),
value._10sats_to_100sats,
),
_100sats_to_1k_sats: (
GroupFilter::Range(Sats::_100.into()..Sats::_1K.into()),
value._100sats_to_1k_sats,
),
_1k_sats_to_10k_sats: (
GroupFilter::Range(Sats::_1K.into()..Sats::_10K.into()),
value._1k_sats_to_10k_sats,
),
_10k_sats_to_100k_sats: (
GroupFilter::Range(Sats::_10K.into()..Sats::_100K.into()),
value._10k_sats_to_100k_sats,
),
_100k_sats_to_1m_sats: (
GroupFilter::Range(Sats::_100K.into()..Sats::_1M.into()),
value._100k_sats_to_1m_sats,
),
_1m_sats_to_10m_sats: (
GroupFilter::Range(Sats::_1M.into()..Sats::_10M.into()),
value._1m_sats_to_10m_sats,
),
_10m_sats_to_1btc: (
GroupFilter::Range(Sats::_10M.into()..Sats::_1BTC.into()),
value._10m_sats_to_1btc,
),
_1btc_to_10btc: (
GroupFilter::Range(Sats::_1BTC.into()..Sats::_10BTC.into()),
value._1btc_to_10btc,
),
_10btc_to_100btc: (
GroupFilter::Range(Sats::_10BTC.into()..Sats::_100BTC.into()),
value._10btc_to_100btc,
),
_100btc_to_1k_btc: (
GroupFilter::Range(Sats::_100BTC.into()..Sats::_1K_BTC.into()),
value._100btc_to_1k_btc,
),
_1k_btc_to_10k_btc: (
GroupFilter::Range(Sats::_1K_BTC.into()..Sats::_10K_BTC.into()),
value._1k_btc_to_10k_btc,
),
_10k_btc_to_100k_btc: (
GroupFilter::Range(Sats::_10K_BTC.into()..Sats::_100K_BTC.into()),
value._10k_btc_to_100k_btc,
),
_100k_btc_or_more: (
GroupFilter::GreaterOrEqual(Sats::_100K_BTC.into()),
value._100k_btc_or_more,
),
}
}
}
impl<T> ByAmountRange<T> {
#[allow(clippy::inconsistent_digit_grouping)]
pub fn get_mut(&mut self, value: Sats) -> &mut T {
if value == Sats::ZERO {
&mut self._0sats
} else if value < Sats::_10 {
&mut self._1sat_to_10sats
} else if value < Sats::_100 {
&mut self._10sats_to_100sats
} else if value < Sats::_1K {
&mut self._100sats_to_1k_sats
} else if value < Sats::_10K {
&mut self._1k_sats_to_10k_sats
} else if value < Sats::_100K {
&mut self._10k_sats_to_100k_sats
} else if value < Sats::_1M {
&mut self._100k_sats_to_1m_sats
} else if value < Sats::_10M {
&mut self._1m_sats_to_10m_sats
} else if value < Sats::_1BTC {
&mut self._10m_sats_to_1btc
} else if value < Sats::_10BTC {
&mut self._1btc_to_10btc
} else if value < Sats::_100BTC {
&mut self._10btc_to_100btc
} else if value < Sats::_1K_BTC {
&mut self._100btc_to_1k_btc
} else if value < Sats::_10K_BTC {
&mut self._1k_btc_to_10k_btc
} else if value < Sats::_100K_BTC {
&mut self._10k_btc_to_100k_btc
} else {
&mut self._100k_btc_or_more
}
}
pub fn as_vec(&self) -> [&T; 15] {
[
&self._0sats,
&self._1sat_to_10sats,
&self._10sats_to_100sats,
&self._100sats_to_1k_sats,
&self._1k_sats_to_10k_sats,
&self._10k_sats_to_100k_sats,
&self._100k_sats_to_1m_sats,
&self._1m_sats_to_10m_sats,
&self._10m_sats_to_1btc,
&self._1btc_to_10btc,
&self._10btc_to_100btc,
&self._100btc_to_1k_btc,
&self._1k_btc_to_10k_btc,
&self._10k_btc_to_100k_btc,
&self._100k_btc_or_more,
]
}
pub fn as_typed_vec(&self) -> [(Sats, &T); 15] {
[
(Sats::ZERO, &self._0sats),
(Sats::_1, &self._1sat_to_10sats),
(Sats::_10, &self._10sats_to_100sats),
(Sats::_100, &self._100sats_to_1k_sats),
(Sats::_1K, &self._1k_sats_to_10k_sats),
(Sats::_10K, &self._10k_sats_to_100k_sats),
(Sats::_100K, &self._100k_sats_to_1m_sats),
(Sats::_1M, &self._1m_sats_to_10m_sats),
(Sats::_10M, &self._10m_sats_to_1btc),
(Sats::_1BTC, &self._1btc_to_10btc),
(Sats::_10BTC, &self._10btc_to_100btc),
(Sats::_100BTC, &self._100btc_to_1k_btc),
(Sats::_1K_BTC, &self._1k_btc_to_10k_btc),
(Sats::_10K_BTC, &self._10k_btc_to_100k_btc),
(Sats::_100K_BTC, &self._100k_btc_or_more),
]
}
pub fn as_mut_vec(&mut self) -> [&mut T; 15] {
[
&mut self._0sats,
&mut self._1sat_to_10sats,
&mut self._10sats_to_100sats,
&mut self._100sats_to_1k_sats,
&mut self._1k_sats_to_10k_sats,
&mut self._10k_sats_to_100k_sats,
&mut self._100k_sats_to_1m_sats,
&mut self._1m_sats_to_10m_sats,
&mut self._10m_sats_to_1btc,
&mut self._1btc_to_10btc,
&mut self._10btc_to_100btc,
&mut self._100btc_to_1k_btc,
&mut self._1k_btc_to_10k_btc,
&mut self._10k_btc_to_100k_btc,
&mut self._100k_btc_or_more,
]
}
}
impl<T> ByAmountRange<(GroupFilter, T)> {
pub fn vecs(&self) -> [&T; 15] {
[
&self._0sats.1,
&self._1sat_to_10sats.1,
&self._10sats_to_100sats.1,
&self._100sats_to_1k_sats.1,
&self._1k_sats_to_10k_sats.1,
&self._10k_sats_to_100k_sats.1,
&self._100k_sats_to_1m_sats.1,
&self._1m_sats_to_10m_sats.1,
&self._10m_sats_to_1btc.1,
&self._1btc_to_10btc.1,
&self._10btc_to_100btc.1,
&self._100btc_to_1k_btc.1,
&self._1k_btc_to_10k_btc.1,
&self._10k_btc_to_100k_btc.1,
&self._100k_btc_or_more.1,
]
}
}
impl<T> Add for ByAmountRange<T>
where
T: Add<Output = T>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
_0sats: self._0sats + rhs._0sats,
_1sat_to_10sats: self._1sat_to_10sats + rhs._1sat_to_10sats,
_10sats_to_100sats: self._10sats_to_100sats + rhs._10sats_to_100sats,
_100sats_to_1k_sats: self._100sats_to_1k_sats + rhs._100sats_to_1k_sats,
_1k_sats_to_10k_sats: self._1k_sats_to_10k_sats + rhs._1k_sats_to_10k_sats,
_10k_sats_to_100k_sats: self._10k_sats_to_100k_sats + rhs._10k_sats_to_100k_sats,
_100k_sats_to_1m_sats: self._100k_sats_to_1m_sats + rhs._100k_sats_to_1m_sats,
_1m_sats_to_10m_sats: self._1m_sats_to_10m_sats + rhs._1m_sats_to_10m_sats,
_10m_sats_to_1btc: self._10m_sats_to_1btc + rhs._10m_sats_to_1btc,
_1btc_to_10btc: self._1btc_to_10btc + rhs._1btc_to_10btc,
_10btc_to_100btc: self._10btc_to_100btc + rhs._10btc_to_100btc,
_100btc_to_1k_btc: self._100btc_to_1k_btc + rhs._100btc_to_1k_btc,
_1k_btc_to_10k_btc: self._1k_btc_to_10k_btc + rhs._1k_btc_to_10k_btc,
_10k_btc_to_100k_btc: self._10k_btc_to_100k_btc + rhs._10k_btc_to_100k_btc,
_100k_btc_or_more: self._100k_btc_or_more + rhs._100k_btc_or_more,
}
}
}
impl<T> AddAssign for ByAmountRange<T>
where
T: AddAssign,
{
fn add_assign(&mut self, rhs: Self) {
self._0sats += rhs._0sats;
self._1sat_to_10sats += rhs._1sat_to_10sats;
self._10sats_to_100sats += rhs._10sats_to_100sats;
self._100sats_to_1k_sats += rhs._100sats_to_1k_sats;
self._1k_sats_to_10k_sats += rhs._1k_sats_to_10k_sats;
self._10k_sats_to_100k_sats += rhs._10k_sats_to_100k_sats;
self._100k_sats_to_1m_sats += rhs._100k_sats_to_1m_sats;
self._1m_sats_to_10m_sats += rhs._1m_sats_to_10m_sats;
self._10m_sats_to_1btc += rhs._10m_sats_to_1btc;
self._1btc_to_10btc += rhs._1btc_to_10btc;
self._10btc_to_100btc += rhs._10btc_to_100btc;
self._100btc_to_1k_btc += rhs._100btc_to_1k_btc;
self._1k_btc_to_10k_btc += rhs._1k_btc_to_10k_btc;
self._10k_btc_to_100k_btc += rhs._10k_btc_to_100k_btc;
self._100k_btc_or_more += rhs._100k_btc_or_more;
}
}
@@ -0,0 +1,12 @@
#[derive(Debug, Default)]
pub struct ByAnyAddress<T> {
pub loaded: T,
pub empty: T,
}
impl<T> ByAnyAddress<Option<T>> {
pub fn take(&mut self) {
self.loaded.take();
self.empty.take();
}
}

Some files were not shown because too many files have changed in this diff Show More