Compare commits

...

365 Commits

Author SHA1 Message Date
nym21 f477bd66f3 release: v0.0.55 2025-06-13 10:23:38 +02:00
nym21 d7d77ae8f0 global: multiple fixes 2025-06-13 10:22:03 +02:00
nym21 31110a740d release: v0.0.54 2025-06-12 22:18:36 +02:00
nym21 b64d8b1d7f release: v0.0.53 2025-06-12 22:16:33 +02:00
nym21 c46006aacc web: filter possible index choices in charts 2025-06-12 22:09:33 +02:00
nym21 92f81b1493 web: fix css 2025-06-12 20:23:23 +02:00
nym21 70213cfc8f websites: default: add auto price series type 2025-06-12 18:41:56 +02:00
nym21 8a82bf5c50 websites: default: add live price 2025-06-12 18:10:24 +02:00
nym21 37405384a2 vec: fixed compressed, still slow par read, cli: made raw the default 2025-06-12 16:31:54 +02:00
nym21 54ea6cc53b indexer: only raw format + global: fixes 2025-06-12 12:33:43 +02:00
nym21 339c00d815 release: v0.0.52 2025-06-11 21:19:41 +02:00
nym21 ea6b4dcde2 websites: default: remove scrollToSelected 2025-06-11 21:19:22 +02:00
nym21 2b84623d1e release: v0.0.51 2025-06-11 21:09:07 +02:00
nym21 c8b3afa56b websites: default: fix sw adn co 2025-06-11 21:08:42 +02:00
nym21 1348f3c24c release: v0.0.50 2025-06-11 18:11:22 +02:00
nym21 62208ce3e1 websites: default: fix minBarSpacing 2025-06-11 18:11:11 +02:00
nym21 813b2481de release: v0.0.49 2025-06-11 17:51:31 +02:00
nym21 27b924ba61 cargo: set full version of crates 2025-06-11 17:51:11 +02:00
nym21 b40170b8ce websites: default: snapshot 2025-06-11 17:45:17 +02:00
nym21 8bfa9d2734 websites: default: snapshot 2025-06-11 11:25:25 +02:00
nym21 c7cf76d4a8 websites: default: snapshot 2025-06-10 18:54:18 +02:00
nym21 dfd2969b3e websites: default: snapshot 2025-06-09 17:58:26 +02:00
nym21 0e1866fe1d release: v0.0.48 2025-06-09 13:53:33 +02:00
nym21 b9ae46b913 readme: update 2025-06-09 13:53:09 +02:00
nym21 06e7284055 websites: default: snapshot 2025-06-09 13:05:03 +02:00
nym21 93289e8fca release: v0.0.47 2025-06-08 20:35:36 +02:00
nym21 130d5057d4 server: readme: add index-t-value documentation 2025-06-08 20:35:26 +02:00
nym21 be492d5084 server: add support for /api/X-to-Y + fix query cli + add meta api endpoints 2025-06-08 20:30:53 +02:00
nym21 e0bf1d736f query: add count param 2025-06-08 18:26:59 +02:00
nym21 5a6b71cbeb server: add ddos protection 2025-06-08 17:06:36 +02:00
nym21 e6934cd5e2 release: v0.0.46 2025-06-08 16:06:27 +02:00
nym21 b5aada0792 websites: mv sw to root 2025-06-08 16:05:21 +02:00
nym21 165ea83ac3 websites: update service worker 2025-06-08 13:03:37 +02:00
nym21 440a82dee4 release: v0.0.45 2025-06-08 09:11:31 +02:00
nym21 9c2d3e5e26 server: fix existing folder endpoints 2025-06-08 09:10:55 +02:00
nym21 6fb6abcbe5 release: v0.0.44 2025-06-07 18:53:51 +02:00
nym21 dc449dafd1 websites: default: up deps + fix css 2025-06-07 18:53:34 +02:00
nym21 ecdaeebbfb release: v0.0.43 2025-06-07 13:48:25 +02:00
nym21 fa958b59bd fetcher: support new api 2025-06-07 13:48:07 +02:00
nym21 fb3d8521cd computer: coinblocks fix overflow 2025-06-07 13:29:08 +02:00
nym21 608c401cf3 release: v0.0.42 2025-06-07 10:40:32 +02:00
nym21 1c3da90a24 release: v0.0.41 2025-06-07 10:31:36 +02:00
nym21 34567f3375 changelog: reset last 2025-06-07 10:31:07 +02:00
nym21 51bcbeb48f global: multiple fixes 2025-06-07 09:30:42 +02:00
nym21 cc0f9c42df global: snapshot 2025-06-06 16:08:20 +02:00
nym21 a11bf5523b global: wip 2025-06-06 12:23:45 +02:00
nym21 1921c3d901 global: wip 2025-06-06 10:46:38 +02:00
nym21 d568469e8b global: works but data is wrong 2025-06-04 17:01:16 +02:00
nym21 20d5c7e8d5 global: wip + fixed eager mode 2025-06-03 17:49:20 +02:00
nym21 9f289ed9de global: wip 2025-06-03 10:11:51 +02:00
nym21 93ee5e480b global: wip 2025-06-02 18:22:42 +02:00
nym21 98a312701f computer: more frequent flushes 2025-06-01 16:11:13 +02:00
nym21 cbcf603b63 global: wip 2025-06-01 14:37:19 +02:00
nym21 f976f672cf global: wip 2025-05-31 20:45:59 +02:00
nym21 cfc3081e8a global: snapshot 2025-05-29 10:39:58 +02:00
nym21 99818924ee global: snapshot 2025-05-28 16:53:18 +02:00
nym21 9bbf3a027f global: snapshot 2025-05-28 15:42:55 +02:00
nym21 93e01902e3 global: snapshot 2025-05-27 15:19:53 +02:00
nym21 34919aba05 global: versions 2025-05-26 11:34:37 +02:00
nym21 a8ee4cf57f release: v0.0.40 2025-05-25 13:38:48 +02:00
nym21 b39548b4c6 core: fix eq and cmp of float structs 2025-05-25 12:35:52 +02:00
nym21 4217c22ff6 global: utxos part 8 2025-05-25 00:27:18 +02:00
nym21 4ab10670c9 global: utxos part 7 2025-05-24 12:52:15 +02:00
nym21 2883f88de6 global: utxos part 6 2025-05-23 17:52:01 +02:00
nym21 e002a61a19 global: utxos part 5 2025-05-22 19:04:55 +02:00
nym21 5893376279 global: utxos part 4 2025-05-19 17:53:09 +02:00
nym21 411c5e4c4d global: snapshot 2025-05-18 17:28:09 +02:00
nym21 c2a77072d2 global: utxos part 3 2025-05-18 11:52:14 +02:00
nym21 c8a25934a6 global: utxos part 2 2025-05-17 19:51:52 +02:00
nym21 7b38355cd4 release: v0.0.39 2025-05-16 23:37:51 +02:00
nym21 ddc54e0b98 release: v0.0.38 2025-05-16 23:34:32 +02:00
nym21 8a7003782b global: utxos dataset part 1 2025-05-16 23:33:19 +02:00
nym21 8e6464dacb release: v0.0.37 2025-05-14 11:28:38 +02:00
nym21 92b1dc0afb global: dca classes 2025-05-14 11:28:18 +02:00
nym21 7562f51e07 release: v0.0.36 2025-05-13 13:01:32 +02:00
nym21 09bba99e68 kibo: add priceline 2025-05-13 13:01:11 +02:00
nym21 9d674cd49b global: snapshot 2025-05-13 11:46:03 +02:00
nym21 88a0c9ea03 global: returns (lump sum vs dca) 2025-05-13 01:27:21 +02:00
nym21 5014e0ce3e release: v0.0.35 2025-05-12 12:56:08 +02:00
nym21 b7a1ee9ebc global: averages + ratio datasets 2025-05-12 12:55:40 +02:00
nym21 292ceddd66 comp + kibo: add market smas 2025-05-10 13:17:51 +02:00
nym21 4b52b80000 release: v0.0.34 2025-05-09 21:35:07 +02:00
nym21 9f20664c6e global: add some market charts 2025-05-09 16:04:54 +02:00
nym21 851a6aac0e release: v0.0.33 2025-05-08 12:53:19 +02:00
nym21 1f1e73c47a vec: computed: fix eager path + delete path if lazy 2025-05-08 12:31:31 +02:00
nym21 112f61ca18 release: v0.0.32 2025-05-08 11:17:35 +02:00
nym21 96eeacbe2b lazy: done 2025-05-08 11:15:47 +02:00
nym21 3f62da879c comp + lazy: part 6 2025-05-06 12:23:37 +02:00
nym21 aa30feb875 comp + vec: snapshot before bug hunting 2025-05-06 00:44:39 +02:00
nym21 9ba3c2b7c5 global: big vec refactor + lazy 2025-05-05 12:47:52 +02:00
nym21 320c708e10 computer: lazy part 4 2025-05-03 17:28:48 +02:00
nym21 efa7294f59 computer: lazy part 3 2025-05-03 11:44:33 +02:00
nym21 ae0e092935 computer: lazy part 2 2025-05-01 20:52:39 +02:00
nym21 c77aecbfce computer: lazy part 1 2025-05-01 17:25:59 +02:00
nym21 700352ec45 vec: caching only in iter 2025-04-30 18:29:18 +02:00
nym21 664b125ce2 release: v0.0.31 2025-04-30 01:12:28 +02:00
nym21 5f4b1c9e32 global: fixes 2025-04-30 01:11:42 +02:00
nym21 d11d3f19bd fix: old X links that now directed to impersonater 2025-04-29 15:04:59 +02:00
nym21 f34f4f2738 computer: remove last indexes 2025-04-29 15:02:41 +02:00
nym21 15db7c2310 computer: use count instead of last_index 2025-04-29 11:33:14 +02:00
nym21 f9257ed04d global: vec iter part 2 2025-04-28 18:30:11 +02:00
nym21 15e6ef8488 computer: remove the need for &mut vecs 2025-04-28 11:21:28 +02:00
nym21 9ae0a57f22 global: snapshot 2025-04-27 16:29:21 +02:00
nym21 1e38c21f8e vec: make collect_range use into_iter 2025-04-27 10:39:14 +02:00
nym21 bdc3c19163 vec: iter + global: snapshot 2025-04-27 00:21:21 +02:00
nym21 d55478da54 kibo: remove old packages versions 2025-04-26 17:28:41 +02:00
nym21 82bcc55645 global: fixes + snapshot + packages 2025-04-26 17:22:58 +02:00
nym21 07618ebe43 global: renames + refactor + p2a support 2025-04-25 18:16:23 +02:00
nym21 1492834d1e fjall: use a single keyspace for all stores + core: locktime -> rawlocktime 2025-04-24 15:59:13 +02:00
nym21 5ab6197356 computer: init lazy/eager 2025-04-23 22:36:10 +02:00
nym21 0a789fe551 release: v0.0.30 2025-04-23 00:07:41 +02:00
nym21 caa8ff23ed kibo: fix charts data fetch 2025-04-23 00:04:09 +02:00
nym21 ee30d1d36d release: v0.0.29 2025-04-22 19:34:55 +02:00
nym21 0d9415db9d kibo: fix add ts-ignore 2025-04-22 19:33:57 +02:00
nym21 8020e1126f kibo: database: part 2 2025-04-22 19:31:30 +02:00
nym21 3439422057 kibo: database: part 1 2025-04-21 23:17:37 +02:00
nym21 68d2bf736f release: v0.0.28 2025-04-19 11:46:05 +02:00
nym21 d78c39fd8c computer + kibo: part 14 - fixes 2025-04-19 11:45:26 +02:00
nym21 b1dcad86b4 release: v0.0.27 2025-04-18 19:41:13 +02:00
nym21 9b6124074d dist: another fix 2025-04-18 19:40:09 +02:00
nym21 02cbaa1e80 release: v0.0.26 2025-04-18 19:00:19 +02:00
nym21 a12f1321c7 dist: fix format tag version 2025-04-18 18:59:53 +02:00
nym21 8b67f592ac release: v0.0.25 2025-04-18 18:48:10 +02:00
nym21 319d17b337 dist: fix ubuntu version 2025-04-18 18:45:27 +02:00
nym21 476eaa85da github: add manual trigger 2025-04-18 18:37:51 +02:00
nym21 d26099855c dist: update ubuntu version 2025-04-18 18:36:18 +02:00
nym21 e47456da17 release: v0.0.24 2025-04-18 18:22:23 +02:00
nym21 a464d5d0b6 crates: upgrade 2025-04-18 18:20:40 +02:00
nym21 1cfb7b5615 computer + kibo: part 13 2025-04-18 18:08:11 +02:00
nym21 ac7c2f3d03 kibo: cleanup 2025-04-17 17:06:44 +02:00
nym21 638d9e6e01 computer: part 12 2025-04-17 15:22:34 +02:00
nym21 8b9df2a396 parser: readme 2025-04-15 14:40:56 +02:00
nym21 d7fe911bde parser: readme 2025-04-15 11:56:50 +02:00
nym21 0acc3d511b parser: readme 2025-04-15 11:27:48 +02:00
nym21 4cf465f419 computer: unwrap 2025-04-15 11:18:31 +02:00
nym21 b686d317a9 release: v0.0.23 2025-04-14 22:22:19 +02:00
nym21 dcef541852 release: v0.0.22 2025-04-14 21:57:15 +02:00
nym21 abdd733f11 deps: upgrade 2025-04-14 21:56:47 +02:00
nym21 942431e882 computer + kibo: part 11 2025-04-14 16:27:22 +02:00
nym21 1c75ea046c computer + kibo: part 10 2025-04-11 19:21:35 +02:00
nym21 f32b6daa51 release: v0.0.21 2025-04-11 12:01:30 +02:00
nym21 3736d6ba5e computer + kibo: part 9 2025-04-11 12:00:26 +02:00
nym21 9788b01f35 readmes: update discord link yet again 2025-04-10 22:57:09 +02:00
nym21 9aec991da6 release: v0.0.20 2025-04-10 21:39:34 +02:00
nym21 910701ce04 cleanup: old files 2025-04-10 21:39:12 +02:00
nym21 34b462d511 global: snapshot 2025-04-10 21:38:39 +02:00
nym21 139e93b2f0 vec: rework part 4 2025-04-10 15:55:26 +02:00
nym21 0dd7e9359e vec: rework part 3 2025-04-10 01:11:52 +02:00
nym21 41cf0225e3 vec: rework part 2 2025-04-09 22:59:18 +02:00
nym21 962254e511 vec: rework part 1 2025-04-09 16:31:31 +02:00
nym21 a7f2b24bac comp + vec: tiny opti 2025-04-08 15:38:20 +02:00
nym21 1323d988af computer + kibo: part 8 2025-04-08 11:40:35 +02:00
nym21 7c49e5c749 release: v0.0.19 2025-04-07 15:48:39 +02:00
nym21 cd69ec4fa3 computer: part 7 2025-04-07 15:48:00 +02:00
nym21 4c7e9fbee2 computer: part 6 2025-04-07 12:18:18 +02:00
nym21 1639df5616 computer: part 5 2025-04-06 12:01:45 +02:00
nym21 810cdbd790 chore: Release 2025-04-05 12:13:31 +02:00
nym21 0d4f4aec4e computer: part 4 2025-04-05 12:12:55 +02:00
nym21 6b1863d3b4 chore: Release 2025-04-05 00:58:50 +02:00
nym21 27f5a3b16b dist: move config to config.toml 2025-04-05 00:55:22 +02:00
nym21 876cd8291b disk: init 2025-04-05 00:33:31 +02:00
nym21 d0c46e4ef3 server: yet another fix + release: v0.0.16 2025-04-04 19:20:28 +02:00
nym21 feb8898ebf server: forgot a 'v' + release: v0.0.15 2025-04-04 18:59:49 +02:00
nym21 4fef8c5cfd release: v0.0.14 2025-04-04 18:49:41 +02:00
nym21 7d56d8e35b server: fix downloaded repo version path 2025-04-04 18:49:21 +02:00
nym21 5f1a3a9c8f release: v0.0.13 2025-04-04 18:31:31 +02:00
nym21 0767b3156d global: snapshot 2025-04-04 18:31:10 +02:00
nym21 9f16379b41 readme: add warning 2025-04-04 18:23:03 +02:00
nym21 be632aaf37 kibo: fix simulation 2025-04-04 17:44:22 +02:00
nym21 118c87faf7 kibo: snapshot 2025-04-04 11:30:59 +02:00
nym21 ec1e53d566 kibo: finished converting ts types to jsdoc 2025-04-04 10:54:44 +02:00
nym21 6a17ee414a kibo: move types around 2025-04-04 00:40:40 +02:00
nym21 6700686e4b website: signals: upgrade to tresshaked v0.2.4 2025-04-03 15:15:55 +02:00
nym21 e8c34dd59b global: snapshot 2025-04-03 14:31:39 +02:00
nym21 4c2da31bb3 server: version repo url when not in dev mode 2025-04-02 16:49:35 +02:00
nym21 c0144b99bf release: v0.0.12 2025-04-02 16:45:19 +02:00
nym21 a0c32fc146 server: api doc 2025-04-02 16:43:31 +02:00
nym21 a07b641adb global: snapshot 2025-04-02 16:20:17 +02:00
nym21 0bb869fb33 kibo: changed font from satoshi to geist 2025-04-01 23:46:25 +02:00
nym21 72389e0129 readmes: update 2025-04-01 00:24:27 +02:00
nym21 f49529fa70 release: v0.0.11 2025-03-31 18:52:39 +02:00
nym21 afcc34b5cc readmes: add shields 2025-03-31 18:52:16 +02:00
nym21 655b99cac8 server: add not_modified check when no 'to' param 2025-03-31 17:19:33 +02:00
nym21 cc5091e28c kibo: part 5 2025-03-31 17:17:34 +02:00
nym21 1c72362c6b kibo: part 4 2025-03-31 10:02:59 +02:00
nym21 50ad5f681b kibo: part 3 (broken) 2025-03-29 13:01:46 +01:00
nym21 50bf670931 kibo: part 2 (broken) 2025-03-25 20:55:47 +01:00
nym21 7a8896864f kibo: part 1.1 2025-03-21 17:17:04 +01:00
nym21 51fbf148d9 kibo: part 1 2025-03-21 16:59:39 +01:00
nym21 a9929438cd computer: part 3 2025-03-21 11:45:23 +01:00
nym21 5a94b6b56c cargo: update crates 2025-03-20 21:40:57 +01:00
nym21 52cfbf60d4 computer: part 2 2025-03-20 21:40:06 +01:00
nym21 29c10f8854 computer: part 1 2025-03-19 12:01:54 +01:00
nym21 ad761e388d READMEs: updated 2025-03-17 09:55:06 +01:00
nym21 07493ab0a6 README: add badges 2025-03-15 17:51:54 +01:00
nym21 7441011ae7 pr: merge #17 from aki-mizu/main
Merge pull request #17 from aki-mizu/main
fix old biter link in README
2025-03-15 15:18:32 +00:00
Darrell d0818f456d fix biter link 2025-03-16 00:10:25 +09:00
nym21 06ea07a021 release: v0.0.9 2025-03-14 20:55:21 +01:00
nym21 36d97ad5ca compression: added everywhere 2025-03-14 18:10:03 +01:00
nym21 a995eb2929 vec: compression part 2 and done 2025-03-14 16:00:47 +01:00
nym21 c459a3033d vec: compression part 1 2025-03-13 17:11:04 +01:00
nym21 b4fbcf6bee global: snapshot 2025-03-11 17:55:14 +01:00
nym21 b9e679a514 vec: moved compute functions to computer 2025-03-11 16:27:45 +01:00
nym21 64d73b93e4 global: snapshot 2025-03-11 15:36:40 +01:00
nym21 db70b05088 global: snapshot 2025-03-10 23:08:07 +01:00
nym21 9428beeae5 global: snapshot 2025-03-10 11:42:15 +01:00
nym21 f9f7172702 brk: use brk_cli::main as bin 2025-03-07 00:00:27 +01:00
nym21 f1851b304c server: server struct 2025-03-06 14:52:26 +01:00
nym21 d2ca6f1d46 server: use query for search 2025-03-05 16:55:37 +01:00
nym21 b27297cdc6 server: multiple frontends + auto download from github when needed 2025-03-05 12:22:11 +01:00
nym21 0d0edd7917 global: snapshot + core: impl Display for bytes structs 2025-03-04 12:29:19 +01:00
nym21 fc6f12fb22 general: fixed builds 2025-03-03 19:36:17 +01:00
nym21 d24096374f cli: add config for run command 2025-03-03 19:11:47 +01:00
nym21 4f1d04009a readme: update 2025-03-03 12:50:49 +01:00
nym21 79e9fde937 readme: updatre 2025-03-03 12:34:27 +01:00
nym21 0ebaf6a171 readme: update 2025-03-03 10:52:07 +01:00
nym21 be2012f28d cli + query: improvements 2025-03-02 20:15:01 +01:00
nym21 ceefc8ffc6 global: snapshot 2025-03-02 12:45:33 +01:00
nym21 0453b6903a vec: readme: removed example 2025-03-02 11:19:01 +01:00
nym21 691952249b readme: update 2025-03-02 11:08:53 +01:00
nym21 0ceae2852e query: init 2025-03-02 11:08:35 +01:00
nym21 6d7ff38cf2 global: snapshot 2025-03-01 15:22:34 +01:00
nym21 1b93ccf608 global: snapshot 2025-02-28 11:52:25 +01:00
nym21 5b1ca3711a kibo.money: cleaned packages 2025-02-27 12:34:45 +01:00
nym21 877f9299e1 global: snapshot 2025-02-27 12:32:54 +01:00
nym21 677aca7a03 indexer: rollback fixed via fjall v2.6.6 (conv on discord) 2025-02-27 00:50:35 +01:00
nym21 66b31a62d0 readme: add disclaimer 2025-02-26 12:59:35 +01:00
nym21 34923638c5 global: cargo update 2025-02-26 10:45:00 +01:00
nym21 bb61b3dc22 global: init readmes 2025-02-26 10:07:05 +01:00
nym21 01ecae8979 indexer: fixed Fjalls rollback 2025-02-24 18:15:13 +01:00
nym21 53175c9ed7 core: init 2025-02-24 14:08:51 +01:00
nym21 bc7a76755b git: removed old files 2025-02-24 00:53:57 +01:00
nym21 92758f3e4e Merge pull request #16 from bitcoinresearchkit/bkr-v0.0
Bkr v0.0
2025-02-23 23:47:39 +00:00
nym21 b09767c526 cargo: update descriptions 2025-02-24 00:43:50 +01:00
nym21 2f93fd7c36 global: crates cleanup 2025-02-24 00:25:58 +01:00
nym21 8acbcc548c parser: fixed hanging + global: snapshot 2025-02-23 21:53:39 +01:00
nym21 19cf34f9d4 brk: first commit 2025-02-23 01:25:15 +01:00
nym21 8c3f519016 iterator: add xor support 2025-02-22 14:06:43 +01:00
nym21 e63b42278c iterator: simplified 2025-02-21 20:12:57 +01:00
nym21 66ecd2fcf8 iterator: small changes 2025-02-20 13:11:23 +01:00
nym21 f0d86f2392 indexer: moved height to iterator 2025-02-20 11:40:26 +01:00
nym21 5e39510f21 general: snapshot 2025-02-19 21:43:18 +01:00
nym21 2cb4d65f3d indexer: update readme 2025-02-19 10:56:07 +01:00
nym21 15f2e05192 indexer: improved rollback; global: snapshot 2025-02-18 22:43:12 +01:00
nym21 a122333aaa indexer: rm canopy+sanakirja + init rollback; svec: added truncate 2025-02-15 12:04:20 +01:00
nym21 06b2186bf9 cargo: updated deps 2025-02-14 19:04:14 +01:00
nym21 ed10dccfe2 pricer: snapshot 2025-02-14 19:02:46 +01:00
nym21 a1006dddb5 global: snapshot 2025-02-13 19:00:52 +01:00
nym21 443a32dc81 server: cleanup 2025-02-13 11:10:34 +01:00
nym21 b034b4fe2f python: add read bytes from vec example 2025-02-12 17:22:36 +01:00
nym21 27b270148b server: add tsv support 2025-02-12 16:00:31 +01:00
nym21 269c64e4ed server: smart generic vec routes build 2025-02-12 14:11:04 +01:00
nym21 eaf76e27f5 struct_iterable: removed, seems not needed anymore 2025-02-12 00:06:46 +01:00
nym21 385b881068 server: added csv support to api 2025-02-11 23:38:01 +01:00
nym21 cf26696d12 global: snapshot 2025-02-11 19:01:30 +01:00
nym21 cb7ea40e7c server: started 2025-02-11 18:52:08 +01:00
nym21 5aaa55197e workspace: use folder name for packages 2025-02-05 23:42:48 +01:00
nym21 d86d614520 global: from custom unsafe_slice to zerocopy 2025-02-05 16:42:53 +01:00
nym21 138ca80c10 global: snapshot 2025-02-05 00:23:58 +01:00
nym21 d11a1622f8 storable_vec: add modes 2025-02-04 20:56:48 +01:00
nym21 42c996e16e global: snapshot 2025-02-02 23:55:05 +01:00
nym21 1e37d75e49 global: snapshot 2025-02-02 23:28:03 +01:00
nym21 ad34d9d402 bomputer: init 2025-01-31 11:43:14 +01:00
nym21 8c610f8a83 workspace: reorg 2025-01-28 17:45:36 +01:00
nym21 f7f3e3cc03 bindex: no copy fjall get + small fixes 2025-01-28 11:30:10 +01:00
nym21 d68c6f9f2e bindex: contained fjall code 2025-01-27 23:25:28 +01:00
nym21 90a5c4fbf8 bindex: retrying fjall 2025-01-27 12:49:19 +01:00
nym21 042be6e229 bindex: removed addressindex to in/out puts as they're computable later 2025-01-27 09:55:28 +01:00
nym21 4923c2e204 bindex: converted txindex_to_txoutindex_in to storable vecs 2025-01-26 17:42:47 +01:00
nym21 b94d94e116 bindex: snapshot 2025-01-25 22:46:11 +01:00
nym21 d629ae8fbb bindex: snapshot 2025-01-23 11:11:39 +01:00
nym21 1296a2e9ec general: massive update 2025-01-22 11:38:50 +01:00
nym21 009d02fa68 storable_vec: general update 2025-01-16 13:25:35 +01:00
nym21 4cc57e9c91 bindex: back to sanakirja 2025-01-14 23:35:42 +01:00
nym21 d373c6398e bitbase: renamed to bindex 2025-01-14 12:38:06 +01:00
nym21 82746a0669 bitbase: vecdisk 2025-01-14 12:32:27 +01:00
nym21 1212c3627b cargo: update fjall to v2.5 2025-01-09 20:53:04 +01:00
nym21 813f16ccee bitbase: small optimization 2025-01-09 00:13:52 +01:00
nym21 1c3cb91ecd bitbase: rayon done 2025-01-08 18:01:28 +01:00
nym21 5b1735db2b bitbase: pre-rayon snapshot 2025-01-07 12:15:58 +01:00
nym21 bf31ee5fd6 bitbase: move to transactional 2025-01-06 10:45:04 +01:00
nym21 1380b42c1d bitbase: snapshot 2025-01-03 21:34:10 +01:00
nym21 dea853d840 init: separate parser crate 2025-01-01 10:32:39 +01:00
nym21 d72bf0739a changelog: update 2024-12-27 12:33:09 +01:00
nym21 481f5c0a97 server: add support for dataset by timestamp 2024-12-27 12:28:27 +01:00
nym21 2b017ac6b5 website: lc: update to v4.2.2 2024-12-22 10:22:55 +01:00
nym21 8a733ee337 changelog: update 2024-12-22 09:39:27 +01:00
nym21 9dd87a48a6 server: rework api side 2024-12-22 00:42:11 +01:00
nym21 8fabbde13b global: small fixes 2024-12-17 10:39:28 +01:00
nym21 e0a378cb81 biter: fix ? 2024-12-14 17:39:42 +01:00
nym21 0b3329ca35 git: fix ignore file 2024-12-14 14:56:55 +01:00
nym21 50c77b51db snkrj: move database struct to its own crate 2024-12-14 14:55:44 +01:00
nym21 c883ed19d6 biter: readme: update 2024-12-13 19:57:08 +01:00
nym21 795791219e global: snapshot 2024-12-13 19:55:32 +01:00
nym21 f6f4660cd2 website: fix the previous fix 2024-12-13 16:08:59 +01:00
nym21 9576f6e91e website: fix window resize brave bug 2024-12-13 12:30:56 +01:00
nym21 f5e5bbefb2 changelog: update 2024-12-04 11:22:28 +01:00
nym21 d4323fb5e0 website: dca sim: improve reactivity 2024-12-04 11:15:18 +01:00
nym21 8af1ddd10d website: moved more code to lc wrapper 2024-12-04 10:28:30 +01:00
nym21 62f6d9a413 website: lc: moved markers to container 2024-12-03 19:04:21 +01:00
nym21 783aed5826 website: start containing lc code in wrapper 2024-12-03 17:31:56 +01:00
k 141cd819a1 website: reorg 2024-12-02 10:03:41 +01:00
k 44fa96eb49 website: sim: wording 2024-11-28 21:52:40 +01:00
k 778b514b65 website: simulation: fix sats added 2024-11-28 18:36:22 +01:00
k afd58d69e4 website: simulation: fix 'days ago' 2024-11-28 18:34:01 +01:00
k 4af9849b2b website: simulation: small changes 2024-11-28 18:31:15 +01:00
k 4dac44e720 website: simulation: small fixes 2024-11-28 15:58:11 +01:00
k 71871901ef website: update 2024-11-27 18:36:43 +01:00
k d39e7584c0 website: update 2024-11-27 12:56:04 +01:00
k 4e9c5612ca website: small fixes 2024-11-25 11:28:28 +01:00
k c8510dd45b changelog: update 2024-11-23 16:18:57 +01:00
k c234c17352 general: snapshot 2024-11-23 16:17:06 +01:00
k cfae483d9d parser: fix gnericmap multi_insert_simple_average 2024-11-20 11:42:06 +01:00
k d01ea13de4 global: snapshot 2024-11-20 10:50:14 +01:00
k 9a73ee6952 readme: fix link 2024-11-15 13:44:14 +01:00
k 28eb9e8c17 readme: update 2024-11-15 13:41:16 +01:00
k 749c91f662 readme: removed the fluff 2024-11-15 13:15:30 +01:00
k 97ac17a12a website: delete useless logs 2024-11-11 15:23:07 +01:00
k 32fd4fa8ed website: big update 2024-11-11 15:20:31 +01:00
k 12fe4c6ba5 parser: fix metadata bug 2024-11-08 22:53:39 +01:00
k b1e9fd95ca readme: typos 2024-11-05 09:36:49 +01:00
k d83043d8f2 server: readd content disposition attachement if ext present 2024-11-04 12:44:15 +01:00
k 2abeca6220 readme: update 2024-11-04 12:35:48 +01:00
k 781810ed9c parser: databases: small changes 2024-11-04 11:09:54 +01:00
k 2142847de3 website: moved packages + added ratio charts to compare folders 2024-11-02 12:42:40 +01:00
k ca42c266ef website: update cohorts colors 2024-11-02 00:59:57 +01:00
k f258ef1011 website: readd ratio to individual cohort folders 2024-11-01 20:52:43 +01:00
k 38cb763fd3 website: add compare to liquidity 2024-11-01 20:47:23 +01:00
k 3fa78241ef parser: exit inside global defrag 2024-11-01 20:23:06 +01:00
k 3c7bc13be9 Merge branch 'main' of github.com:kibo-money/kibo 2024-11-01 20:20:54 +01:00
k 2441ca35b3 website: up signals + added compare folder to all groups 2024-11-01 20:19:43 +01:00
k 216a3977be parser: reactivate 'first_defragment' option 2024-10-31 11:39:36 +01:00
k 647a51af15 parser: fix defrag 2024-10-31 09:57:06 +01:00
k 530d4ce717 general: temp rollback 2024-10-30 19:22:42 +01:00
k e5d81b4d5c parser: added databases defragmentation 2024-10-30 19:13:41 +01:00
k 6eaeca1f3d parser: db: improve iter function 2024-10-29 19:38:52 +01:00
k 4220034eab parser: crates upgrade && remove ram from config 2024-10-29 15:22:44 +01:00
k 76a8ddd354 server: tried oxc vs swc, kept swc 2024-10-29 14:40:12 +01:00
k 0bad38a815 iterable: added custom version 2024-10-28 16:51:20 +01:00
k 48a8aad20e parser: AnyDataset DX improvements 2024-10-28 16:48:27 +01:00
k 36ad0b3014 parser: revert save logic 2024-10-27 12:10:22 +01:00
k 95fc103eaf parser: fix metadata versioning 2024-10-27 10:52:42 +01:00
k f5754780a8 global: snapshot 2024-10-26 16:41:38 +02:00
k 7114c3bdf9 general: fixes 2024-10-21 14:36:02 +02:00
k 5b9d599e83 global: snapshot 2024-10-20 18:31:43 +02:00
k ffa4871035 readme: update instances 2024-10-19 11:49:01 +02:00
k 01832ac139 server: add .json option to last value routes 2024-10-19 11:04:32 +02:00
k cb7ff2bb37 changelog: update 2024-10-19 10:46:00 +02:00
k 35dd194b28 general: snapshot 2024-10-19 10:34:12 +02:00
k 7dac857135 docker: snapshot 2024-10-17 19:53:00 +02:00
k 608ccafc70 server: add support for .json .csv and ?all=true 2024-10-16 18:38:43 +02:00
k 4cdc9ef9b3 changelog: update 2024-10-09 00:39:25 +02:00
k db60d4e453 parser: compress empty_address_data 2024-10-09 00:33:14 +02:00
k f5d427a04f parser: cargo cleanup 2024-10-08 22:37:36 +02:00
k e4893e446c cargo: update 2024-10-08 21:53:38 +02:00
k 79ffbf3d1d global: snapshot 2024-10-08 21:47:46 +02:00
k 068bb07d6e global: snapshot 2024-10-04 19:09:09 +02:00
k 1c9d118ba2 changelog: update 2024-10-03 18:37:00 +02:00
k 5308796bac parser: removed liquidity split for everything but all addresses 2024-10-03 17:38:43 +02:00
k 669205aa4d general: snapshot 2024-10-02 10:48:05 +02:00
k 9d2c2f7945 global: snapshot 2024-09-29 20:39:51 +02:00
k e3b44b0adb website: refactor 2024-09-24 17:13:29 +02:00
k 1a303a9c38 docker: init 2024-09-23 18:44:55 +02:00
k 2befa58fce global: add sell side risk ratio 2024-09-21 14:22:27 +02:00
k c8ded4ddb3 general: add /api/last route 2024-09-20 16:56:36 +02:00
514 changed files with 62824 additions and 39940 deletions
+291
View File
@@ -0,0 +1,291 @@
# This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/
#
# Copyright 2022-2024, axodotdev
# SPDX-License-Identifier: MIT or Apache-2.0
#
# CI that:
#
# * checks for a Git Tag that looks like a release
# * builds artifacts with dist (archives, installers, hashes)
# * uploads those artifacts to temporary workflow zip
# * on success, uploads the artifacts to a GitHub Release
#
# Note that the GitHub Release will be created with a generated
# title/body based on your changelogs.
name: Release
permissions:
"contents": "write"
# This task will run whenever you push a git tag that looks like a version
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where
# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
#
# If PACKAGE_NAME is specified, then the announcement will be for that
# package (erroring out if it doesn't have the given version or isn't dist-able).
#
# If PACKAGE_NAME isn't specified, then the announcement will be for all
# (dist-able) packages in the workspace with that version (this mode is
# intended for workspaces with only one dist-able package, or with all dist-able
# packages versioned/released in lockstep).
#
# If you push multiple tags at once, separate instances of this workflow will
# spin up, creating an independent announcement for each one. However, GitHub
# will hard limit this to 3 tags per commit, as it will assume more tags is a
# mistake.
#
# If there's a prerelease-style suffix to the version, then the release(s)
# will be marked as a prerelease.
on:
pull_request:
push:
tags:
- '**[0-9]+.[0-9]+.[0-9]+*'
jobs:
# Run 'dist plan' (or host) to determine what tasks we need to do
plan:
runs-on: "ubuntu-latest"
outputs:
val: ${{ steps.plan.outputs.manifest }}
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }}
publishing: ${{ !github.event.pull_request }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dist
# we specify bash to get pipefail; it guards against the `curl` command
# failing. otherwise `sh` won't catch that `curl` returned non-0
shell: bash
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.28.0/cargo-dist-installer.sh | sh"
- name: Cache dist
uses: actions/upload-artifact@v4
with:
name: cargo-dist-cache
path: ~/.cargo/bin/dist
# sure would be cool if github gave us proper conditionals...
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
# functionality based on whether this is a pull_request, and whether it's from a fork.
# (PRs run on the *source* but secrets are usually on the *target* -- that's *good*
# but also really annoying to build CI around when it needs secrets to work right.)
- id: plan
run: |
dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
echo "dist ran successfully"
cat plan-dist-manifest.json
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@v4
with:
name: artifacts-plan-dist-manifest
path: plan-dist-manifest.json
# Build and packages all the platform-specific things
build-local-artifacts:
name: build-local-artifacts (${{ join(matrix.targets, ', ') }})
# Let the initial task tell us to not run (currently very blunt)
needs:
- plan
if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}
strategy:
fail-fast: false
# Target platforms/runners are computed by dist in create-release.
# Each member of the matrix has the following arguments:
#
# - runner: the github runner
# - dist-args: cli flags to pass to dist
# - install-dist: expression to run to install dist on the runner
#
# Typically there will be:
# - 1 "global" task that builds universal installers
# - N "local" tasks that build each platform's binaries and platform-specific installers
matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
runs-on: ${{ matrix.runner }}
container: ${{ matrix.container && matrix.container.image || null }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
steps:
- name: enable windows longpaths
run: |
git config --global core.longpaths true
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Rust non-interactively if not already installed
if: ${{ matrix.container }}
run: |
if ! command -v cargo > /dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
fi
- name: Install dist
run: ${{ matrix.install_dist.run }}
# Get the dist-manifest
- name: Fetch local artifacts
uses: actions/download-artifact@v4
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- name: Install dependencies
run: |
${{ matrix.packages_install }}
- name: Build artifacts
run: |
# Actually do builds and make zips and whatnot
dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
echo "dist ran successfully"
- id: cargo-dist
name: Post-build
# We force bash here just because github makes it really hard to get values up
# to "real" actions without writing to env-vars, and writing to env-vars has
# inconsistent syntax between shell and powershell.
shell: bash
run: |
# Parse out what we just built and upload it to scratch storage
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
- name: "Upload artifacts"
uses: actions/upload-artifact@v4
with:
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
path: |
${{ steps.cargo-dist.outputs.paths }}
${{ env.BUILD_MANIFEST_NAME }}
# Build and package all the platform-agnostic(ish) things
build-global-artifacts:
needs:
- plan
- build-local-artifacts
runs-on: "ubuntu-latest"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@v4
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
- name: Fetch local artifacts
uses: actions/download-artifact@v4
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- id: cargo-dist
shell: bash
run: |
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
echo "dist ran successfully"
# Parse out what we just built and upload it to scratch storage
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
- name: "Upload artifacts"
uses: actions/upload-artifact@v4
with:
name: artifacts-build-global
path: |
${{ steps.cargo-dist.outputs.paths }}
${{ env.BUILD_MANIFEST_NAME }}
# Determines if we should publish/announce
host:
needs:
- plan
- build-local-artifacts
- build-global-artifacts
# Only run if we're "publishing", and only if local and global didn't fail (skipped is fine)
if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: "ubuntu-latest"
outputs:
val: ${{ steps.host.outputs.manifest }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@v4
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Fetch artifacts from scratch-storage
- name: Fetch artifacts
uses: actions/download-artifact@v4
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- id: host
shell: bash
run: |
dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
echo "artifacts uploaded and released successfully"
cat dist-manifest.json
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@v4
with:
# Overwrite the previous copy
name: artifacts-dist-manifest
path: dist-manifest.json
# Create a GitHub Release while uploading all files to it
- name: "Download GitHub Artifacts"
uses: actions/download-artifact@v4
with:
pattern: artifacts-*
path: artifacts
merge-multiple: true
- name: Cleanup
run: |
# Remove the granular manifests
rm -f artifacts/*-dist-manifest.json
- name: Create GitHub Release
env:
PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}"
ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}"
ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
RELEASE_COMMIT: "${{ github.sha }}"
run: |
# Write and read notes from a file to avoid quoting breaking things
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
announce:
needs:
- plan
- host
# use "always() && ..." to allow us to wait for all publish jobs while
# still allowing individual publish jobs to skip themselves (for prereleases).
# "host" however must run to completion, no skipping allowed!
if: ${{ always() && needs.host.result == 'success' }}
runs-on: "ubuntu-latest"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
+3 -31
View File
@@ -1,46 +1,18 @@
# Mac OS
.DS_Store
# To do
/charts
TODO.md
# Builds
dist
target
# I/O
in
out
.log
/datasets
/price
# Sync
.stfolder
# Copies
*\ copy*
# Ignored
ignore
# Scripts
/start-node.sh
_*
# Editors
.vscode
.zed
# Configs
config.toml
# Flamegraph
flamegraph/
flamegraph.svg
# Benchmarks
benches
# Snapshots
snapshots*/
# Logs
.log
+87 -33
View File
@@ -1,34 +1,88 @@
# Changelog
<!--
## v. 0.X.Y | WIP
![Image of the kibō Web App version 0.X.Y](./assets/v0.X.Y.jpg)
# 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)
-->
## v. 0.4.1 | WIP
# v0.X.0 | WIP | A new beginning
<!-- ![Image of the kibō Web App version 0.4.1](./assets/v0.4.1.jpg) -->
Full rewrite
# [kibo-v0.5.0](https://github.com/kibo-money/kibo/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)
## Datasets
- Added `Sell Side Risk Ratio` to all entities
- Added `Open`, `High` and `Low` datasets
- Added `Satoshis Per Dollar`
- Added `All Time High`
- Added `All Time High Date`
- Added `Days Since All Time High`
- Added `Max Days Between All Time Highs`
- Added `Max Years Between All Time Highs`
- Added `Drawdown`
- Added `Adjusted Value Created`, `Adjusted Value Destroyed` and `Adjusted Spent Output Profit Ratio` to all entities
- Added `Realized Profit To Loss Ratio` to all entities
- Added `Hash Price Min`
- Added `Hash Price Rebound`
- Removed all year datasets (25) in favor for epoch datasets (5), the former was too granular to be really useful
- Removed datasets split by liquidity for all datasets **already split by any address kind**, while fun to have, they took time to compute, ram, and space to store and no one was actually checking them
- Fixed a lot of values in split by liquidity datasets
## Website
- Updated the design yet again which made the website for something more minimal and easier on the eyes
- Added a *Save In Bitcoin* (DCA) simulation page
- ~Added a dashboard~ Added the latest values to the tree next to each option instead, while less values are visible at a time, it's much more readable and organised
- Added a library of PDFs
- Fixed service worker not passing 304 (not modified) response and instead serving cached responses
- Fixed history not being properly registered
- Fixed prices on charts not having a wide enough background due to the font not being fully loaded during the creation of the chart
- Fixed window being moveable on iOS when in standalone mode when it shouldn't be
- Added `Compare` section to all groups, to compare all datasets within a group
- Updated `Solid Signals` library, which had an important breaking change on the `createEffect` function which might bring some bugs
- Fixed some datasets paths
- A lot of code reorg and file splits
- Adopted a framework like approach to load pages while still being pure JS without a build step
- Probably more that was forgotten
## Parser
- Added a `/datasets/last` json file with all the latest values
- Added `--rpcconnect` parameter to the config
- Added handling of SIGINT and SIGTERM terminal signals which menas you can now safely CTRL+C or kill the parser while it's exporting
- Added config print at the start of the program
- Compressed `empty_address_data` struct to save space (should shave of between up to 50% of the `address_index_to_empty_address_data` database)
- Doubled the number of `txid_to_tx_data` databases from 4096 to 8192
- ~Added `--recompute_computed true` argument, to allow recomputation of computed datasets in case of a bug~ Buggy for now
- Fixed not saved arguments, not being processed properly
- Fixed bug in `generic_map.multi_insert_simple_average`
- Added defragmentation option `--first-defragment true` of databases to save space (which can save up to 50%)
- Fixed bug in the computation of averages in `GenericMap`
- Added support and paramer for cookie files with `--rpccookiefile`, and auto find if the path is `--datadir/.cookie`
- Increased number of retries and time between them when fetching price from exchanges APIs
## Server
- Fixed links in several places missing the `/api` part and thus not working
- Fixed broken last values routes
- Added support for the `/datasets/last` file via the `/api/last` route
- Added support for `.json` (won't change anything) and `.csv` (will download a csv file) extension at the end of datasets routes
- Added `all=true` query parameter to dataset routes to get to full history
## v. 0.4.0 | [861950](https://mempool.space/block/00000000000000000000530d0e30ccf7deeace122dcc99f2668a06c6dad83629) - 2024/09/19
## Biter
![Image of the kibō Web App version 0.4.0](./assets/v0.4.0.jpg)
- Moved back to this repo
### Brand
# [kibo-v0.4.0](https://github.com/kibo-money/kibo/tree/a64c544815d9ef785e2fc1323582f774f16b9200) | [861950](https://mempool.space/block/00000000000000000000530d0e30ccf7deeace122dcc99f2668a06c6dad83629) - 2024/09/19
- **Satonomics** is now **kibō** 🎉
![Image of the kibo Web App version 0.4.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.4.0.jpg)
### Website
## Brand
- **Satonomics** is now **kibo** 🎉
## Website
- Complete redesign of the website
- Rewrote the whole application and removed `node`/`npm`/`pnpm` dependencies in favor for pure `HTML`/`CSS`/`Javascript`
@@ -36,7 +90,7 @@
- Added Trading View attribution link to the settings frame and file in the lightweight charts folder
- Many other changes
### Parser
## Parser
- Changed the block iterator from a custom version of [bitcoin-explorer](https://crates.io/crates/bitcoin-explorer) to the homemade [biter](https://crates.io/crates/biter) which allows the parser to run alongside `bitcoind`
- Added datasets compression thanks to [zstd](https://crates.io/crates/zstd) to reduce disk usage
@@ -51,17 +105,17 @@
- Various first run fixes
- Added to `-h` which arguments are saved, which is all of them at the time of writing
### Server
## Server
- Updated the code to support compressed binaries
- Added serving of the website
- Improved `Cache-Control` behavior
## v. 0.3.0 | [853930](https://mempool.space/block/00000000000000000002eb5e9a7950ca2d5d98bd1ed28fc9098aa630d417985d) - 2024/07/26
# [kibo-v0.3.0](https://github.com/kibo-money/kibo/tree/b68b016091c45b071218fba01bac5b76e8eaf18c) | [853930](https://mempool.space/block/00000000000000000002eb5e9a7950ca2d5d98bd1ed28fc9098aa630d417985d) - 2024/07/26
![Image of the Satonomics Web App version 0.3.0](./assets/v0.3.0.jpg)
![Image of the Satonomics Web App version 0.3.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.3.0.jpg)
### Parser
## Parser
- Global
- Improved self-hosting by:
@@ -104,7 +158,7 @@
- Price
- Improved error message when price cannot be found
### App
## App
- General
- Added chart scroll button for nice animations à la Wicked
@@ -130,17 +184,17 @@
- Settings
- Removed the horizontal scroll bar which was unintended
### Server
## Server
- Run file
- 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`
## v. 0.2.0 | [851286](https://mempool.space/block/0000000000000000000281ca7f1bf8c50702bfca168c7af1bdc67c977c1ac8ed) - 2024/07/08
# [kibo-v0.2.0](https://github.com/kibo-money/kibo/tree/248187889283597c5dbb806292297453c25e97b8) | [851286](https://mempool.space/block/0000000000000000000281ca7f1bf8c50702bfca168c7af1bdc67c977c1ac8ed) - 2024/07/08
![Image of the Satonomics Web App version 0.2.0](./assets/v0.2.0.jpg)
![Image of the Satonomics Web App version 0.2.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.2.0.jpg)
### App
## App
- General
- Added the height version of all datasets and many optimizations to make them usable but only available on desktop and tablets for now
@@ -168,24 +222,24 @@
- Hopefully made scrollbars a little more subtle on WIndows and Linux, can't test
- Generale style updates
### Parser
## Parser
- Fixed ulimit only being run in Mac OS instead of whenever the program is detected
## v. 0.1.1 | [849240](https://mempool.space/block/000000000000000000002b8653988655071c07bb5f7181c038f9326bc86db741) - 2024/06/24
# [kibo-v0.1.1](https://github.com/kibo-money/kibo/tree/e55b5195a9de9aea306903c94ed63cb1720fda5f) | [849240](https://mempool.space/block/000000000000000000002b8653988655071c07bb5f7181c038f9326bc86db741) - 2024/06/24
![Image of the Satonomics Web App version 0.1.1](./assets/v0.1.1.jpg)
![Image of the Satonomics Web App version 0.1.1](https://github.com/kibo-money/kibo/blob/main/_assets/v0.1.1.jpg)
### Parser
## Parser
- Fixed overflow in `Price` struct which caused many Realized Caps and Realized Prices to have completely bogus data
- Fixed Realized Cap computation which was using rounded prices instead normal ones
### Server
## Server
- Added the chunk, date and time of the request to the terminal logs
### App
## App
- Chart
- Added double click option on a legend to toggle the visibility of all other series
@@ -218,14 +272,14 @@
- Misc
- Removed tracker even though it was a very privacy friendly as it appeared to not be working properly
### Price
## Price
- Deleted old price datasets and their backups
## v. 0.1.0 | [848642](https://mempool.space/block/000000000000000000020be5761d70751252219a9557f55e91ecdfb86c4e026a) - 2024/06/19
# [kibo-v0.1.0](https://github.com/kibo-money/kibo/tree/a1a576d088c8f83ed32d48753a7611f70a964574) | [848642](https://mempool.space/block/000000000000000000020be5761d70751252219a9557f55e91ecdfb86c4e026a) - 2024/06/19
![Image of the Satonomics Web App version 0.1.0](./assets/v0.1.0.jpg)
![Image of the Satonomics Web App version 0.1.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.1.0.jpg)
## v. 0.0.X | [835444](https://mempool.space/block/000000000000000000009f93907a0dd83c080d5585cc7ec82c076d45f6d7c872) - 2024/03/20
# kibo-v0.0.1 | [835444](https://mempool.space/block/000000000000000000009f93907a0dd83c080d5585cc7ec82c076d45f6d7c872) - 2024/03/20
![Image of the Satonomics Web App version 0.0.X](./assets/v0.0.X.jpg)
![Image of the Satonomics Web App version 0.0.X](https://github.com/kibo-money/kibo/blob/main/_assets/v0.0.X.jpg)
-8
View File
@@ -1,8 +0,0 @@
# Guidelines
## Parser
- Avoid floats as much as possible
- Use structs like `WAmount` and `Price` for calculations
- **Only** use `WAmount.to_btc()` when inserting or computing inside a dataset. It is **very** expensive.
- No `Arc`, `Rc`, `Mutex` even from third party libraries, they're slower
Generated
+3593
View File
File diff suppressed because it is too large Load Diff
+80
View File
@@ -0,0 +1,80 @@
[workspace]
resolver = "3"
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.homepage = "https://bitcoinresearchkit.org"
package.repository = "https://github.com/bitcoinresearchkit/brk"
[profile.release]
lto = "fat"
codegen-units = 1
panic = "abort"
[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" }
byteview = "=0.6.1"
clap = { version = "4.5.40", features = ["string"] }
clap_derive = "4.5.40"
color-eyre = "0.6.5"
derive_deref = "1.1.1"
fjall = "2.11.0"
jiff = "0.2.14"
log = { version = "0.4.27" }
minreq = { version = "2.13.4", features = ["https", "serde_json"] }
rayon = "1.10.0"
serde = { version = "1.0.219" }
serde_bytes = "0.11.17"
serde_derive = "1.0.219"
serde_json = { version = "1.0.140", features = ["float_roundtrip"] }
tabled = "0.20.0"
tokio = { version = "1.45.1", features = ["rt-multi-thread"] }
zerocopy = { version = "0.8.25" }
zerocopy-derive = "0.8.25"
[workspace.metadata.release]
shared-version = true
tag-name = "v{{version}}"
pre-release-commit-message = "release: v{{version}}"
tag-message = "release: v{{version}}"
[workspace.metadata.dist]
cargo-dist-version = "0.28.0"
ci = "github"
installers = []
targets = [
"aarch64-apple-darwin",
"aarch64-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-unknown-linux-gnu",
]
[workspace.metadata.dist.github-custom-runners]
global = "ubuntu-latest"
aarch64-apple-darwin.runner = "macos-14"
x86_64-unknown-linux-gnu.runner = "ubuntu-latest"
x86_64-unknown-linux-gnu.container = { image = "quay.io/pypa/manylinux_2_28_x86_64", host = "x86_64-unknown-linux-musl" }
aarch64-unknown-linux-gnu.runner = "ubuntu-latest"
aarch64-unknown-linux-gnu.container = { image = "quay.io/pypa/manylinux_2_28_x86_64", host = "x86_64-unknown-linux-musl" }
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 kibō
Copyright (c) 2025 bitcoinresearchkit, kibo.money, satonomics
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+81 -167
View File
@@ -1,188 +1,102 @@
<p align="center">
<a href="https://kibo.money" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-full-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-full-light.svg">
<img alt="kibō" src="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-full-light.svg" width="300" height="auto" style="max-width: 100%;">
</picture>
# Bitcoin Research Kit
<p align="left">
<a href="https://github.com/bitcoinresearchkit/brk">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/bitcoinresearchkit/brk?style=social">
</a>
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
<img src="https://img.shields.io/crates/l/brk" alt="License" />
</a>
<a href="https://crates.io/crates/brk">
<img src="https://img.shields.io/crates/v/brk" alt="Version" />
</a>
<a href="https://docs.rs/brk">
<img src="https://img.shields.io/docsrs/brk" alt="Documentation" />
</a>
<img src="https://img.shields.io/crates/size/brk" alt="Size" />
<a href="https://deps.rs/crate/brk">
<img src="https://deps.rs/crate/brk/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
</a>
<a href="https://bsky.app/profile/bitcoinresearchkit.org">
<img src="https://img.shields.io/badge/bluesky-blue?link=https%3A%2F%2Fbsky.app%2Fprofile%2Fbitcoinresearchkit.org" alt="Bluesky" />
</a>
<a href="https://x.com/brkdotorg">
<img src="https://img.shields.io/badge/x.com-black" alt="X" />
</a>
</p>
<p align="center">
<span>Bitcoin is our only <b><i>hope</i></b> for a better future.</span>
</p>
The Bitcoin Research Kit is a high-performance toolchain designed to parse, index, compute, serve and visualize data from a Bitcoin node, enabling users to gain deeper insights into the Bitcoin network.
## Description
In other words it's an alternative to [Glassnode](https://glassnode.com), [mempool.space](https://mempool.space/) (soon) and [electrs](https://github.com/romanz/electrs) (soon) all in one package with a particular focus on simplicity and ease of use.
**kibō** (*hope* in japanese, formerly Satonomics) is a suite of tools that aims to help you understand Bitcoin's various dynamics. To do that, there is a wide number of charts and datasets with a scale by date but also by height free for you to explore. Which allows you to verify an incredible number of things, from the number of UTXOs to the repartition of the supply between different groups over time, with many things in between and it's all made possible thanks to Bitcoin's transparency. Whether you're an enthusiast, a researcher, a miner, an analyst, a trader, a skeptic or just curious, there is something new to learn for everyone !
The toolkit can be used in various ways to accommodate as many needs as possible:
While it's not the first tool trying to solve this problem, it's the first that is completely free, open-source and self-hostable. Which is very important as, just like for Bitcoin itself, I strongly believe that everyone should have access to this kind of data.
- **[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. \
Just like the website, it's entirely free, with no authentication or rate-limiting.
- **[CLI](https://crates.io/crates/brk_cli)** \
Node runners are strongly encouraged to try out and self-host their own instance using BRK's command line interface. \
The CLI has multiple cogs available for users to tweak to adapt to all situations with even the possibility for web developers to create their own custom website which could later on be added as an alternative front-end.
- **[Crates](https://crates.io/crates/brk)** \
Rust developers have access to a wide range crates, each built upon one another with its own specific purpose, enabling independent use and offering great flexibility.
PRs are welcome, especially if their goal is to introduce additional datasets.
If you are a user of [mempool.space](https://mempool.space), you'll find this to be very complimentary, as it's a global and macro view of the chain over time instead.
The primary goal of this project is to be fully-featured and accessible for everyone, regardless of their background or financial situation - whether that person is an enthusiast, researcher, miner, analyst, or simply curious.
If we want the world to move towards and, in the end, to be on a Bitcoin standard, we must have tools like this at our disposal.
In contrast, existing alternatives tend to be either [very costly](https://studio.glassnode.com/pricing) or missing essential features, with the vast majority being closed-source and unverifiable, which fundamentally undermines the principles of Bitcoin.
## Donations
## Crates
This project was started as an answer to the outrageous pricing from Glassnode (and their third tier starting at $833.33/month !).
- [`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_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_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_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
But it is a lot of work and has been worked on **full-time since November of 2023** and has also been operational since then without any ads.
## Hosting as a service
**At the time of writing (2024-09-12), this project has made around 2,200,000 sats, which is around $1300 or $120/month. While I'm very grateful for all donations, it's sadly unsustainable.**
If you'd like to have your own instance hosted for you please contact [hosting@bitcoinresearchkit.org](mailto:hosting@bitcoinresearchkit.org).
So if you find this project useful, [please send some sats](https://geyser.fund/project/kibo/), it would be really appreciated.
- 2 separate dedicated servers (1 GB/s each) with different ISPs and Cloudflare integration for enhanced performance and optimal availability
- 99.99% SLA
- Configured for speed
- Updates delivered at your convenience
- Direct communication for feature requests and support
- Bitcoin Core or Knots with desired version
- Optional subdomains: `*.bitcoinresearchkit.org`, `*.kibo.money` and `*.satonomics.xyz`
- Logo featured in the Readme if desired
If you're a potential sponsor, feel free to contact me in Nostr !
Pricing: `0.01 BTC / month` *or* `0.1 BTC / year`
[Geyser Fund](https://geyser.fund/project/kibo/)
## Acknowledgments
## Warning
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.
This project is still in an early stage. Until more people look at the code and check the various computations in it, the datasets might be, in the worst case, completely false.
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.
## Instances
## Donate
- [kibo.money](https://kibo.money)
- [backup.kibo.money](https://backup.kibo.money)
[`bc1q09 8zsm89 m7kgyz e338vf ejhpdt 92ua9p 3peuve`](bitcoin:bc1q098zsm89m7kgyze338vfejhpdt92ua9p3peuve)
## Structure
[`lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkxmmww3jkuar8d35kgetj8yuq363hv4`](lightning:lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkxmmww3jkuar8d35kgetj8yuq363hv4)
- `parser`: The backbone of the project, it does most of the work by parsing and then computing datasets from the timechain
- `website`: A web app which displays the generated datasets in various charts
- `server`: A small server which will serve both the website and the computed datasets via an API
## Roadmap
- **More Datasets/Charts**
- **Simulations**
- **Dashboards**
- **Nostr integration**
- **API Documentation**
- **Descriptions**
- **Start9 support**
## Setup
### Requirements
- At least 16 GB of RAM
- 1 TB of free space (will use 60-80% of that)
- A running instance of bitcoin-core with txindex=1 and rpc credentials
- Git
### Docker
Coming soon
### Manual
First we need to install Rust (https://www.rust-lang.org/tools/install)
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
If you already had Rust installed you could update it just in case
```bash
rustup update
```
Optionally, you can also install `cargo-watch` for the server to automatically restart it on file change, which will be triggered by new code and new datasets from the parser (https://github.com/watchexec/cargo-watch?tab=readme-ov-file#install)
```bash
cargo install cargo-watch --locked
```
Then you need to choose a path where all files related to **kibō** will live
```bash
cd ???
```
We can now clone the repository
```bash
git clone https://github.com/kibo-money/kibo.git
```
In a new terminal, go to the `parser`'s folder of the repository
```bash
cd ???/kibo/parser
```
Now we can finally start by running the parser, you need to use the `./run.sh` script instead of `cargo run -r` as we need to set various system variables for the program to run smoothly
For the first launch, the parser will need several information such as:
- `--datadir`: which is bitcoin data directory path
- `--rpcuser`: the username of the RPC credentials to talk to the bitcoin server
- `--rpcpassword`: the password of the RPC credentials
Everything will be saved in a `config.toml` file, which will allow you to simply run `./run.sh` next time
Here's an example
```bash
./run.sh --datadir=$HOME/Developer/bitcoin --rpcuser=satoshi --rpcpassword=nakamoto
```
In a new terminal, go to the `server`'s folder of the repository
```bash
cd ???/kibo/server
```
And start it also with the `run.sh` script instead of `cargo run -r`
```bash
./run.sh
```
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/
## Brand
### Name
kibō means _**hope**_ in japanese which is what Bitcoin ultimately is for many, hope for a better future.
### Logo
The dove (borrowed from [svgrepo](https://www.svgrepo.com/svg/351969/dove) for now) is known to represent hope in many cultures.
The orange background is a wink to Bitcoin and when in a circle, it also represents the sun, which means that while it's our hope for a better future, we still have to be careful with our collective goals and actions, to not end up like Icarus.
## Infrastructure
Here's the current infrastructure of the main instance and its backup.
It uses 2 servers, a full and a light one without the parser running but with still datasets syncronized via Syncthing.
Cloudflare is used for their tunnel + CDN services.
Though it's recommended to change to default **Browser Cache TTL** configuration from `4 Hours` to `Respect Existing Headers` (in `Websites / YOUR_DOMAIN / Caching / Configuration / Browser Cache TTL`) and activate `Always use https`.
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/infrastructure-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/infrastructure-light.svg">
<img alt="kibō" src="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/infrastructure-light.svg" width="768" height="auto" style="max-width: 100%;">
</picture>
</p>
## Iterations
A list of all the previous versions and ideas:
- https://github.com/drgarlic/satonomics
- https://github.com/drgarlic/satonomics-parser
- https://github.com/drgarlic/satonomics-explorer
- https://github.com/drgarlic/satonomics-server
- https://github.com/drgarlic/satonomics-app
- https://github.com/drgarlic/bitalisys
- https://github.com/drgarlic/bitesque-app
- https://github.com/drgarlic/bitesque-back
- https://github.com/drgarlic/bitesque-front
- https://github.com/drgarlic/bitesque-assets
- https://github.com/drgarlic/syf
[Geyser Fund](https://geyser.fund/project/brk)
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because it is too large Load Diff
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path d="M 10.874 7.57 L 10.874 6.802 C 10.104 5.809 9.587 4.634 9.397 3.379 C 9.339 3.009 8.877 2.865 8.637 3.152 C 8.059 3.833 7.605 4.632 7.299 5.517 C 8.234 6.565 9.486 7.284 10.874 7.57 Z M 13.937 4.749 C 12.729 4.749 11.751 5.731 11.751 6.939 L 11.751 8.563 C 8.895 8.394 6.474 6.636 5.379 4.142 C 5.229 3.8 4.746 3.78 4.585 4.117 C 4.131 5.077 3.876 6.149 3.876 7.28 C 3.876 9.217 4.808 11.025 6.203 12.364 C 6.562 12.711 6.915 12.998 7.266 13.261 L 3.331 14.245 C 3.038 14.318 2.907 14.658 3.072 14.912 C 3.547 15.648 4.723 16.895 7.261 16.999 C 7.48 17.007 7.698 16.928 7.864 16.783 L 9.648 15.249 L 11.751 15.249 C 14.167 15.249 16.125 13.293 16.125 10.876 L 16.125 6.499 L 17 4.749 L 13.937 4.749 Z M 13.937 7.391 C 13.697 7.391 13.458 7.175 13.458 6.935 C 13.458 6.695 13.697 6.483 13.937 6.483 C 14.177 6.483 14.399 6.703 14.399 6.943 C 14.399 7.183 14.177 7.391 13.937 7.391 Z" style="fill: #12100f;"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path d="M 10.874 7.57 L 10.874 6.802 C 10.104 5.809 9.587 4.634 9.397 3.379 C 9.339 3.009 8.877 2.865 8.637 3.152 C 8.059 3.833 7.605 4.632 7.299 5.517 C 8.234 6.565 9.486 7.284 10.874 7.57 Z M 13.937 4.749 C 12.729 4.749 11.751 5.731 11.751 6.939 L 11.751 8.563 C 8.895 8.394 6.474 6.636 5.379 4.142 C 5.229 3.8 4.746 3.78 4.585 4.117 C 4.131 5.077 3.876 6.149 3.876 7.28 C 3.876 9.217 4.808 11.025 6.203 12.364 C 6.562 12.711 6.915 12.998 7.266 13.261 L 3.331 14.245 C 3.038 14.318 2.907 14.658 3.072 14.912 C 3.547 15.648 4.723 16.895 7.261 16.999 C 7.48 17.007 7.698 16.928 7.864 16.783 L 9.648 15.249 L 11.751 15.249 C 14.167 15.249 16.125 13.293 16.125 10.876 L 16.125 6.499 L 17 4.749 L 13.937 4.749 Z M 13.937 7.391 C 13.697 7.391 13.458 7.175 13.458 6.935 C 13.458 6.695 13.697 6.483 13.937 6.483 C 14.177 6.483 14.399 6.703 14.399 6.943 C 14.399 7.183 14.177 7.391 13.937 7.391 Z" style="fill: #fffaf6;"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path d="M 10.874 7.57 L 10.874 6.802 C 10.104 5.809 9.587 4.634 9.397 3.379 C 9.339 3.009 8.877 2.865 8.637 3.152 C 8.059 3.833 7.605 4.632 7.299 5.517 C 8.234 6.565 9.486 7.284 10.874 7.57 Z M 13.937 4.749 C 12.729 4.749 11.751 5.731 11.751 6.939 L 11.751 8.563 C 8.895 8.394 6.474 6.636 5.379 4.142 C 5.229 3.8 4.746 3.78 4.585 4.117 C 4.131 5.077 3.876 6.149 3.876 7.28 C 3.876 9.217 4.808 11.025 6.203 12.364 C 6.562 12.711 6.915 12.998 7.266 13.261 L 3.331 14.245 C 3.038 14.318 2.907 14.658 3.072 14.912 C 3.547 15.648 4.723 16.895 7.261 16.999 C 7.48 17.007 7.698 16.928 7.864 16.783 L 9.648 15.249 L 11.751 15.249 C 14.167 15.249 16.125 13.293 16.125 10.876 L 16.125 6.499 L 17 4.749 L 13.937 4.749 Z M 13.937 7.391 C 13.697 7.391 13.458 7.175 13.458 6.935 C 13.458 6.695 13.697 6.483 13.937 6.483 C 14.177 6.483 14.399 6.703 14.399 6.943 C 14.399 7.183 14.177 7.391 13.937 7.391 Z" style="fill: #f26610;"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

-11
View File
@@ -1,11 +0,0 @@
<svg viewBox="0 0 720 180" xmlns="http://www.w3.org/2000/svg">
<defs></defs>
<g transform="matrix(7.5, 0, 0, 7.5, -2046.71228, -1592.744873)">
<ellipse style="fill: #f26610;" cx="284.895" cy="224.366" rx="12" ry="12"></ellipse>
<path d="M 285.769 221.936 L 285.769 221.168 C 284.999 220.175 284.482 219 284.292 217.745 C 284.234 217.375 283.772 217.231 283.532 217.518 C 282.954 218.199 282.5 218.998 282.194 219.883 C 283.129 220.931 284.381 221.65 285.769 221.936 Z M 288.832 219.115 C 287.624 219.115 286.646 220.097 286.646 221.305 L 286.646 222.929 C 283.79 222.76 281.369 221.002 280.274 218.508 C 280.124 218.166 279.641 218.146 279.48 218.483 C 279.026 219.443 278.771 220.515 278.771 221.646 C 278.771 223.583 279.703 225.391 281.098 226.73 C 281.457 227.077 281.81 227.364 282.161 227.627 L 278.226 228.611 C 277.933 228.684 277.802 229.024 277.967 229.278 C 278.442 230.014 279.618 231.261 282.156 231.365 C 282.375 231.373 282.593 231.294 282.759 231.149 L 284.543 229.615 L 286.646 229.615 C 289.062 229.615 291.02 227.659 291.02 225.242 L 291.02 220.865 L 291.895 219.115 L 288.832 219.115 Z M 288.832 221.757 C 288.592 221.757 288.353 221.541 288.353 221.301 C 288.353 221.061 288.592 220.849 288.832 220.849 C 289.072 220.849 289.294 221.069 289.294 221.309 C 289.294 221.549 289.072 221.757 288.832 221.757 Z" style="fill: #fffaf6;"></path>
</g>
<g transform="matrix(1, 0, 0, 1, -30, 0)">
<path d="M 278.049 146.789 L 278.049 127.527 L 287.141 117.972 L 304.4 146.789 L 331.83 146.789 L 303.784 100.251 L 332.755 69.739 L 303.013 69.739 L 278.049 97.477 L 278.049 30.598 L 254.318 30.598 L 254.318 146.789 L 278.049 146.789 Z M 354.169 57.719 C 361.565 57.719 367.575 51.709 367.575 44.158 C 367.575 36.608 361.565 30.752 354.169 30.752 C 346.618 30.752 340.608 36.608 340.608 44.158 C 340.608 51.709 346.618 57.719 354.169 57.719 Z M 342.457 146.789 L 366.188 146.789 L 366.188 69.739 L 342.457 69.739 L 342.457 146.789 Z M 406.407 146.789 L 407.64 136.927 C 411.801 144.015 421.047 148.792 431.834 148.792 C 453.716 148.792 468.972 132.92 468.972 109.035 C 468.972 83.916 455.257 67.119 433.683 67.119 C 422.588 67.119 412.417 71.742 407.794 78.677 L 407.794 30.598 L 384.063 30.598 L 384.063 146.789 L 406.407 146.789 Z M 407.948 107.802 C 407.948 96.244 415.653 88.539 426.749 88.539 C 437.998 88.539 445.087 96.398 445.087 107.802 C 445.087 119.205 437.998 127.064 426.749 127.064 C 415.653 127.064 407.948 119.359 407.948 107.802 Z M 498.713 56.332 L 543.402 56.332 L 543.402 40.306 L 498.713 40.306 L 498.713 56.332 Z M 478.526 108.11 C 478.526 132.458 496.402 148.638 521.058 148.638 C 545.56 148.638 563.435 132.458 563.435 108.11 C 563.435 83.762 545.56 67.428 521.058 67.428 C 496.402 67.428 478.526 83.762 478.526 108.11 Z M 502.412 107.956 C 502.412 96.398 509.963 88.693 521.058 88.693 C 531.999 88.693 539.55 96.398 539.55 107.956 C 539.55 119.667 531.999 127.372 521.058 127.372 C 509.963 127.372 502.412 119.667 502.412 107.956 Z" style="fill: #fffaf6;"></path>
<path d="M 589.19 97.802 L 589.19 106.23 L 610.948 106.23 C 605.1 112.938 597.446 119.044 587.986 124.376 L 593.404 131.514 C 597.532 128.934 601.488 126.268 605.186 123.43 L 605.186 146.048 L 614.13 146.048 L 614.13 123.43 L 626.944 123.43 L 626.944 149.402 L 635.974 149.402 L 635.974 123.43 L 649.82 123.43 L 649.82 134.008 C 649.82 136.072 649.046 137.104 647.498 137.104 L 640.36 136.674 L 642.768 145.188 L 650.422 145.188 C 655.926 145.188 658.678 142.092 658.678 135.986 L 658.678 115.174 L 635.974 115.174 L 635.974 108.638 L 626.944 108.638 L 626.944 115.174 L 614.388 115.174 C 617.054 112.336 619.548 109.326 621.784 106.23 L 665.128 106.23 L 665.128 97.802 L 626.858 97.802 C 627.89 95.824 628.836 93.76 629.696 91.61 L 620.838 90.492 C 619.806 92.9 618.516 95.394 617.14 97.802 L 589.19 97.802 Z M 648.1 68.734 C 642.338 72.088 636.232 75.098 629.868 77.678 C 621.612 75.012 612.926 72.518 603.896 70.282 L 599.252 77.248 C 605.272 78.624 611.206 80.258 617.226 82.15 C 610.088 84.386 602.606 86.106 594.78 87.482 L 599.596 95.308 C 612.324 92.04 622.472 89.116 630.04 86.364 C 638.124 89.116 646.122 92.298 654.034 95.824 L 658.936 88.428 C 653.26 86.02 647.412 83.698 641.392 81.548 C 646.208 79.226 651.11 76.56 655.926 73.55 L 648.1 68.734 Z M 675.438 77.85 L 675.438 85.848 L 682.404 85.848 L 682.404 98.92 C 682.404 101.5 681.114 103.22 678.62 104.166 L 680.684 110.874 C 692.036 108.896 701.926 106.66 710.182 104.08 L 708.634 96.426 C 703.474 98.146 697.454 99.608 690.574 100.984 L 690.574 85.848 L 712.332 85.848 L 712.332 77.85 L 698.916 77.85 C 698.4 74.668 697.884 71.744 697.368 69.164 L 688.338 70.712 C 688.94 72.862 689.542 75.27 690.144 77.85 L 675.438 77.85 Z M 724.028 89.632 L 739.25 89.632 L 739.25 93.502 L 723.856 93.502 C 723.942 92.47 724.028 91.352 724.028 90.32 L 724.028 89.632 Z M 739.25 83.096 L 724.028 83.096 L 724.028 79.226 L 739.25 79.226 L 739.25 83.096 Z M 722.652 100.038 L 739.25 100.038 L 739.25 100.898 C 739.25 103.048 738.218 104.166 736.24 104.166 C 733.918 104.166 731.424 103.994 728.758 103.822 L 730.822 111.562 L 738.734 111.562 C 744.582 111.562 747.506 108.982 747.506 103.908 L 747.506 72.002 L 715.6 72.002 L 715.6 90.922 C 715.428 97.286 713.192 102.532 708.892 106.746 L 715.342 112.594 C 718.782 109.068 721.276 104.854 722.652 100.038 Z M 708.462 121.452 L 708.462 126.784 L 683.608 126.784 L 683.608 134.352 L 708.462 134.352 L 708.462 139.598 L 675.524 139.598 L 675.524 147.51 L 750 147.51 L 750 139.598 L 717.062 139.598 L 717.062 134.352 L 742.174 134.352 L 742.174 126.784 L 717.062 126.784 L 717.062 121.452 L 746.216 121.452 L 746.216 113.712 L 679.308 113.712 L 679.308 121.452 L 708.462 121.452 Z" style="fill: #68625f"></path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

-11
View File
@@ -1,11 +0,0 @@
<svg viewBox="0 0 720 180" xmlns="http://www.w3.org/2000/svg">
<defs></defs>
<g transform="matrix(7.5, 0, 0, 7.5, -2046.71228, -1592.744873)">
<ellipse style="fill: #f26610;" cx="284.895" cy="224.366" rx="12" ry="12"></ellipse>
<path d="M 285.769 221.936 L 285.769 221.168 C 284.999 220.175 284.482 219 284.292 217.745 C 284.234 217.375 283.772 217.231 283.532 217.518 C 282.954 218.199 282.5 218.998 282.194 219.883 C 283.129 220.931 284.381 221.65 285.769 221.936 Z M 288.832 219.115 C 287.624 219.115 286.646 220.097 286.646 221.305 L 286.646 222.929 C 283.79 222.76 281.369 221.002 280.274 218.508 C 280.124 218.166 279.641 218.146 279.48 218.483 C 279.026 219.443 278.771 220.515 278.771 221.646 C 278.771 223.583 279.703 225.391 281.098 226.73 C 281.457 227.077 281.81 227.364 282.161 227.627 L 278.226 228.611 C 277.933 228.684 277.802 229.024 277.967 229.278 C 278.442 230.014 279.618 231.261 282.156 231.365 C 282.375 231.373 282.593 231.294 282.759 231.149 L 284.543 229.615 L 286.646 229.615 C 289.062 229.615 291.02 227.659 291.02 225.242 L 291.02 220.865 L 291.895 219.115 L 288.832 219.115 Z M 288.832 221.757 C 288.592 221.757 288.353 221.541 288.353 221.301 C 288.353 221.061 288.592 220.849 288.832 220.849 C 289.072 220.849 289.294 221.069 289.294 221.309 C 289.294 221.549 289.072 221.757 288.832 221.757 Z" style="fill: #fffaf6;"></path>
</g>
<g transform="matrix(1, 0, 0, 1, -30, 0)">
<path d="M 278.049 146.789 L 278.049 127.527 L 287.141 117.972 L 304.4 146.789 L 331.83 146.789 L 303.784 100.251 L 332.755 69.739 L 303.013 69.739 L 278.049 97.477 L 278.049 30.598 L 254.318 30.598 L 254.318 146.789 L 278.049 146.789 Z M 354.169 57.719 C 361.565 57.719 367.575 51.709 367.575 44.158 C 367.575 36.608 361.565 30.752 354.169 30.752 C 346.618 30.752 340.608 36.608 340.608 44.158 C 340.608 51.709 346.618 57.719 354.169 57.719 Z M 342.457 146.789 L 366.188 146.789 L 366.188 69.739 L 342.457 69.739 L 342.457 146.789 Z M 406.407 146.789 L 407.64 136.927 C 411.801 144.015 421.047 148.792 431.834 148.792 C 453.716 148.792 468.972 132.92 468.972 109.035 C 468.972 83.916 455.257 67.119 433.683 67.119 C 422.588 67.119 412.417 71.742 407.794 78.677 L 407.794 30.598 L 384.063 30.598 L 384.063 146.789 L 406.407 146.789 Z M 407.948 107.802 C 407.948 96.244 415.653 88.539 426.749 88.539 C 437.998 88.539 445.087 96.398 445.087 107.802 C 445.087 119.205 437.998 127.064 426.749 127.064 C 415.653 127.064 407.948 119.359 407.948 107.802 Z M 498.713 56.332 L 543.402 56.332 L 543.402 40.306 L 498.713 40.306 L 498.713 56.332 Z M 478.526 108.11 C 478.526 132.458 496.402 148.638 521.058 148.638 C 545.56 148.638 563.435 132.458 563.435 108.11 C 563.435 83.762 545.56 67.428 521.058 67.428 C 496.402 67.428 478.526 83.762 478.526 108.11 Z M 502.412 107.956 C 502.412 96.398 509.963 88.693 521.058 88.693 C 531.999 88.693 539.55 96.398 539.55 107.956 C 539.55 119.667 531.999 127.372 521.058 127.372 C 509.963 127.372 502.412 119.667 502.412 107.956 Z" style="fill: #12100f;"></path>
<path d="M 589.19 97.802 L 589.19 106.23 L 610.948 106.23 C 605.1 112.938 597.446 119.044 587.986 124.376 L 593.404 131.514 C 597.532 128.934 601.488 126.268 605.186 123.43 L 605.186 146.048 L 614.13 146.048 L 614.13 123.43 L 626.944 123.43 L 626.944 149.402 L 635.974 149.402 L 635.974 123.43 L 649.82 123.43 L 649.82 134.008 C 649.82 136.072 649.046 137.104 647.498 137.104 L 640.36 136.674 L 642.768 145.188 L 650.422 145.188 C 655.926 145.188 658.678 142.092 658.678 135.986 L 658.678 115.174 L 635.974 115.174 L 635.974 108.638 L 626.944 108.638 L 626.944 115.174 L 614.388 115.174 C 617.054 112.336 619.548 109.326 621.784 106.23 L 665.128 106.23 L 665.128 97.802 L 626.858 97.802 C 627.89 95.824 628.836 93.76 629.696 91.61 L 620.838 90.492 C 619.806 92.9 618.516 95.394 617.14 97.802 L 589.19 97.802 Z M 648.1 68.734 C 642.338 72.088 636.232 75.098 629.868 77.678 C 621.612 75.012 612.926 72.518 603.896 70.282 L 599.252 77.248 C 605.272 78.624 611.206 80.258 617.226 82.15 C 610.088 84.386 602.606 86.106 594.78 87.482 L 599.596 95.308 C 612.324 92.04 622.472 89.116 630.04 86.364 C 638.124 89.116 646.122 92.298 654.034 95.824 L 658.936 88.428 C 653.26 86.02 647.412 83.698 641.392 81.548 C 646.208 79.226 651.11 76.56 655.926 73.55 L 648.1 68.734 Z M 675.438 77.85 L 675.438 85.848 L 682.404 85.848 L 682.404 98.92 C 682.404 101.5 681.114 103.22 678.62 104.166 L 680.684 110.874 C 692.036 108.896 701.926 106.66 710.182 104.08 L 708.634 96.426 C 703.474 98.146 697.454 99.608 690.574 100.984 L 690.574 85.848 L 712.332 85.848 L 712.332 77.85 L 698.916 77.85 C 698.4 74.668 697.884 71.744 697.368 69.164 L 688.338 70.712 C 688.94 72.862 689.542 75.27 690.144 77.85 L 675.438 77.85 Z M 724.028 89.632 L 739.25 89.632 L 739.25 93.502 L 723.856 93.502 C 723.942 92.47 724.028 91.352 724.028 90.32 L 724.028 89.632 Z M 739.25 83.096 L 724.028 83.096 L 724.028 79.226 L 739.25 79.226 L 739.25 83.096 Z M 722.652 100.038 L 739.25 100.038 L 739.25 100.898 C 739.25 103.048 738.218 104.166 736.24 104.166 C 733.918 104.166 731.424 103.994 728.758 103.822 L 730.822 111.562 L 738.734 111.562 C 744.582 111.562 747.506 108.982 747.506 103.908 L 747.506 72.002 L 715.6 72.002 L 715.6 90.922 C 715.428 97.286 713.192 102.532 708.892 106.746 L 715.342 112.594 C 718.782 109.068 721.276 104.854 722.652 100.038 Z M 708.462 121.452 L 708.462 126.784 L 683.608 126.784 L 683.608 134.352 L 708.462 134.352 L 708.462 139.598 L 675.524 139.598 L 675.524 147.51 L 750 147.51 L 750 139.598 L 717.062 139.598 L 717.062 134.352 L 742.174 134.352 L 742.174 126.784 L 717.062 126.784 L 717.062 121.452 L 746.216 121.452 L 746.216 113.712 L 679.308 113.712 L 679.308 121.452 L 708.462 121.452 Z" style="fill: #b4aca9;"></path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

-5
View File
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<ellipse style="fill: #f26610;" cx="12" cy="12" rx="12" ry="12"/>
<path d="M 12.874 9.57 L 12.874 8.802 C 12.104 7.809 11.587 6.634 11.397 5.379 C 11.339 5.009 10.877 4.865 10.637 5.152 C 10.059 5.833 9.605 6.632 9.299 7.517 C 10.234 8.565 11.486 9.284 12.874 9.57 Z M 15.937 6.749 C 14.729 6.749 13.751 7.731 13.751 8.939 L 13.751 10.563 C 10.895 10.394 8.474 8.636 7.379 6.142 C 7.229 5.8 6.746 5.78 6.585 6.117 C 6.131 7.077 5.876 8.149 5.876 9.28 C 5.876 11.217 6.808 13.025 8.203 14.364 C 8.562 14.711 8.915 14.998 9.266 15.261 L 5.331 16.245 C 5.038 16.318 4.907 16.658 5.072 16.912 C 5.547 17.648 6.723 18.895 9.261 18.999 C 9.48 19.007 9.698 18.928 9.864 18.783 L 11.648 17.249 L 13.751 17.249 C 16.167 17.249 18.125 15.293 18.125 12.876 L 18.125 8.499 L 19 6.749 L 15.937 6.749 Z M 15.937 9.391 C 15.697 9.391 15.458 9.175 15.458 8.935 C 15.458 8.695 15.697 8.483 15.937 8.483 C 16.177 8.483 16.399 8.703 16.399 8.943 C 16.399 9.183 16.177 9.391 15.937 9.391 Z" style="fill: #fffaf6;"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 500 180" xmlns="http://www.w3.org/2000/svg">
<defs/>
<g transform="matrix(1, 0, 0, 1, -252.158997, 0)">
<path d="M 278.049 146.789 L 278.049 127.527 L 287.141 117.972 L 304.4 146.789 L 331.83 146.789 L 303.784 100.251 L 332.755 69.739 L 303.013 69.739 L 278.049 97.477 L 278.049 30.598 L 254.318 30.598 L 254.318 146.789 L 278.049 146.789 Z M 354.169 57.719 C 361.565 57.719 367.575 51.709 367.575 44.158 C 367.575 36.608 361.565 30.752 354.169 30.752 C 346.618 30.752 340.608 36.608 340.608 44.158 C 340.608 51.709 346.618 57.719 354.169 57.719 Z M 342.457 146.789 L 366.188 146.789 L 366.188 69.739 L 342.457 69.739 L 342.457 146.789 Z M 406.407 146.789 L 407.64 136.927 C 411.801 144.015 421.047 148.792 431.834 148.792 C 453.716 148.792 468.972 132.92 468.972 109.035 C 468.972 83.916 455.257 67.119 433.683 67.119 C 422.588 67.119 412.417 71.742 407.794 78.677 L 407.794 30.598 L 384.063 30.598 L 384.063 146.789 L 406.407 146.789 Z M 407.948 107.802 C 407.948 96.244 415.653 88.539 426.749 88.539 C 437.998 88.539 445.087 96.398 445.087 107.802 C 445.087 119.205 437.998 127.064 426.749 127.064 C 415.653 127.064 407.948 119.359 407.948 107.802 Z M 498.713 56.332 L 543.402 56.332 L 543.402 40.306 L 498.713 40.306 L 498.713 56.332 Z M 478.526 108.11 C 478.526 132.458 496.402 148.638 521.058 148.638 C 545.56 148.638 563.435 132.458 563.435 108.11 C 563.435 83.762 545.56 67.428 521.058 67.428 C 496.402 67.428 478.526 83.762 478.526 108.11 Z M 502.412 107.956 C 502.412 96.398 509.963 88.693 521.058 88.693 C 531.999 88.693 539.55 96.398 539.55 107.956 C 539.55 119.667 531.999 127.372 521.058 127.372 C 509.963 127.372 502.412 119.667 502.412 107.956 Z" style="fill: rgb(16, 16, 14);"/>
<path d="M 589.19 97.802 L 589.19 106.23 L 610.948 106.23 C 605.1 112.938 597.446 119.044 587.986 124.376 L 593.404 131.514 C 597.532 128.934 601.488 126.268 605.186 123.43 L 605.186 146.048 L 614.13 146.048 L 614.13 123.43 L 626.944 123.43 L 626.944 149.402 L 635.974 149.402 L 635.974 123.43 L 649.82 123.43 L 649.82 134.008 C 649.82 136.072 649.046 137.104 647.498 137.104 L 640.36 136.674 L 642.768 145.188 L 650.422 145.188 C 655.926 145.188 658.678 142.092 658.678 135.986 L 658.678 115.174 L 635.974 115.174 L 635.974 108.638 L 626.944 108.638 L 626.944 115.174 L 614.388 115.174 C 617.054 112.336 619.548 109.326 621.784 106.23 L 665.128 106.23 L 665.128 97.802 L 626.858 97.802 C 627.89 95.824 628.836 93.76 629.696 91.61 L 620.838 90.492 C 619.806 92.9 618.516 95.394 617.14 97.802 L 589.19 97.802 Z M 648.1 68.734 C 642.338 72.088 636.232 75.098 629.868 77.678 C 621.612 75.012 612.926 72.518 603.896 70.282 L 599.252 77.248 C 605.272 78.624 611.206 80.258 617.226 82.15 C 610.088 84.386 602.606 86.106 594.78 87.482 L 599.596 95.308 C 612.324 92.04 622.472 89.116 630.04 86.364 C 638.124 89.116 646.122 92.298 654.034 95.824 L 658.936 88.428 C 653.26 86.02 647.412 83.698 641.392 81.548 C 646.208 79.226 651.11 76.56 655.926 73.55 L 648.1 68.734 Z M 675.438 77.85 L 675.438 85.848 L 682.404 85.848 L 682.404 98.92 C 682.404 101.5 681.114 103.22 678.62 104.166 L 680.684 110.874 C 692.036 108.896 701.926 106.66 710.182 104.08 L 708.634 96.426 C 703.474 98.146 697.454 99.608 690.574 100.984 L 690.574 85.848 L 712.332 85.848 L 712.332 77.85 L 698.916 77.85 C 698.4 74.668 697.884 71.744 697.368 69.164 L 688.338 70.712 C 688.94 72.862 689.542 75.27 690.144 77.85 L 675.438 77.85 Z M 724.028 89.632 L 739.25 89.632 L 739.25 93.502 L 723.856 93.502 C 723.942 92.47 724.028 91.352 724.028 90.32 L 724.028 89.632 Z M 739.25 83.096 L 724.028 83.096 L 724.028 79.226 L 739.25 79.226 L 739.25 83.096 Z M 722.652 100.038 L 739.25 100.038 L 739.25 100.898 C 739.25 103.048 738.218 104.166 736.24 104.166 C 733.918 104.166 731.424 103.994 728.758 103.822 L 730.822 111.562 L 738.734 111.562 C 744.582 111.562 747.506 108.982 747.506 103.908 L 747.506 72.002 L 715.6 72.002 L 715.6 90.922 C 715.428 97.286 713.192 102.532 708.892 106.746 L 715.342 112.594 C 718.782 109.068 721.276 104.854 722.652 100.038 Z M 708.462 121.452 L 708.462 126.784 L 683.608 126.784 L 683.608 134.352 L 708.462 134.352 L 708.462 139.598 L 675.524 139.598 L 675.524 147.51 L 750 147.51 L 750 139.598 L 717.062 139.598 L 717.062 134.352 L 742.174 134.352 L 742.174 126.784 L 717.062 126.784 L 717.062 121.452 L 746.216 121.452 L 746.216 113.712 L 679.308 113.712 L 679.308 121.452 L 708.462 121.452 Z" style="fill: rgb(192, 192, 171);"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 500 180" xmlns="http://www.w3.org/2000/svg">
<defs/>
<g transform="matrix(1, 0, 0, 1, -252.158997, 0)">
<path d="M 278.049 146.789 L 278.049 127.527 L 287.141 117.972 L 304.4 146.789 L 331.83 146.789 L 303.784 100.251 L 332.755 69.739 L 303.013 69.739 L 278.049 97.477 L 278.049 30.598 L 254.318 30.598 L 254.318 146.789 L 278.049 146.789 Z M 354.169 57.719 C 361.565 57.719 367.575 51.709 367.575 44.158 C 367.575 36.608 361.565 30.752 354.169 30.752 C 346.618 30.752 340.608 36.608 340.608 44.158 C 340.608 51.709 346.618 57.719 354.169 57.719 Z M 342.457 146.789 L 366.188 146.789 L 366.188 69.739 L 342.457 69.739 L 342.457 146.789 Z M 406.407 146.789 L 407.64 136.927 C 411.801 144.015 421.047 148.792 431.834 148.792 C 453.716 148.792 468.972 132.92 468.972 109.035 C 468.972 83.916 455.257 67.119 433.683 67.119 C 422.588 67.119 412.417 71.742 407.794 78.677 L 407.794 30.598 L 384.063 30.598 L 384.063 146.789 L 406.407 146.789 Z M 407.948 107.802 C 407.948 96.244 415.653 88.539 426.749 88.539 C 437.998 88.539 445.087 96.398 445.087 107.802 C 445.087 119.205 437.998 127.064 426.749 127.064 C 415.653 127.064 407.948 119.359 407.948 107.802 Z M 498.713 56.332 L 543.402 56.332 L 543.402 40.306 L 498.713 40.306 L 498.713 56.332 Z M 478.526 108.11 C 478.526 132.458 496.402 148.638 521.058 148.638 C 545.56 148.638 563.435 132.458 563.435 108.11 C 563.435 83.762 545.56 67.428 521.058 67.428 C 496.402 67.428 478.526 83.762 478.526 108.11 Z M 502.412 107.956 C 502.412 96.398 509.963 88.693 521.058 88.693 C 531.999 88.693 539.55 96.398 539.55 107.956 C 539.55 119.667 531.999 127.372 521.058 127.372 C 509.963 127.372 502.412 119.667 502.412 107.956 Z" style="fill: rgb(16, 16, 14);"/>
<path d="M 589.19 97.802 L 589.19 106.23 L 610.948 106.23 C 605.1 112.938 597.446 119.044 587.986 124.376 L 593.404 131.514 C 597.532 128.934 601.488 126.268 605.186 123.43 L 605.186 146.048 L 614.13 146.048 L 614.13 123.43 L 626.944 123.43 L 626.944 149.402 L 635.974 149.402 L 635.974 123.43 L 649.82 123.43 L 649.82 134.008 C 649.82 136.072 649.046 137.104 647.498 137.104 L 640.36 136.674 L 642.768 145.188 L 650.422 145.188 C 655.926 145.188 658.678 142.092 658.678 135.986 L 658.678 115.174 L 635.974 115.174 L 635.974 108.638 L 626.944 108.638 L 626.944 115.174 L 614.388 115.174 C 617.054 112.336 619.548 109.326 621.784 106.23 L 665.128 106.23 L 665.128 97.802 L 626.858 97.802 C 627.89 95.824 628.836 93.76 629.696 91.61 L 620.838 90.492 C 619.806 92.9 618.516 95.394 617.14 97.802 L 589.19 97.802 Z M 648.1 68.734 C 642.338 72.088 636.232 75.098 629.868 77.678 C 621.612 75.012 612.926 72.518 603.896 70.282 L 599.252 77.248 C 605.272 78.624 611.206 80.258 617.226 82.15 C 610.088 84.386 602.606 86.106 594.78 87.482 L 599.596 95.308 C 612.324 92.04 622.472 89.116 630.04 86.364 C 638.124 89.116 646.122 92.298 654.034 95.824 L 658.936 88.428 C 653.26 86.02 647.412 83.698 641.392 81.548 C 646.208 79.226 651.11 76.56 655.926 73.55 L 648.1 68.734 Z M 675.438 77.85 L 675.438 85.848 L 682.404 85.848 L 682.404 98.92 C 682.404 101.5 681.114 103.22 678.62 104.166 L 680.684 110.874 C 692.036 108.896 701.926 106.66 710.182 104.08 L 708.634 96.426 C 703.474 98.146 697.454 99.608 690.574 100.984 L 690.574 85.848 L 712.332 85.848 L 712.332 77.85 L 698.916 77.85 C 698.4 74.668 697.884 71.744 697.368 69.164 L 688.338 70.712 C 688.94 72.862 689.542 75.27 690.144 77.85 L 675.438 77.85 Z M 724.028 89.632 L 739.25 89.632 L 739.25 93.502 L 723.856 93.502 C 723.942 92.47 724.028 91.352 724.028 90.32 L 724.028 89.632 Z M 739.25 83.096 L 724.028 83.096 L 724.028 79.226 L 739.25 79.226 L 739.25 83.096 Z M 722.652 100.038 L 739.25 100.038 L 739.25 100.898 C 739.25 103.048 738.218 104.166 736.24 104.166 C 733.918 104.166 731.424 103.994 728.758 103.822 L 730.822 111.562 L 738.734 111.562 C 744.582 111.562 747.506 108.982 747.506 103.908 L 747.506 72.002 L 715.6 72.002 L 715.6 90.922 C 715.428 97.286 713.192 102.532 708.892 106.746 L 715.342 112.594 C 718.782 109.068 721.276 104.854 722.652 100.038 Z M 708.462 121.452 L 708.462 126.784 L 683.608 126.784 L 683.608 134.352 L 708.462 134.352 L 708.462 139.598 L 675.524 139.598 L 675.524 147.51 L 750 147.51 L 750 139.598 L 717.062 139.598 L 717.062 134.352 L 742.174 134.352 L 742.174 126.784 L 717.062 126.784 L 717.062 121.452 L 746.216 121.452 L 746.216 113.712 L 679.308 113.712 L 679.308 121.452 L 708.462 121.452 Z" style="fill: rgb(192, 192, 171);"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 310 180" xmlns="http://www.w3.org/2000/svg">
<defs/>
<g transform="matrix(1, 0, 0, 1, -253.876495, 0)">
<path d="M 278.049 146.789 L 278.049 127.527 L 287.141 117.972 L 304.4 146.789 L 331.83 146.789 L 303.784 100.251 L 332.755 69.739 L 303.013 69.739 L 278.049 97.477 L 278.049 30.598 L 254.318 30.598 L 254.318 146.789 L 278.049 146.789 Z M 354.169 57.719 C 361.565 57.719 367.575 51.709 367.575 44.158 C 367.575 36.608 361.565 30.752 354.169 30.752 C 346.618 30.752 340.608 36.608 340.608 44.158 C 340.608 51.709 346.618 57.719 354.169 57.719 Z M 342.457 146.789 L 366.188 146.789 L 366.188 69.739 L 342.457 69.739 L 342.457 146.789 Z M 406.407 146.789 L 407.64 136.927 C 411.801 144.015 421.047 148.792 431.834 148.792 C 453.716 148.792 468.972 132.92 468.972 109.035 C 468.972 83.916 455.257 67.119 433.683 67.119 C 422.588 67.119 412.417 71.742 407.794 78.677 L 407.794 30.598 L 384.063 30.598 L 384.063 146.789 L 406.407 146.789 Z M 407.948 107.802 C 407.948 96.244 415.653 88.539 426.749 88.539 C 437.998 88.539 445.087 96.398 445.087 107.802 C 445.087 119.205 437.998 127.064 426.749 127.064 C 415.653 127.064 407.948 119.359 407.948 107.802 Z M 498.713 56.332 L 543.402 56.332 L 543.402 40.306 L 498.713 40.306 L 498.713 56.332 Z M 478.526 108.11 C 478.526 132.458 496.402 148.638 521.058 148.638 C 545.56 148.638 563.435 132.458 563.435 108.11 C 563.435 83.762 545.56 67.428 521.058 67.428 C 496.402 67.428 478.526 83.762 478.526 108.11 Z M 502.412 107.956 C 502.412 96.398 509.963 88.693 521.058 88.693 C 531.999 88.693 539.55 96.398 539.55 107.956 C 539.55 119.667 531.999 127.372 521.058 127.372 C 509.963 127.372 502.412 119.667 502.412 107.956 Z" style="fill: rgb(16, 16, 14);"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 310 180" xmlns="http://www.w3.org/2000/svg">
<defs/>
<g transform="matrix(1, 0, 0, 1, -253.876495, 0)">
<path d="M 278.049 146.789 L 278.049 127.527 L 287.141 117.972 L 304.4 146.789 L 331.83 146.789 L 303.784 100.251 L 332.755 69.739 L 303.013 69.739 L 278.049 97.477 L 278.049 30.598 L 254.318 30.598 L 254.318 146.789 L 278.049 146.789 Z M 354.169 57.719 C 361.565 57.719 367.575 51.709 367.575 44.158 C 367.575 36.608 361.565 30.752 354.169 30.752 C 346.618 30.752 340.608 36.608 340.608 44.158 C 340.608 51.709 346.618 57.719 354.169 57.719 Z M 342.457 146.789 L 366.188 146.789 L 366.188 69.739 L 342.457 69.739 L 342.457 146.789 Z M 406.407 146.789 L 407.64 136.927 C 411.801 144.015 421.047 148.792 431.834 148.792 C 453.716 148.792 468.972 132.92 468.972 109.035 C 468.972 83.916 455.257 67.119 433.683 67.119 C 422.588 67.119 412.417 71.742 407.794 78.677 L 407.794 30.598 L 384.063 30.598 L 384.063 146.789 L 406.407 146.789 Z M 407.948 107.802 C 407.948 96.244 415.653 88.539 426.749 88.539 C 437.998 88.539 445.087 96.398 445.087 107.802 C 445.087 119.205 437.998 127.064 426.749 127.064 C 415.653 127.064 407.948 119.359 407.948 107.802 Z M 498.713 56.332 L 543.402 56.332 L 543.402 40.306 L 498.713 40.306 L 498.713 56.332 Z M 478.526 108.11 C 478.526 132.458 496.402 148.638 521.058 148.638 C 545.56 148.638 563.435 132.458 563.435 108.11 C 563.435 83.762 545.56 67.428 521.058 67.428 C 496.402 67.428 478.526 83.762 478.526 108.11 Z M 502.412 107.956 C 502.412 96.398 509.963 88.693 521.058 88.693 C 531.999 88.693 539.55 96.398 539.55 107.956 C 539.55 119.667 531.999 127.372 521.058 127.372 C 509.963 127.372 502.412 119.667 502.412 107.956 Z" style="fill: rgb(16, 16, 14);"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 453 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

+55
View File
@@ -0,0 +1,55 @@
[package]
name = "brk"
description.workspace = true
license.workspace = true
readme.workspace = true
homepage.workspace = true
repository.workspace = true
edition.workspace = true
version.workspace = true
[features]
full = [
"core",
"computer",
"exit",
"fetcher",
"indexer",
"logger",
"parser",
"query",
"server",
"state",
"store",
"vec",
]
core = ["brk_core"]
computer = ["brk_computer"]
exit = ["brk_exit"]
fetcher = ["brk_fetcher"]
indexer = ["brk_indexer"]
logger = ["brk_logger"]
parser = ["brk_parser"]
query = ["brk_query"]
server = ["brk_server"]
state = ["brk_state"]
store = ["brk_store"]
vec = ["brk_vec"]
[dependencies]
brk_cli = { workspace = true }
brk_core = { workspace = true, optional = true }
brk_computer = { workspace = true, optional = true }
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_parser = { workspace = true, optional = true }
brk_query = { 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 }
[package.metadata.docs.rs]
all-features = true
+49
View File
@@ -0,0 +1,49 @@
#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
#[cfg(feature = "core")]
#[doc(inline)]
pub use brk_core as core;
#[cfg(feature = "computer")]
#[doc(inline)]
pub use brk_computer as computer;
#[cfg(feature = "exit")]
#[doc(inline)]
pub use brk_exit as exit;
#[cfg(feature = "fetcher")]
#[doc(inline)]
pub use brk_fetcher as fetcher;
#[cfg(feature = "indexer")]
#[doc(inline)]
pub use brk_indexer as indexer;
#[cfg(feature = "logger")]
#[doc(inline)]
pub use brk_logger as logger;
#[cfg(feature = "parser")]
#[doc(inline)]
pub use brk_parser as parser;
#[cfg(feature = "query")]
#[doc(inline)]
pub use brk_query as query;
#[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")]
#[doc(inline)]
pub use brk_vec as vec;
+1
View File
@@ -0,0 +1 @@
use brk_cli::main;
+36
View File
@@ -0,0 +1,36 @@
[package]
name = "brk_cli"
description = "A command line interface to interact with the full Bitcoin Research Kit"
version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
[dependencies]
bitcoincore-rpc = { workspace = true }
brk_computer = { workspace = true }
brk_core = { workspace = true }
brk_exit = { workspace = true }
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 }
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"
[[bin]]
name = "brk"
path = "src/main.rs"
[package.metadata.dist]
dist = false
+91
View File
@@ -0,0 +1,91 @@
# BRK Cli
<p align="left">
<a href="https://github.com/bitcoinresearchkit/brk">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/bitcoinresearchkit/brk?style=social">
</a>
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
<img src="https://img.shields.io/crates/l/brk" alt="License" />
</a>
<a href="https://crates.io/crates/brk_cli">
<img src="https://img.shields.io/crates/v/brk_cli" alt="Version" />
</a>
<a href="https://docs.rs/brk_cli">
<img src="https://img.shields.io/docsrs/brk_cli" alt="Documentation" />
</a>
<img src="https://img.shields.io/crates/size/brk_cli" alt="Size" />
<a href="https://deps.rs/crate/brk_cli">
<img src="https://deps.rs/crate/brk_cli/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
</a>
<a href="https://bsky.app/profile/bitcoinresearchkit.org">
<img src="https://img.shields.io/badge/bluesky-blue?link=https%3A%2F%2Fbsky.app%2Fprofile%2Fbitcoinresearchkit.org" alt="Bluesky" />
</a>
<a href="https://x.com/brkdotorg">
<img src="https://img.shields.io/badge/x.com-black" alt="X" />
</a>
</p>
A command line interface to interact with the full Bitcoin Research Kit. It's built on top of every other create and gives the possility to use BRK using the terminal instead of Rust.
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)).
## Requirements
### Hardware
#### Recommended
- [Latest base model Mac mini](https://www.apple.com/mac-mini/)
- [Thunderbolt 4 SSD enclosure](https://satechi.net/products/usb4-nvme-ssd-pro-enclosure/Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC80MDE4ODQ3MDA2NzI4OA==?queryID=7961465089021ee203a60db7e62e90d2)
- [2 TB NVMe SSD](https://shop.sandisk.com/products/ssd/internal-ssd/wd-black-sn850x-nvme-ssd?sku=WDS200T2X0E-00BCA0)
#### Minimum
To be determined
### Software
- [Bitcoin](https://bitcoin.org/en/full-node)
- [Rust](https://www.rust-lang.org/tools/install)
- Unix based operating system (Mac OS or Linux)
- 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)).
### Cargo
```bash
# Install
cargo install brk # or `cargo install brk_cli`, the result is the same
# Update
cargo install brk # or `cargo install-update -a` if you have `cargo-update` installed
```
### Source
```bash
git clone https://github.com/bitcoinresearchkit/brk.git
cd brk/crates/brk
cargo run -r
```
## Usage
Run `brk -h` to view each available command 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.
Every parameter set for `brk run` will be saved at `~/.brk/config.toml`, which will allow you to simply run `brk run` next time.
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/
+46
View File
@@ -0,0 +1,46 @@
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 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),
}
pub fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
fs::create_dir_all(dot_brk_path())?;
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),
})?
.join()
.unwrap()
}
+1
View File
@@ -0,0 +1 @@
use brk_cli::main;
+55
View File
@@ -0,0 +1,55 @@
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(())
}
+472
View File
@@ -0,0 +1,472 @@
use std::{
fs,
path::{Path, PathBuf},
thread::sleep,
time::Duration,
};
use bitcoincore_rpc::{self, Auth, Client, 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 log::info;
use serde::{Deserialize, Serialize};
pub fn run(config: RunConfig) -> color_eyre::Result<()> {
let config = RunConfig::import(Some(config))?;
let rpc = config.rpc()?;
let exit = Exit::new();
let parser = brk_parser::Parser::new(config.blocksdir(), rpc);
let format = config.format();
let mut indexer = Indexer::new(&config.outputsdir(), config.check_collisions())?;
indexer.import_stores()?;
indexer.import_vecs()?;
let wait_for_synced_node = || -> color_eyre::Result<()> {
let is_synced = || -> color_eyre::Result<bool> {
let info = rpc.get_blockchain_info()?;
Ok(info.headers == info.blocks)
};
if !is_synced()? {
info!("Waiting for node to be synced...");
while !is_synced()? {
sleep(Duration::from_secs(1))
}
}
Ok(())
};
let mut computer = Computer::new(&config.outputsdir(), config.fetcher(), format);
computer.import_stores(&indexer)?;
computer.import_vecs(&indexer, config.computation())?;
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 server = Server::new(served_indexer, served_computer, config.website())?;
let opt = Some(tokio::spawn(async move {
server.serve().await.unwrap();
}));
sleep(Duration::from_secs(1));
opt
} else {
None
};
if config.process() {
loop {
wait_for_synced_node()?;
let block_count = rpc.get_block_count()?;
info!("{} blocks found.", block_count + 1);
let starting_indexes = indexer.index(&parser, rpc, &exit)?;
computer.compute(&mut indexer, starting_indexes, &exit)?;
if let Some(delay) = config.delay() {
sleep(Duration::from_secs(delay))
}
info!("Waiting for new blocks...");
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,
}
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "brk_computer"
description = "A Bitcoin dataset computer, built on top of brk_indexer"
version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
[dependencies]
bitcoin = { workspace = true }
bitcoincore-rpc = { workspace = true }
brk_core = { workspace = true }
brk_exit = { workspace = true }
brk_fetcher = { workspace = true }
brk_indexer = { workspace = true }
brk_logger = { workspace = true }
brk_parser = { workspace = true }
brk_state = { workspace = true }
brk_vec = { workspace = true }
color-eyre = { workspace = true }
fjall = { workspace = true }
jiff = { workspace = true }
log = { workspace = true }
rayon = { workspace = true }
+34
View File
@@ -0,0 +1,34 @@
# BRK Computer
<p align="left">
<a href="https://github.com/bitcoinresearchkit/brk">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/bitcoinresearchkit/brk?style=social">
</a>
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
<img src="https://img.shields.io/crates/l/brk" alt="License" />
</a>
<a href="https://crates.io/crates/brk_computer">
<img src="https://img.shields.io/crates/v/brk_computer" alt="Version" />
</a>
<a href="https://docs.rs/brk_computer">
<img src="https://img.shields.io/docsrs/brk_computer" alt="Documentation" />
</a>
<img src="https://img.shields.io/crates/size/brk_computer" alt="Size" />
<a href="https://deps.rs/crate/brk_computer">
<img src="https://deps.rs/crate/brk_computer/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
</a>
<a href="https://bsky.app/profile/bitcoinresearchkit.org">
<img src="https://img.shields.io/badge/bluesky-blue?link=https%3A%2F%2Fbsky.app%2Fprofile%2Fbitcoinresearchkit.org" alt="Bluesky" />
</a>
<a href="https://x.com/brkdotorg">
<img src="https://img.shields.io/badge/x.com-black" alt="X" />
</a>
</p>
A dataset computer, built on top of `brk_indexer` and `brk_fetcher`. It computes any dataset you can think of and if it doesn't feel free to create an issue.
+54
View File
@@ -0,0 +1,54 @@
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};
pub fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
brk_logger::init(Some(Path::new(".log")));
let bitcoin_dir = default_bitcoin_path();
let rpc = Box::leak(Box::new(bitcoincore_rpc::Client::new(
"http://localhost:8332",
bitcoincore_rpc::Auth::CookieFile(bitcoin_dir.join(".cookie")),
)?));
let exit = Exit::new();
// Can't increase main thread's stack programatically, thus we need to use another thread
thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(move || -> color_eyre::Result<()> {
let parser = Parser::new(bitcoin_dir.join("blocks"), rpc);
let _outputs_dir = default_brk_path().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 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 starting_indexes = indexer.index(&parser, rpc, &exit)?;
computer.compute(&mut indexer, starting_indexes, &exit)?;
Ok(())
})?
.join()
.unwrap()
}
+104
View File
@@ -0,0 +1,104 @@
#![doc = include_str!("../README.md")]
#![doc = "\n## Example\n\n```rust"]
#![doc = include_str!("../examples/main.rs")]
#![doc = "```"]
use std::path::{Path, PathBuf};
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 log::info;
use stores::Stores;
use vecs::Vecs;
#[derive(Clone)]
pub struct Computer {
path: PathBuf,
fetcher: Option<Fetcher>,
vecs: Option<Vecs>,
stores: Option<Stores>,
format: Format,
}
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,
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(())
}
/// 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(())
}
}
impl Computer {
pub fn compute(
&mut self,
indexer: &mut Indexer,
starting_indexes: brk_indexer::Indexes,
exit: &Exit,
) -> 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()
}
}
+34
View File
@@ -0,0 +1,34 @@
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,
})
}
}
+27
View File
@@ -0,0 +1,27 @@
use std::ops::{Add, Div};
pub fn get_percentile<T>(sorted: &[T], percentile: f64) -> T
where
T: Clone + Div<usize, Output = T> + Add<T, Output = T>,
{
let len = sorted.len();
if len == 0 {
panic!();
} else if len == 1 {
sorted[0].clone()
} else {
let index = (len - 1) as f64 * percentile;
let fract = index.fract();
if fract != 0.0 {
let left = sorted.get(index as usize).unwrap().clone();
let right = sorted.get(index.ceil() as usize).unwrap().clone();
left / 2 + right / 2
} else {
// dbg!(sorted.len(), index);
sorted.get(index as usize).unwrap().clone()
}
}
}
+264
View File
@@ -0,0 +1,264 @@
use std::{fs, path::Path};
use brk_core::{
CheckedSub, DifficultyEpoch, HalvingEpoch, Height, StoredU32, StoredU64, StoredUsize,
Timestamp, Version, Weight,
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, AnyIterableVec, Computation, EagerVec, Format};
use super::{
Indexes,
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, StorableVecGeneatorOptions},
indexes,
};
const VERSION: Version = Version::ZERO;
#[derive(Clone)]
pub struct Vecs {
pub height_to_interval: EagerVec<Height, Timestamp>,
pub height_to_vbytes: EagerVec<Height, StoredU64>,
pub difficultyepoch_to_timestamp: EagerVec<DifficultyEpoch, Timestamp>,
pub halvingepoch_to_timestamp: EagerVec<HalvingEpoch, Timestamp>,
pub timeindexes_to_timestamp: ComputedVecsFromDateIndex<Timestamp>,
pub indexes_to_block_count: ComputedVecsFromHeight<StoredU32>,
pub indexes_to_block_interval: ComputedVecsFromHeight<Timestamp>,
pub indexes_to_block_size: ComputedVecsFromHeight<StoredUsize>,
pub indexes_to_block_vbytes: ComputedVecsFromHeight<StoredU64>,
pub indexes_to_block_weight: ComputedVecsFromHeight<Weight>,
}
impl Vecs {
pub fn forced_import(
path: &Path,
version: Version,
_computation: Computation,
format: Format,
) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
Ok(Self {
height_to_interval: EagerVec::forced_import(
path,
"interval",
version + VERSION + Version::ZERO,
format,
)?,
timeindexes_to_timestamp: ComputedVecsFromDateIndex::forced_import(
path,
"timestamp",
true,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_first(),
)?,
indexes_to_block_interval: ComputedVecsFromHeight::forced_import(
path,
"block_interval",
false,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_percentiles()
.add_minmax()
.add_average(),
)?,
indexes_to_block_count: ComputedVecsFromHeight::forced_import(
path,
"block_count",
true,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
)?,
indexes_to_block_weight: ComputedVecsFromHeight::forced_import(
path,
"block_weight",
false,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
)?,
indexes_to_block_size: ComputedVecsFromHeight::forced_import(
path,
"block_size",
false,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
)?,
height_to_vbytes: EagerVec::forced_import(
path,
"vbytes",
version + VERSION + Version::ZERO,
format,
)?,
indexes_to_block_vbytes: ComputedVecsFromHeight::forced_import(
path,
"block_vbytes",
false,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default()
.add_sum()
.add_cumulative(),
)?,
difficultyepoch_to_timestamp: EagerVec::forced_import(
path,
"timestamp",
version + VERSION + Version::ZERO,
format,
)?,
halvingepoch_to_timestamp: EagerVec::forced_import(
path,
"timestamp",
version + VERSION + Version::ZERO,
format,
)?,
})
}
pub fn compute(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> color_eyre::Result<()> {
self.timeindexes_to_timestamp.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_transform(
starting_indexes.dateindex,
&indexes.dateindex_to_date,
|(di, d, ..)| (di, Timestamp::from(d)),
exit,
)
},
)?;
self.indexes_to_block_count.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, indexer, _, starting_indexes, exit| {
let indexer_vecs = indexer.vecs();
v.compute_range(
starting_indexes.height,
&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();
self.height_to_interval.compute_transform(
starting_indexes.height,
&indexer_vecs.height_to_timestamp,
|(height, timestamp, ..)| {
let interval = height.decremented().map_or(Timestamp::ZERO, |prev_h| {
let prev_timestamp = height_to_timestamp_iter.unwrap_get_inner(prev_h);
timestamp
.checked_sub(prev_timestamp)
.unwrap_or(Timestamp::ZERO)
});
(height, interval)
},
exit,
)?;
self.indexes_to_block_interval.compute_rest(
indexes,
starting_indexes,
exit,
Some(&self.height_to_interval),
)?;
self.indexes_to_block_weight.compute_rest(
indexes,
starting_indexes,
exit,
Some(&indexer_vecs.height_to_weight),
)?;
self.indexes_to_block_size.compute_rest(
indexes,
starting_indexes,
exit,
Some(&indexer_vecs.height_to_total_size),
)?;
self.height_to_vbytes.compute_transform(
starting_indexes.height,
&indexer_vecs.height_to_weight,
|(h, w, ..)| {
(
h,
StoredU64::from(bitcoin::Weight::from(w).to_vbytes_floor()),
)
},
exit,
)?;
self.indexes_to_block_vbytes.compute_rest(
indexes,
starting_indexes,
exit,
Some(&self.height_to_vbytes),
)?;
let mut height_to_timestamp_iter = indexer_vecs.height_to_timestamp.iter();
self.difficultyepoch_to_timestamp.compute_transform(
starting_indexes.difficultyepoch,
&indexes.difficultyepoch_to_first_height,
|(i, h, ..)| (i, height_to_timestamp_iter.unwrap_get_inner(h)),
exit,
)?;
self.halvingepoch_to_timestamp.compute_transform(
starting_indexes.halvingepoch,
&indexes.halvingepoch_to_first_height,
|(i, h, ..)| (i, height_to_timestamp_iter.unwrap_get_inner(h)),
exit,
)?;
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
vec![
&self.height_to_interval as &dyn AnyCollectableVec,
&self.height_to_vbytes,
&self.difficultyepoch_to_timestamp,
&self.halvingepoch_to_timestamp,
],
self.timeindexes_to_timestamp.vecs(),
self.indexes_to_block_count.vecs(),
self.indexes_to_block_interval.vecs(),
self.indexes_to_block_size.vecs(),
self.indexes_to_block_vbytes.vecs(),
self.indexes_to_block_weight.vecs(),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
+154
View File
@@ -0,0 +1,154 @@
use std::{fs, path::Path};
use brk_core::{StoredU8, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, AnyVec, Computation, Format};
use super::{
Indexes,
grouped::{ComputedVecsFromHeight, StorableVecGeneatorOptions},
indexes,
};
const VERSION: Version = Version::ZERO;
#[derive(Clone)]
pub struct Vecs {
pub _0: ComputedVecsFromHeight<StoredU8>,
pub _1: ComputedVecsFromHeight<StoredU8>,
pub _50: ComputedVecsFromHeight<StoredU8>,
pub _100: ComputedVecsFromHeight<StoredU8>,
}
impl Vecs {
pub fn forced_import(
path: &Path,
version: Version,
_computation: Computation,
format: Format,
) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
Ok(Self {
_0: ComputedVecsFromHeight::forced_import(
path,
"0",
true,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
)?,
_1: ComputedVecsFromHeight::forced_import(
path,
"1",
true,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
)?,
_50: ComputedVecsFromHeight::forced_import(
path,
"50",
true,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
)?,
_100: ComputedVecsFromHeight::forced_import(
path,
"100",
true,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
)?,
})
}
pub fn compute(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> color_eyre::Result<()> {
self._0.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU8::new(0)),
exit,
)
},
)?;
self._1.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU8::new(1)),
exit,
)
},
)?;
self._50.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU8::new(50)),
exit,
)
},
)?;
self._100.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU8::new(100)),
exit,
)
},
)?;
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
self._0.vecs(),
self._1.vecs(),
self._50.vecs(),
self._100.vecs(),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,907 @@
use std::path::Path;
use brk_core::{CheckedSub, Result, StoredUsize, Version};
use brk_exit::Exit;
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format, StoredIndex, StoredType};
use color_eyre::eyre::ContextCompat;
use crate::utils::get_percentile;
use super::ComputedType;
#[derive(Clone, Debug)]
pub struct ComputedVecBuilder<I, T>
where
I: StoredIndex,
T: ComputedType,
{
pub first: Option<Box<EagerVec<I, T>>>,
pub average: Option<Box<EagerVec<I, T>>>,
pub sum: Option<Box<EagerVec<I, T>>>,
pub max: Option<Box<EagerVec<I, T>>>,
pub _90p: Option<Box<EagerVec<I, T>>>,
pub _75p: Option<Box<EagerVec<I, T>>>,
pub median: Option<Box<EagerVec<I, T>>>,
pub _25p: Option<Box<EagerVec<I, T>>>,
pub _10p: Option<Box<EagerVec<I, T>>>,
pub min: Option<Box<EagerVec<I, T>>>,
pub last: Option<Box<EagerVec<I, T>>>,
pub cumulative: Option<Box<EagerVec<I, T>>>,
}
const VERSION: Version = Version::ZERO;
impl<I, T> ComputedVecBuilder<I, T>
where
I: StoredIndex,
T: ComputedType,
{
pub fn forced_import(
path: &Path,
name: &str,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
let only_one_active = options.is_only_one_active();
let 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| {
if only_one_active {
name.to_string()
} else {
suffix(s)
}
};
let s = Self {
first: options.first.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_prefix("first"),
version + VERSION + Version::ZERO,
format,
)
.unwrap(),
)
}),
last: options.last.then(|| {
Box::new(
EagerVec::forced_import(path, name, version + Version::ZERO, format).unwrap(),
)
}),
min: options.min.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_suffix("min"),
version + VERSION + Version::ZERO,
format,
)
.unwrap(),
)
}),
max: options.max.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_suffix("max"),
version + VERSION + Version::ZERO,
format,
)
.unwrap(),
)
}),
median: options.median.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_suffix("median"),
version + VERSION + Version::ZERO,
format,
)
.unwrap(),
)
}),
average: options.average.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_suffix("average"),
version + VERSION + Version::ZERO,
format,
)
.unwrap(),
)
}),
sum: options.sum.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_suffix("sum"),
version + VERSION + Version::ZERO,
format,
)
.unwrap(),
)
}),
cumulative: options.cumulative.then(|| {
Box::new(
EagerVec::forced_import(
path,
&prefix("cumulative"),
version + VERSION + Version::ZERO,
format,
)
.unwrap(),
)
}),
_90p: options._90p.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_suffix("90p"),
version + VERSION + Version::ZERO,
format,
)
.unwrap(),
)
}),
_75p: options._75p.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_suffix("75p"),
version + VERSION + Version::ZERO,
format,
)
.unwrap(),
)
}),
_25p: options._25p.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_suffix("25p"),
version + VERSION + Version::ZERO,
format,
)
.unwrap(),
)
}),
_10p: options._10p.then(|| {
Box::new(
EagerVec::forced_import(
path,
&maybe_suffix("10p"),
version + VERSION + Version::ZERO,
format,
)
.unwrap(),
)
}),
};
Ok(s)
}
pub fn extend(
&mut self,
max_from: I,
source: &impl AnyIterableVec<I, T>,
exit: &Exit,
) -> Result<()> {
if self.cumulative.is_none() {
return Ok(());
};
self.validate_computed_version_or_reset_file(source.version())?;
let index = self.starting_index(max_from);
let cumulative_vec = self.cumulative.as_mut().unwrap();
let mut cumulative = index.decremented().map_or(T::from(0_usize), |index| {
cumulative_vec.iter().unwrap_get_inner(index)
});
source.iter_at(index).try_for_each(|(i, v)| -> Result<()> {
cumulative = cumulative.clone() + v.into_inner();
cumulative_vec.forced_push_at(i, cumulative.clone(), exit)
})?;
self.safe_flush(exit)?;
Ok(())
}
pub fn compute<I2>(
&mut self,
max_from: I,
source: &impl AnyIterableVec<I2, T>,
first_indexes: &impl AnyIterableVec<I, I2>,
count_indexes: &impl AnyIterableVec<I, StoredUsize>,
exit: &Exit,
) -> Result<()>
where
I2: StoredIndex + StoredType + CheckedSub<I2>,
{
self.validate_computed_version_or_reset_file(
source.version() + first_indexes.version() + count_indexes.version(),
)?;
let index = self.starting_index(max_from);
let mut count_indexes_iter = count_indexes.iter();
let mut source_iter = source.iter();
let cumulative_vec = self.cumulative.as_mut();
let mut cumulative = cumulative_vec.map(|cumulative_vec| {
index.decremented().map_or(T::from(0_usize), |index| {
cumulative_vec.iter().unwrap_get_inner(index)
})
});
first_indexes
.iter_at(index)
.try_for_each(|(i, first_index)| -> Result<()> {
let first_index = first_index.into_inner();
let count_index = count_indexes_iter.unwrap_get_inner(i);
if let Some(first) = self.first.as_mut() {
let f = source_iter
.get_inner(first_index)
.unwrap_or_else(|| T::from(0_usize));
first.forced_push_at(index, f, exit)?;
}
if let Some(last) = self.last.as_mut() {
let count_index = *count_index;
if count_index == 0 {
panic!("should compute last if count can be 0")
}
let last_index = first_index + (count_index - 1);
let v = source_iter.unwrap_get_inner(last_index);
// .context("to work")
// .inspect_err(|_| {
// dbg!(first_index, count_index, last_index);
// })
// .unwrap()
// .into_inner();
last.forced_push_at(index, v, exit)?;
}
let needs_sum_or_cumulative = self.sum.is_some() || self.cumulative.is_some();
let needs_average_sum_or_cumulative =
needs_sum_or_cumulative || self.average.is_some();
let needs_sorted = self.max.is_some()
|| self._90p.is_some()
|| self._75p.is_some()
|| self.median.is_some()
|| self._25p.is_some()
|| self._10p.is_some()
|| self.min.is_some();
let needs_values = needs_sorted || needs_average_sum_or_cumulative;
if needs_values {
source_iter.set(first_index);
let mut values = (&mut source_iter)
.take(*count_index)
.map(|(_, v)| v.into_inner())
.collect::<Vec<_>>();
if needs_sorted {
values.sort_unstable();
if let Some(max) = self.max.as_mut() {
max.forced_push_at(
i,
values
.last()
.context("expect some")
.inspect_err(|_| {
dbg!(
&values,
max.path(),
first_indexes.name(),
first_index,
count_indexes.name(),
count_index,
source.len(),
source.name()
);
})
.unwrap()
.clone(),
exit,
)?;
}
if let Some(_90p) = self._90p.as_mut() {
_90p.forced_push_at(i, get_percentile(&values, 0.90), exit)?;
}
if let Some(_75p) = self._75p.as_mut() {
_75p.forced_push_at(i, get_percentile(&values, 0.75), exit)?;
}
if let Some(median) = self.median.as_mut() {
median.forced_push_at(i, get_percentile(&values, 0.50), exit)?;
}
if let Some(_25p) = self._25p.as_mut() {
_25p.forced_push_at(i, get_percentile(&values, 0.25), exit)?;
}
if let Some(_10p) = self._10p.as_mut() {
_10p.forced_push_at(i, get_percentile(&values, 0.10), exit)?;
}
if let Some(min) = self.min.as_mut() {
min.forced_push_at(i, values.first().unwrap().clone(), exit)?;
}
}
if needs_average_sum_or_cumulative {
let len = values.len();
let sum = values.into_iter().fold(T::from(0), |a, b| a + b);
if let Some(average) = self.average.as_mut() {
let avg = sum.clone() / len;
average.forced_push_at(i, avg, exit)?;
}
if needs_sum_or_cumulative {
if let Some(sum_vec) = self.sum.as_mut() {
sum_vec.forced_push_at(i, sum.clone(), exit)?;
}
if let Some(cumulative_vec) = self.cumulative.as_mut() {
let t = cumulative.as_ref().unwrap().clone() + sum;
cumulative.replace(t.clone());
cumulative_vec.forced_push_at(i, t, exit)?;
}
}
}
}
Ok(())
})?;
self.safe_flush(exit)?;
Ok(())
}
#[allow(clippy::wrong_self_convention)]
pub fn from_aligned<I2>(
&mut self,
max_from: I,
source: &ComputedVecBuilder<I2, T>,
first_indexes: &impl AnyIterableVec<I, I2>,
count_indexes: &impl AnyIterableVec<I, StoredUsize>,
exit: &Exit,
) -> Result<()>
where
I2: StoredIndex + StoredType + CheckedSub<I2>,
{
if self._90p.is_some()
|| self._75p.is_some()
|| self.median.is_some()
|| self._25p.is_some()
|| self._10p.is_some()
{
panic!("unsupported");
}
self.validate_computed_version_or_reset_file(
VERSION + first_indexes.version() + count_indexes.version(),
)?;
let index = self.starting_index(max_from);
let mut count_indexes_iter = count_indexes.iter();
let mut source_first_iter = source.first.as_ref().map(|f| f.iter());
let mut source_last_iter = source.last.as_ref().map(|f| f.iter());
let mut source_max_iter = source.max.as_ref().map(|f| f.iter());
let mut source_min_iter = source.min.as_ref().map(|f| f.iter());
let mut source_average_iter = source.average.as_ref().map(|f| f.iter());
let mut source_sum_iter = source.sum.as_ref().map(|f| f.iter());
let mut cumulative = self.cumulative.as_mut().map(|cumulative_vec| {
index.decremented().map_or(T::from(0_usize), |index| {
cumulative_vec.iter().unwrap_get_inner(index)
})
});
first_indexes
.iter_at(index)
.try_for_each(|(i, first_index, ..)| -> Result<()> {
let first_index = first_index.into_inner();
let count_index = count_indexes_iter.unwrap_get_inner(i);
if let Some(first) = self.first.as_mut() {
let v = source_first_iter
.as_mut()
.unwrap()
.unwrap_get_inner(first_index);
first.forced_push_at(index, v, exit)?;
}
if let Some(last) = self.last.as_mut() {
let count_index = *count_index;
if count_index == 0 {
panic!("should compute last if count can be 0")
}
let last_index = first_index + (count_index - 1);
let v = source_last_iter
.as_mut()
.unwrap()
.unwrap_get_inner(last_index);
last.forced_push_at(index, v, exit)?;
}
let needs_sum_or_cumulative = self.sum.is_some() || self.cumulative.is_some();
let needs_average_sum_or_cumulative =
needs_sum_or_cumulative || self.average.is_some();
let needs_sorted = self.max.is_some() || self.min.is_some();
let needs_values = needs_sorted || needs_average_sum_or_cumulative;
if needs_values {
if needs_sorted {
if let Some(max) = self.max.as_mut() {
let source_max_iter = source_max_iter.as_mut().unwrap();
source_max_iter.set(first_index);
let mut values = source_max_iter
.take(*count_index)
.map(|(_, v)| v.into_inner())
.collect::<Vec<_>>();
values.sort_unstable();
max.forced_push_at(i, values.last().unwrap().clone(), exit)?;
}
if let Some(min) = self.min.as_mut() {
let source_min_iter = source_min_iter.as_mut().unwrap();
source_min_iter.set(first_index);
let mut values = source_min_iter
.take(*count_index)
.map(|(_, v)| v.into_inner())
.collect::<Vec<_>>();
values.sort_unstable();
min.forced_push_at(i, values.first().unwrap().clone(), exit)?;
}
}
if needs_average_sum_or_cumulative {
if let Some(average) = self.average.as_mut() {
let source_average_iter = source_average_iter.as_mut().unwrap();
source_average_iter.set(first_index);
let values = source_average_iter
.take(*count_index)
.map(|(_, v)| v.into_inner())
.collect::<Vec<_>>();
let len = values.len();
let cumulative = values.into_iter().fold(T::from(0), |a, b| a + b);
// TODO: Multiply by count then divide by cumulative
// Right now it's not 100% accurate as there could be more or less elements in the lower timeframe (28 days vs 31 days in a month for example)
let avg = cumulative / len;
average.forced_push_at(i, avg, exit)?;
}
if needs_sum_or_cumulative {
let source_sum_iter = source_sum_iter.as_mut().unwrap();
source_sum_iter.set(first_index);
let values = source_sum_iter
.take(*count_index)
.map(|(_, v)| v.into_inner())
.collect::<Vec<_>>();
let sum = values.into_iter().fold(T::from(0), |a, b| a + b);
if let Some(sum_vec) = self.sum.as_mut() {
sum_vec.forced_push_at(i, sum.clone(), exit)?;
}
if let Some(cumulative_vec) = self.cumulative.as_mut() {
let t = cumulative.as_ref().unwrap().clone() + sum;
cumulative.replace(t.clone());
cumulative_vec.forced_push_at(i, t, exit)?;
}
}
}
}
Ok(())
})?;
self.safe_flush(exit)?;
Ok(())
}
pub fn starting_index(&self, max_from: I) -> I {
max_from.min(I::from(
self.vecs().into_iter().map(|v| v.len()).min().unwrap(),
))
}
pub fn unwrap_first(&self) -> &EagerVec<I, T> {
self.first.as_ref().unwrap()
}
#[allow(unused)]
pub fn unwrap_average(&self) -> &EagerVec<I, T> {
self.average.as_ref().unwrap()
}
pub fn unwrap_sum(&self) -> &EagerVec<I, T> {
self.sum.as_ref().unwrap()
}
pub fn unwrap_max(&self) -> &EagerVec<I, T> {
self.max.as_ref().unwrap()
}
#[allow(unused)]
pub fn unwrap_90p(&self) -> &EagerVec<I, T> {
self._90p.as_ref().unwrap()
}
#[allow(unused)]
pub fn unwrap_75p(&self) -> &EagerVec<I, T> {
self._75p.as_ref().unwrap()
}
#[allow(unused)]
pub fn unwrap_median(&self) -> &EagerVec<I, T> {
self.median.as_ref().unwrap()
}
#[allow(unused)]
pub fn unwrap_25p(&self) -> &EagerVec<I, T> {
self._25p.as_ref().unwrap()
}
#[allow(unused)]
pub fn unwrap_10p(&self) -> &EagerVec<I, T> {
self._10p.as_ref().unwrap()
}
pub fn unwrap_min(&self) -> &EagerVec<I, T> {
self.min.as_ref().unwrap()
}
pub fn unwrap_last(&self) -> &EagerVec<I, T> {
self.last.as_ref().unwrap()
}
#[allow(unused)]
pub fn unwrap_cumulative(&self) -> &EagerVec<I, T> {
self.cumulative.as_ref().unwrap()
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
let mut v: Vec<&dyn AnyCollectableVec> = vec![];
if let Some(first) = self.first.as_ref() {
v.push(first.as_ref());
}
if let Some(last) = self.last.as_ref() {
v.push(last.as_ref());
}
if let Some(min) = self.min.as_ref() {
v.push(min.as_ref());
}
if let Some(max) = self.max.as_ref() {
v.push(max.as_ref());
}
if let Some(median) = self.median.as_ref() {
v.push(median.as_ref());
}
if let Some(average) = self.average.as_ref() {
v.push(average.as_ref());
}
if let Some(sum) = self.sum.as_ref() {
v.push(sum.as_ref());
}
if let Some(cumulative) = self.cumulative.as_ref() {
v.push(cumulative.as_ref());
}
if let Some(_90p) = self._90p.as_ref() {
v.push(_90p.as_ref());
}
if let Some(_75p) = self._75p.as_ref() {
v.push(_75p.as_ref());
}
if let Some(_25p) = self._25p.as_ref() {
v.push(_25p.as_ref());
}
if let Some(_10p) = self._10p.as_ref() {
v.push(_10p.as_ref());
}
v
}
pub fn safe_flush(&mut self, exit: &Exit) -> Result<()> {
if let Some(first) = self.first.as_mut() {
first.safe_flush(exit)?;
}
if let Some(last) = self.last.as_mut() {
last.safe_flush(exit)?;
}
if let Some(min) = self.min.as_mut() {
min.safe_flush(exit)?;
}
if let Some(max) = self.max.as_mut() {
max.safe_flush(exit)?;
}
if let Some(median) = self.median.as_mut() {
median.safe_flush(exit)?;
}
if let Some(average) = self.average.as_mut() {
average.safe_flush(exit)?;
}
if let Some(sum) = self.sum.as_mut() {
sum.safe_flush(exit)?;
}
if let Some(cumulative) = self.cumulative.as_mut() {
cumulative.safe_flush(exit)?;
}
if let Some(_90p) = self._90p.as_mut() {
_90p.safe_flush(exit)?;
}
if let Some(_75p) = self._75p.as_mut() {
_75p.safe_flush(exit)?;
}
if let Some(_25p) = self._25p.as_mut() {
_25p.safe_flush(exit)?;
}
if let Some(_10p) = self._10p.as_mut() {
_10p.safe_flush(exit)?;
}
Ok(())
}
pub fn validate_computed_version_or_reset_file(&mut self, version: Version) -> Result<()> {
if let Some(first) = self.first.as_mut() {
first.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
if let Some(last) = self.last.as_mut() {
last.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
if let Some(min) = self.min.as_mut() {
min.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
if let Some(max) = self.max.as_mut() {
max.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
if let Some(median) = self.median.as_mut() {
median.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
if let Some(average) = self.average.as_mut() {
average.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
if let Some(sum) = self.sum.as_mut() {
sum.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
if let Some(cumulative) = self.cumulative.as_mut() {
cumulative.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
if let Some(_90p) = self._90p.as_mut() {
_90p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
if let Some(_75p) = self._75p.as_mut() {
_75p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
if let Some(_25p) = self._25p.as_mut() {
_25p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
if let Some(_10p) = self._10p.as_mut() {
_10p.validate_computed_version_or_reset_file(Version::ZERO + version)?;
}
Ok(())
}
}
#[derive(Default, Clone, Copy)]
pub struct StorableVecGeneatorOptions {
average: bool,
sum: bool,
max: bool,
_90p: bool,
_75p: bool,
median: bool,
_25p: bool,
_10p: bool,
min: bool,
first: bool,
last: bool,
cumulative: bool,
}
impl StorableVecGeneatorOptions {
pub fn add_first(mut self) -> Self {
self.first = true;
self
}
pub fn add_last(mut self) -> Self {
self.last = true;
self
}
pub fn add_min(mut self) -> Self {
self.min = true;
self
}
pub fn add_max(mut self) -> Self {
self.max = true;
self
}
#[allow(unused)]
pub fn add_median(mut self) -> Self {
self.median = true;
self
}
pub fn add_average(mut self) -> Self {
self.average = true;
self
}
pub fn add_sum(mut self) -> Self {
self.sum = true;
self
}
#[allow(unused)]
pub fn add_90p(mut self) -> Self {
self._90p = true;
self
}
#[allow(unused)]
pub fn add_75p(mut self) -> Self {
self._75p = true;
self
}
#[allow(unused)]
pub fn add_25p(mut self) -> Self {
self._25p = true;
self
}
#[allow(unused)]
pub fn add_10p(mut self) -> Self {
self._10p = true;
self
}
pub fn add_cumulative(mut self) -> Self {
self.cumulative = true;
self
}
#[allow(unused)]
pub fn rm_min(mut self) -> Self {
self.min = false;
self
}
#[allow(unused)]
pub fn rm_max(mut self) -> Self {
self.max = false;
self
}
#[allow(unused)]
pub fn rm_median(mut self) -> Self {
self.median = false;
self
}
#[allow(unused)]
pub fn rm_average(mut self) -> Self {
self.average = false;
self
}
#[allow(unused)]
pub fn rm_sum(mut self) -> Self {
self.sum = false;
self
}
#[allow(unused)]
pub fn rm_90p(mut self) -> Self {
self._90p = false;
self
}
#[allow(unused)]
pub fn rm_75p(mut self) -> Self {
self._75p = false;
self
}
#[allow(unused)]
pub fn rm_25p(mut self) -> Self {
self._25p = false;
self
}
#[allow(unused)]
pub fn rm_10p(mut self) -> Self {
self._10p = false;
self
}
#[allow(unused)]
pub fn rm_cumulative(mut self) -> Self {
self.cumulative = false;
self
}
pub fn add_minmax(mut self) -> Self {
self.min = true;
self.max = true;
self
}
pub fn add_percentiles(mut self) -> Self {
self._90p = true;
self._75p = true;
self.median = true;
self._25p = true;
self._10p = true;
self
}
pub fn remove_percentiles(mut self) -> Self {
self._90p = false;
self._75p = false;
self.median = false;
self._25p = false;
self._10p = false;
self
}
pub fn is_only_one_active(&self) -> bool {
[
self.average,
self.sum,
self.max,
self._90p,
self._75p,
self.median,
self._25p,
self._10p,
self.min,
self.first,
self.last,
self.cumulative,
]
.iter()
.filter(|b| **b)
.count()
== 1
}
pub fn copy_self_extra(&self) -> Self {
Self {
cumulative: self.cumulative,
..Self::default()
}
}
}
@@ -0,0 +1,218 @@
use std::path::Path;
use brk_core::{
DateIndex, DecadeIndex, MonthIndex, QuarterIndex, Result, Version, WeekIndex, YearIndex,
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format};
use crate::vecs::{Indexes, indexes};
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
#[derive(Clone)]
pub struct ComputedVecsFromDateIndex<T>
where
T: ComputedType + PartialOrd,
{
pub dateindex: Option<EagerVec<DateIndex, T>>,
pub dateindex_extra: ComputedVecBuilder<DateIndex, T>,
pub weekindex: ComputedVecBuilder<WeekIndex, T>,
pub monthindex: ComputedVecBuilder<MonthIndex, T>,
pub quarterindex: ComputedVecBuilder<QuarterIndex, T>,
pub yearindex: ComputedVecBuilder<YearIndex, T>,
pub decadeindex: ComputedVecBuilder<DecadeIndex, T>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromDateIndex<T>
where
T: ComputedType,
{
pub fn forced_import(
path: &Path,
name: &str,
compute_source: bool,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
let dateindex = compute_source.then(|| {
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format).unwrap()
});
let dateindex_extra = ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options.copy_self_extra(),
)?;
let options = options.remove_percentiles();
Ok(Self {
dateindex,
dateindex_extra,
weekindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
monthindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
quarterindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
yearindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
decadeindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
})
}
pub fn compute_all<F>(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
mut compute: F,
) -> color_eyre::Result<()>
where
F: FnMut(
&mut EagerVec<DateIndex, T>,
&Indexer,
&indexes::Vecs,
&Indexes,
&Exit,
) -> Result<()>,
{
compute(
self.dateindex.as_mut().unwrap(),
indexer,
indexes,
starting_indexes,
exit,
)?;
let dateindex: Option<&EagerVec<DateIndex, T>> = None;
self.compute_rest(indexes, starting_indexes, exit, dateindex)
}
pub fn compute_rest(
&mut self,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
dateindex: Option<&impl AnyIterableVec<DateIndex, T>>,
) -> color_eyre::Result<()> {
if let Some(dateindex) = dateindex {
self.dateindex_extra
.extend(starting_indexes.dateindex, dateindex, exit)?;
self.weekindex.compute(
starting_indexes.weekindex,
dateindex,
&indexes.weekindex_to_first_dateindex,
&indexes.weekindex_to_dateindex_count,
exit,
)?;
self.monthindex.compute(
starting_indexes.monthindex,
dateindex,
&indexes.monthindex_to_first_dateindex,
&indexes.monthindex_to_dateindex_count,
exit,
)?;
} else {
let dateindex = self.dateindex.as_ref().unwrap();
self.dateindex_extra
.extend(starting_indexes.dateindex, dateindex, exit)?;
self.weekindex.compute(
starting_indexes.weekindex,
dateindex,
&indexes.weekindex_to_first_dateindex,
&indexes.weekindex_to_dateindex_count,
exit,
)?;
self.monthindex.compute(
starting_indexes.monthindex,
dateindex,
&indexes.monthindex_to_first_dateindex,
&indexes.monthindex_to_dateindex_count,
exit,
)?;
}
self.quarterindex.from_aligned(
starting_indexes.quarterindex,
&self.monthindex,
&indexes.quarterindex_to_first_monthindex,
&indexes.quarterindex_to_monthindex_count,
exit,
)?;
self.yearindex.from_aligned(
starting_indexes.yearindex,
&self.monthindex,
&indexes.yearindex_to_first_monthindex,
&indexes.yearindex_to_monthindex_count,
exit,
)?;
self.decadeindex.from_aligned(
starting_indexes.decadeindex,
&self.yearindex,
&indexes.decadeindex_to_first_yearindex,
&indexes.decadeindex_to_yearindex_count,
exit,
)?;
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
self.dateindex
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.dateindex_extra.vecs(),
self.weekindex.vecs(),
self.monthindex.vecs(),
self.quarterindex.vecs(),
self.yearindex.vecs(),
self.decadeindex.vecs(),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
@@ -0,0 +1,253 @@
use std::path::Path;
use brk_core::{
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, Result, Version,
WeekIndex, YearIndex,
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, AnyIterableVec, EagerVec, Format};
use crate::vecs::{Indexes, indexes};
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
#[derive(Clone)]
pub struct ComputedVecsFromHeight<T>
where
T: ComputedType + PartialOrd,
{
pub height: Option<EagerVec<Height, T>>,
pub height_extra: ComputedVecBuilder<Height, T>,
pub dateindex: ComputedVecBuilder<DateIndex, T>,
pub weekindex: ComputedVecBuilder<WeekIndex, T>,
pub difficultyepoch: ComputedVecBuilder<DifficultyEpoch, T>,
pub monthindex: ComputedVecBuilder<MonthIndex, T>,
pub quarterindex: ComputedVecBuilder<QuarterIndex, T>,
pub yearindex: ComputedVecBuilder<YearIndex, T>,
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
pub decadeindex: ComputedVecBuilder<DecadeIndex, T>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromHeight<T>
where
T: ComputedType + Ord + From<f64>,
f64: From<T>,
{
pub fn forced_import(
path: &Path,
name: &str,
compute_source: bool,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
let height = compute_source.then(|| {
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format).unwrap()
});
let height_extra = ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options.copy_self_extra(),
)?;
let dateindex = ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?;
let options = options.remove_percentiles();
Ok(Self {
height,
height_extra,
dateindex,
weekindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
difficultyepoch: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
monthindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
quarterindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
yearindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
// halvingepoch: StorableVecGeneator::forced_import(path, name, version + VERSION + Version::ZERO, format, options)?,
decadeindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
})
}
pub fn compute_all<F>(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
mut compute: F,
) -> color_eyre::Result<()>
where
F: FnMut(&mut EagerVec<Height, T>, &Indexer, &indexes::Vecs, &Indexes, &Exit) -> Result<()>,
{
compute(
self.height.as_mut().unwrap(),
indexer,
indexes,
starting_indexes,
exit,
)?;
let height: Option<&EagerVec<Height, T>> = None;
self.compute_rest(indexes, starting_indexes, exit, height)
}
pub fn compute_rest(
&mut self,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
height: Option<&impl AnyIterableVec<Height, T>>,
) -> color_eyre::Result<()> {
if let Some(height) = height {
self.height_extra
.extend(starting_indexes.height, height, exit)?;
self.dateindex.compute(
starting_indexes.dateindex,
height,
&indexes.dateindex_to_first_height,
&indexes.dateindex_to_height_count,
exit,
)?;
self.difficultyepoch.compute(
starting_indexes.difficultyepoch,
height,
&indexes.difficultyepoch_to_first_height,
&indexes.difficultyepoch_to_height_count,
exit,
)?;
} else {
let height = self.height.as_ref().unwrap();
self.height_extra
.extend(starting_indexes.height, height, exit)?;
self.dateindex.compute(
starting_indexes.dateindex,
height,
&indexes.dateindex_to_first_height,
&indexes.dateindex_to_height_count,
exit,
)?;
self.difficultyepoch.compute(
starting_indexes.difficultyepoch,
height,
&indexes.difficultyepoch_to_first_height,
&indexes.difficultyepoch_to_height_count,
exit,
)?;
}
self.weekindex.from_aligned(
starting_indexes.weekindex,
&self.dateindex,
&indexes.weekindex_to_first_dateindex,
&indexes.weekindex_to_dateindex_count,
exit,
)?;
self.monthindex.from_aligned(
starting_indexes.monthindex,
&self.dateindex,
&indexes.monthindex_to_first_dateindex,
&indexes.monthindex_to_dateindex_count,
exit,
)?;
self.quarterindex.from_aligned(
starting_indexes.quarterindex,
&self.monthindex,
&indexes.quarterindex_to_first_monthindex,
&indexes.quarterindex_to_monthindex_count,
exit,
)?;
self.yearindex.from_aligned(
starting_indexes.yearindex,
&self.monthindex,
&indexes.yearindex_to_first_monthindex,
&indexes.yearindex_to_monthindex_count,
exit,
)?;
self.decadeindex.from_aligned(
starting_indexes.decadeindex,
&self.yearindex,
&indexes.decadeindex_to_first_yearindex,
&indexes.decadeindex_to_yearindex_count,
exit,
)?;
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
self.height
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.height_extra.vecs(),
self.dateindex.vecs(),
self.weekindex.vecs(),
self.difficultyepoch.vecs(),
self.monthindex.vecs(),
self.quarterindex.vecs(),
self.yearindex.vecs(),
// self.halvingepoch.vecs(),
self.decadeindex.vecs(),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
@@ -0,0 +1,102 @@
use std::path::Path;
use brk_core::{DifficultyEpoch, Height, Result, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, EagerVec, Format};
use crate::vecs::{Indexes, indexes};
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
#[derive(Clone)]
pub struct ComputedVecsFromHeightStrict<T>
where
T: ComputedType + PartialOrd,
{
pub height: EagerVec<Height, T>,
pub height_extra: ComputedVecBuilder<Height, T>,
pub difficultyepoch: ComputedVecBuilder<DifficultyEpoch, T>,
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromHeightStrict<T>
where
T: ComputedType + Ord + From<f64>,
f64: From<T>,
{
pub fn forced_import(
path: &Path,
name: &str,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
let height =
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format)?;
let height_extra = ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options.copy_self_extra(),
)?;
let options = options.remove_percentiles();
Ok(Self {
height,
height_extra,
difficultyepoch: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
// halvingepoch: StorableVecGeneator::forced_import(path, name, version + VERSION + Version::ZERO, format, options)?,
})
}
pub fn compute<F>(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
mut compute: F,
) -> color_eyre::Result<()>
where
F: FnMut(&mut EagerVec<Height, T>, &Indexer, &indexes::Vecs, &Indexes, &Exit) -> Result<()>,
{
compute(&mut self.height, indexer, indexes, starting_indexes, exit)?;
self.height_extra
.extend(starting_indexes.height, &self.height, exit)?;
self.difficultyepoch.compute(
starting_indexes.difficultyepoch,
&self.height,
&indexes.difficultyepoch_to_first_height,
&indexes.difficultyepoch_to_height_count,
exit,
)?;
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
vec![&self.height as &dyn AnyCollectableVec],
self.height_extra.vecs(),
self.difficultyepoch.vecs(),
// self.halvingepoch.vecs(),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
@@ -0,0 +1,629 @@
use std::path::Path;
use brk_core::{
Bitcoin, DateIndex, DecadeIndex, DifficultyEpoch, Dollars, Height, MonthIndex, QuarterIndex,
Result, Sats, TxIndex, Version, WeekIndex, YearIndex,
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{
AnyCollectableVec, AnyVec, CollectableVec, EagerVec, Format, StoredIndex, VecIterator,
};
use crate::vecs::{Indexes, fetched, indexes};
use super::{ComputedType, ComputedVecBuilder, StorableVecGeneatorOptions};
#[derive(Clone)]
pub struct ComputedVecsFromTxindex<T>
where
T: ComputedType + PartialOrd,
{
pub txindex: Option<Box<EagerVec<TxIndex, T>>>,
pub height: ComputedVecBuilder<Height, T>,
pub dateindex: ComputedVecBuilder<DateIndex, T>,
pub weekindex: ComputedVecBuilder<WeekIndex, T>,
pub difficultyepoch: ComputedVecBuilder<DifficultyEpoch, T>,
pub monthindex: ComputedVecBuilder<MonthIndex, T>,
pub quarterindex: ComputedVecBuilder<QuarterIndex, T>,
pub yearindex: ComputedVecBuilder<YearIndex, T>,
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
pub decadeindex: ComputedVecBuilder<DecadeIndex, T>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromTxindex<T>
where
T: ComputedType + Ord + From<f64>,
f64: From<T>,
{
pub fn forced_import(
path: &Path,
name: &str,
compute_source: bool,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
let txindex = compute_source.then(|| {
Box::new(
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format)
.unwrap(),
)
});
let height = ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?;
let options = options.remove_percentiles();
Ok(Self {
txindex,
height,
dateindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
weekindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
difficultyepoch: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
monthindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
quarterindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
yearindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
// halvingepoch: StorableVecGeneator::forced_import(path, name, version + VERSION + Version::ZERO, format, options)?,
decadeindex: ComputedVecBuilder::forced_import(
path,
name,
version + VERSION + Version::ZERO,
format,
options,
)?,
})
}
// #[allow(unused)]
// pub fn compute_all<F>(
// &mut self,
// indexer: &Indexer,
// indexes: &indexes::Vecs,
// starting_indexes: &Indexes,
// exit: &Exit,
// mut compute: F,
// ) -> color_eyre::Result<()>
// where
// F: FnMut(
// &mut EagerVec<TxIndex, T>,
// &Indexer,
// &indexes::Vecs,
// &Indexes,
// &Exit,
// ) -> Result<()>,
// {
// compute(
// self.txindex.as_mut().unwrap(),
// indexer,
// indexes,
// starting_indexes,
// exit,
// )?;
// let txindex: Option<&StoredVec<TxIndex, T>> = None;
// self.compute_rest(indexer, indexes, starting_indexes, exit, txindex)?;
// Ok(())
// }
pub fn compute_rest(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
txindex: Option<&impl CollectableVec<TxIndex, T>>,
) -> Result<()> {
if let Some(txindex) = txindex {
self.height.compute(
starting_indexes.height,
txindex,
&indexer.vecs().height_to_first_txindex,
&indexes.height_to_txindex_count,
exit,
)?;
} else {
let txindex = self.txindex.as_ref().unwrap().as_ref();
self.height.compute(
starting_indexes.height,
txindex,
&indexer.vecs().height_to_first_txindex,
&indexes.height_to_txindex_count,
exit,
)?;
}
self.compute_after_height(indexes, starting_indexes, exit)
}
fn compute_after_height(
&mut self,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.dateindex.from_aligned(
starting_indexes.dateindex,
&self.height,
&indexes.dateindex_to_first_height,
&indexes.dateindex_to_height_count,
exit,
)?;
self.weekindex.from_aligned(
starting_indexes.weekindex,
&self.dateindex,
&indexes.weekindex_to_first_dateindex,
&indexes.weekindex_to_dateindex_count,
exit,
)?;
self.monthindex.from_aligned(
starting_indexes.monthindex,
&self.dateindex,
&indexes.monthindex_to_first_dateindex,
&indexes.monthindex_to_dateindex_count,
exit,
)?;
self.quarterindex.from_aligned(
starting_indexes.quarterindex,
&self.monthindex,
&indexes.quarterindex_to_first_monthindex,
&indexes.quarterindex_to_monthindex_count,
exit,
)?;
self.yearindex.from_aligned(
starting_indexes.yearindex,
&self.monthindex,
&indexes.yearindex_to_first_monthindex,
&indexes.yearindex_to_monthindex_count,
exit,
)?;
self.decadeindex.from_aligned(
starting_indexes.decadeindex,
&self.yearindex,
&indexes.decadeindex_to_first_yearindex,
&indexes.decadeindex_to_yearindex_count,
exit,
)?;
self.difficultyepoch.from_aligned(
starting_indexes.difficultyepoch,
&self.height,
&indexes.difficultyepoch_to_first_height,
&indexes.difficultyepoch_to_height_count,
exit,
)?;
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
self.txindex
.as_ref()
.map_or(vec![], |v| vec![v.as_ref() as &dyn AnyCollectableVec]),
self.height.vecs(),
self.dateindex.vecs(),
self.weekindex.vecs(),
self.difficultyepoch.vecs(),
self.monthindex.vecs(),
self.quarterindex.vecs(),
self.yearindex.vecs(),
// self.halvingepoch.vecs(),
self.decadeindex.vecs(),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
impl ComputedVecsFromTxindex<Bitcoin> {
pub fn compute_rest_from_sats(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
sats: &ComputedVecsFromTxindex<Sats>,
txindex: Option<&impl CollectableVec<TxIndex, Bitcoin>>,
) -> Result<()> {
let txindex_version = if let Some(txindex) = txindex {
txindex.version()
} else {
self.txindex.as_ref().unwrap().as_ref().version()
};
self.height
.validate_computed_version_or_reset_file(txindex_version)?;
let starting_index = self.height.starting_index(starting_indexes.height);
(starting_index.unwrap_to_usize()..indexer.vecs().height_to_weight.len())
.map(Height::from)
.try_for_each(|height| -> Result<()> {
if let Some(first) = self.height.first.as_mut() {
first.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_first()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
if let Some(average) = self.height.average.as_mut() {
average.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_average()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
if let Some(sum) = self.height.sum.as_mut() {
sum.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_sum()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
if let Some(max) = self.height.max.as_mut() {
max.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_max()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
if let Some(_90p) = self.height._90p.as_mut() {
_90p.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_90p()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
if let Some(_75p) = self.height._75p.as_mut() {
_75p.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_75p()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
if let Some(median) = self.height.median.as_mut() {
median.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_median()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
if let Some(_25p) = self.height._25p.as_mut() {
_25p.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_25p()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
if let Some(_10p) = self.height._10p.as_mut() {
_10p.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_10p()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
if let Some(min) = self.height.min.as_mut() {
min.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_min()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
if let Some(last) = self.height.last.as_mut() {
last.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_last()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
if let Some(cumulative) = self.height.cumulative.as_mut() {
cumulative.forced_push_at(
height,
Bitcoin::from(
sats.height
.unwrap_cumulative()
.into_iter()
.unwrap_get_inner(height),
),
exit,
)?;
}
Ok(())
})?;
self.height.safe_flush(exit)?;
self.compute_after_height(indexes, starting_indexes, exit)
}
}
impl ComputedVecsFromTxindex<Dollars> {
#[allow(clippy::too_many_arguments)]
pub fn compute_rest_from_bitcoin(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
bitcoin: &ComputedVecsFromTxindex<Bitcoin>,
txindex: Option<&impl CollectableVec<TxIndex, Dollars>>,
fetched: &fetched::Vecs,
) -> Result<()> {
let txindex_version = if let Some(txindex) = txindex {
txindex.version()
} else {
self.txindex.as_ref().unwrap().as_ref().version()
};
self.height
.validate_computed_version_or_reset_file(txindex_version)?;
let starting_index = self.height.starting_index(starting_indexes.height);
let mut close_iter = fetched.chainindexes_to_close.height.into_iter();
(starting_index.unwrap_to_usize()..indexer.vecs().height_to_weight.len())
.map(Height::from)
.try_for_each(|height| -> Result<()> {
let price = *close_iter.unwrap_get_inner(height);
if let Some(first) = self.height.first.as_mut() {
first.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_first()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
if let Some(average) = self.height.average.as_mut() {
average.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_average()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
if let Some(sum) = self.height.sum.as_mut() {
sum.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_sum()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
if let Some(max) = self.height.max.as_mut() {
max.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_max()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
if let Some(_90p) = self.height._90p.as_mut() {
_90p.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_90p()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
if let Some(_75p) = self.height._75p.as_mut() {
_75p.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_75p()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
if let Some(median) = self.height.median.as_mut() {
median.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_median()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
if let Some(_25p) = self.height._25p.as_mut() {
_25p.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_25p()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
if let Some(_10p) = self.height._10p.as_mut() {
_10p.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_10p()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
if let Some(min) = self.height.min.as_mut() {
min.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_min()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
if let Some(last) = self.height.last.as_mut() {
last.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_last()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
if let Some(cumulative) = self.height.cumulative.as_mut() {
cumulative.forced_push_at(
height,
price
* bitcoin
.height
.unwrap_cumulative()
.into_iter()
.unwrap_get_inner(height),
exit,
)?;
}
Ok(())
})?;
self.height.safe_flush(exit)?;
self.compute_after_height(indexes, starting_indexes, exit)
}
}
@@ -0,0 +1,23 @@
mod builder;
mod from_dateindex;
mod from_height;
mod from_height_strict;
mod from_txindex;
mod ratio_from_dateindex;
mod r#type;
mod value_from_dateindex;
mod value_from_height;
mod value_from_txindex;
mod value_height;
pub use builder::*;
pub use from_dateindex::*;
pub use from_height::*;
pub use from_height_strict::*;
pub use from_txindex::*;
pub use ratio_from_dateindex::*;
use r#type::*;
pub use value_from_dateindex::*;
pub use value_from_height::*;
pub use value_from_txindex::*;
pub use value_height::*;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,13 @@
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
{
}
@@ -0,0 +1,176 @@
use std::path::Path;
use brk_core::{Bitcoin, DateIndex, Dollars, Result, Sats, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, CollectableVec, EagerVec, Format, StoredVec};
use crate::vecs::{Indexes, fetched, grouped::ComputedVecsFromDateIndex, indexes};
use super::StorableVecGeneatorOptions;
#[derive(Clone)]
pub struct ComputedValueVecsFromDateIndex {
pub sats: ComputedVecsFromDateIndex<Sats>,
pub bitcoin: ComputedVecsFromDateIndex<Bitcoin>,
pub dollars: Option<ComputedVecsFromDateIndex<Dollars>>,
}
const VERSION: Version = Version::ZERO;
impl ComputedValueVecsFromDateIndex {
pub fn forced_import(
path: &Path,
name: &str,
compute_source: bool,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
compute_dollars: bool,
) -> color_eyre::Result<Self> {
Ok(Self {
sats: ComputedVecsFromDateIndex::forced_import(
path,
name,
compute_source,
version + VERSION,
format,
options,
)?,
bitcoin: ComputedVecsFromDateIndex::forced_import(
path,
&format!("{name}_in_btc"),
true,
version + VERSION,
format,
options,
)?,
dollars: compute_dollars.then(|| {
ComputedVecsFromDateIndex::forced_import(
path,
&format!("{name}_in_usd"),
true,
version + VERSION,
format,
options,
)
.unwrap()
}),
})
}
pub fn compute_all<F>(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
fetched: Option<&fetched::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
mut compute: F,
) -> color_eyre::Result<()>
where
F: FnMut(
&mut EagerVec<DateIndex, Sats>,
&Indexer,
&indexes::Vecs,
&Indexes,
&Exit,
) -> Result<()>,
{
compute(
self.sats.dateindex.as_mut().unwrap(),
indexer,
indexes,
starting_indexes,
exit,
)?;
let dateindex: Option<&StoredVec<DateIndex, Sats>> = None;
self.compute_rest(indexer, indexes, fetched, starting_indexes, exit, dateindex)?;
Ok(())
}
pub fn compute_rest(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
fetched: Option<&fetched::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
dateindex: Option<&impl CollectableVec<DateIndex, Sats>>,
) -> color_eyre::Result<()> {
if let Some(dateindex) = dateindex {
self.sats
.compute_rest(indexes, starting_indexes, exit, Some(dateindex))?;
self.bitcoin.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_from_sats(starting_indexes.dateindex, dateindex, exit)
},
)?;
} else {
let dateindex: Option<&StoredVec<DateIndex, Sats>> = None;
self.sats
.compute_rest(indexes, starting_indexes, exit, dateindex)?;
self.bitcoin.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_from_sats(
starting_indexes.dateindex,
self.sats.dateindex.as_ref().unwrap(),
exit,
)
},
)?;
}
let dateindex_to_bitcoin = self.bitcoin.dateindex.as_ref().unwrap();
let dateindex_to_close = fetched
.as_ref()
.unwrap()
.timeindexes_to_close
.dateindex
.as_ref()
.unwrap();
if let Some(dollars) = self.dollars.as_mut() {
dollars.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_from_bitcoin(
starting_indexes.dateindex,
dateindex_to_bitcoin,
dateindex_to_close,
exit,
)
},
)?;
}
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
self.sats.vecs(),
self.bitcoin.vecs(),
self.dollars.as_ref().map_or(vec![], |v| v.vecs()),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
@@ -0,0 +1,170 @@
use std::path::Path;
use brk_core::{Bitcoin, Dollars, Height, Result, Sats, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, CollectableVec, EagerVec, Format, StoredVec};
use crate::vecs::{Indexes, fetched, indexes};
use super::{ComputedVecsFromHeight, StorableVecGeneatorOptions};
#[derive(Clone)]
pub struct ComputedValueVecsFromHeight {
pub sats: ComputedVecsFromHeight<Sats>,
pub bitcoin: ComputedVecsFromHeight<Bitcoin>,
pub dollars: Option<ComputedVecsFromHeight<Dollars>>,
}
const VERSION: Version = Version::ZERO;
impl ComputedValueVecsFromHeight {
pub fn forced_import(
path: &Path,
name: &str,
compute_source: bool,
version: Version,
format: Format,
options: StorableVecGeneatorOptions,
compute_dollars: bool,
) -> color_eyre::Result<Self> {
Ok(Self {
sats: ComputedVecsFromHeight::forced_import(
path,
name,
compute_source,
version + VERSION,
format,
options,
)?,
bitcoin: ComputedVecsFromHeight::forced_import(
path,
&format!("{name}_in_btc"),
true,
version + VERSION,
format,
options,
)?,
dollars: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&format!("{name}_in_usd"),
true,
version + VERSION,
format,
options,
)
.unwrap()
}),
})
}
pub fn compute_all<F>(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
fetched: Option<&fetched::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
mut compute: F,
) -> color_eyre::Result<()>
where
F: FnMut(
&mut EagerVec<Height, Sats>,
&Indexer,
&indexes::Vecs,
&Indexes,
&Exit,
) -> Result<()>,
{
compute(
self.sats.height.as_mut().unwrap(),
indexer,
indexes,
starting_indexes,
exit,
)?;
let height: Option<&StoredVec<Height, Sats>> = None;
self.compute_rest(indexer, indexes, fetched, starting_indexes, exit, height)?;
Ok(())
}
pub fn compute_rest(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
fetched: Option<&fetched::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
height: Option<&impl CollectableVec<Height, Sats>>,
) -> color_eyre::Result<()> {
if let Some(height) = height {
self.sats
.compute_rest(indexes, starting_indexes, exit, Some(height))?;
self.bitcoin.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_from_sats(starting_indexes.height, height, exit)
},
)?;
} else {
let height: Option<&StoredVec<Height, Sats>> = None;
self.sats
.compute_rest(indexes, starting_indexes, exit, height)?;
self.bitcoin.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_from_sats(
starting_indexes.height,
self.sats.height.as_ref().unwrap(),
exit,
)
},
)?;
}
let height_to_bitcoin = self.bitcoin.height.as_ref().unwrap();
let height_to_close = &fetched.as_ref().unwrap().chainindexes_to_close.height;
if let Some(dollars) = self.dollars.as_mut() {
dollars.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_from_bitcoin(
starting_indexes.height,
height_to_bitcoin,
height_to_close,
exit,
)
},
)?;
}
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
self.sats.vecs(),
self.bitcoin.vecs(),
self.dollars.as_ref().map_or(vec![], |v| v.vecs()),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
@@ -0,0 +1,240 @@
use std::path::Path;
use brk_core::{Bitcoin, Close, Dollars, Height, Sats, TxIndex, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{
AnyCollectableVec, BoxedAnyIterableVec, CloneableAnyIterableVec, CollectableVec, Computation,
ComputedVecFrom3, Format, LazyVecFrom1, StoredIndex, StoredVec,
};
use crate::vecs::{Indexes, fetched, indexes};
use super::{ComputedVecsFromTxindex, StorableVecGeneatorOptions};
#[derive(Clone)]
pub struct ComputedValueVecsFromTxindex {
pub sats: ComputedVecsFromTxindex<Sats>,
pub bitcoin_txindex: LazyVecFrom1<TxIndex, Bitcoin, TxIndex, Sats>,
pub bitcoin: ComputedVecsFromTxindex<Bitcoin>,
#[allow(clippy::type_complexity)]
pub dollars_txindex: Option<
ComputedVecFrom3<
TxIndex,
Dollars,
TxIndex,
Bitcoin,
TxIndex,
Height,
Height,
Close<Dollars>,
>,
>,
pub dollars: Option<ComputedVecsFromTxindex<Dollars>>,
}
const VERSION: Version = Version::ZERO;
impl ComputedValueVecsFromTxindex {
#[allow(clippy::too_many_arguments)]
pub fn forced_import(
path: &Path,
name: &str,
indexes: &indexes::Vecs,
source: Option<BoxedAnyIterableVec<TxIndex, Sats>>,
version: Version,
computation: Computation,
format: Format,
fetched: Option<&fetched::Vecs>,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
let compute_source = source.is_none();
let compute_dollars = fetched.is_some();
let name_in_btc = format!("{name}_in_btc");
let name_in_usd = format!("{name}_in_usd");
let sats = ComputedVecsFromTxindex::forced_import(
path,
name,
compute_source,
version + VERSION,
format,
options,
)?;
let bitcoin_txindex = LazyVecFrom1::init(
&name_in_btc,
version + VERSION,
source.map_or_else(|| sats.txindex.as_ref().unwrap().boxed_clone(), |s| s),
|txindex: TxIndex, iter| {
iter.next_at(txindex.unwrap_to_usize()).map(|(_, value)| {
let sats = value.into_inner();
Bitcoin::from(sats)
})
},
);
let bitcoin = ComputedVecsFromTxindex::forced_import(
path,
&name_in_btc,
false,
version + VERSION,
format,
options,
)?;
let dollars_txindex = fetched.map(|fetched| {
ComputedVecFrom3::forced_import_or_init_from_3(
computation,
path,
&name_in_usd,
version + VERSION,
format,
bitcoin_txindex.boxed_clone(),
indexes.txindex_to_height.boxed_clone(),
fetched.chainindexes_to_close.height.boxed_clone(),
|txindex: TxIndex,
txindex_to_btc_iter,
txindex_to_height_iter,
height_to_close_iter| {
let txindex = txindex.unwrap_to_usize();
txindex_to_btc_iter.next_at(txindex).and_then(|(_, value)| {
let btc = value.into_inner();
txindex_to_height_iter
.next_at(txindex)
.and_then(|(_, value)| {
let height = value.into_inner();
height_to_close_iter
.next_at(height.unwrap_to_usize())
.map(|(_, close)| *close.into_inner() * btc)
})
})
},
)
.unwrap()
});
Ok(Self {
sats,
bitcoin_txindex,
bitcoin,
dollars_txindex,
dollars: compute_dollars.then(|| {
ComputedVecsFromTxindex::forced_import(
path,
&name_in_usd,
false,
version + VERSION,
format,
options,
)
.unwrap()
}),
})
}
// pub fn compute_all<F>(
// &mut self,
// indexer: &Indexer,
// indexes: &indexes::Vecs,
// fetched: Option<&marketprice::Vecs>,
// starting_indexes: &Indexes,
// exit: &Exit,
// mut compute: F,
// ) -> color_eyre::Result<()>
// where
// F: FnMut(
// &mut EagerVec<TxIndex, Sats>,
// &Indexer,
// &indexes::Vecs,
// &Indexes,
// &Exit,
// ) -> Result<()>,
// {
// compute(
// self.sats.txindex.as_mut().unwrap(),
// indexer,
// indexes,
// starting_indexes,
// exit,
// )?;
// let txindex: Option<&StoredVec<TxIndex, Sats>> = None;
// self.compute_rest(
// indexer,
// indexes,
// fetched,
// starting_indexes,
// exit,
// txindex,
// )?;
// Ok(())
// }
pub fn compute_rest(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
txindex: Option<&impl CollectableVec<TxIndex, Sats>>,
fetched: Option<&fetched::Vecs>,
) -> color_eyre::Result<()> {
if let Some(txindex) = txindex {
self.sats
.compute_rest(indexer, indexes, starting_indexes, exit, Some(txindex))?;
} else {
let txindex: Option<&StoredVec<TxIndex, Sats>> = None;
self.sats
.compute_rest(indexer, indexes, starting_indexes, exit, txindex)?;
}
self.bitcoin.compute_rest_from_sats(
indexer,
indexes,
starting_indexes,
exit,
&self.sats,
Some(&self.bitcoin_txindex),
)?;
if let Some(dollars) = self.dollars.as_mut() {
let dollars_txindex = self.dollars_txindex.as_mut().unwrap();
dollars_txindex.compute_if_necessary(
starting_indexes.txindex,
&indexer.vecs().txindex_to_txid,
exit,
)?;
dollars.compute_rest_from_bitcoin(
indexer,
indexes,
starting_indexes,
exit,
&self.bitcoin,
Some(dollars_txindex),
fetched.as_ref().unwrap(),
)?;
}
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
self.sats.vecs(),
vec![&self.bitcoin_txindex as &dyn AnyCollectableVec],
self.bitcoin.vecs(),
self.dollars_txindex
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.dollars.as_ref().map_or(vec![], |v| v.vecs()),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
@@ -0,0 +1,126 @@
use std::path::Path;
use brk_core::{Bitcoin, Dollars, Height, Result, Sats, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, CollectableVec, EagerVec, Format, StoredVec};
use crate::vecs::{Indexes, fetched, indexes};
#[derive(Clone)]
pub struct ComputedHeightValueVecs {
pub sats: Option<EagerVec<Height, Sats>>,
pub bitcoin: EagerVec<Height, Bitcoin>,
pub dollars: Option<EagerVec<Height, Dollars>>,
}
const VERSION: Version = Version::ZERO;
impl ComputedHeightValueVecs {
pub fn forced_import(
path: &Path,
name: &str,
compute_source: bool,
version: Version,
format: Format,
compute_dollars: bool,
) -> color_eyre::Result<Self> {
Ok(Self {
sats: compute_source.then(|| {
EagerVec::forced_import(path, name, version + VERSION + Version::ZERO, format)
.unwrap()
}),
bitcoin: EagerVec::forced_import(
path,
&format!("{name}_in_btc"),
version + VERSION + Version::ZERO,
format,
)?,
dollars: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&format!("{name}_in_usd"),
version + VERSION + Version::ZERO,
format,
)
.unwrap()
}),
})
}
pub fn compute_all<F>(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
fetched: Option<&fetched::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
mut compute: F,
) -> color_eyre::Result<()>
where
F: FnMut(
&mut EagerVec<Height, Sats>,
&Indexer,
&indexes::Vecs,
&Indexes,
&Exit,
) -> Result<()>,
{
compute(
self.sats.as_mut().unwrap(),
indexer,
indexes,
starting_indexes,
exit,
)?;
let height: Option<&StoredVec<Height, Sats>> = None;
self.compute_rest(fetched, starting_indexes, exit, height)?;
Ok(())
}
pub fn compute_rest(
&mut self,
fetched: Option<&fetched::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
height: Option<&impl CollectableVec<Height, Sats>>,
) -> color_eyre::Result<()> {
if let Some(height) = height {
self.bitcoin
.compute_from_sats(starting_indexes.height, height, exit)?;
} else {
self.bitcoin.compute_from_sats(
starting_indexes.height,
self.sats.as_ref().unwrap(),
exit,
)?;
}
let height_to_bitcoin = &self.bitcoin;
let height_to_close = &fetched.as_ref().unwrap().chainindexes_to_close.height;
if let Some(dollars) = self.dollars.as_mut() {
dollars.compute_from_bitcoin(
starting_indexes.height,
height_to_bitcoin,
height_to_close,
exit,
)?;
}
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
vec![&self.bitcoin as &dyn AnyCollectableVec],
self.sats.as_ref().map_or(vec![], |v| vec![v]),
self.dollars.as_ref().map_or(vec![], |v| vec![v]),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+135
View File
@@ -0,0 +1,135 @@
use std::{fs, path::Path};
use brk_core::{DifficultyEpoch, HalvingEpoch, StoredF64, Version};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyCollectableVec, Computation, Format, VecIterator};
use super::{
Indexes,
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, StorableVecGeneatorOptions},
indexes,
};
const VERSION: Version = Version::ZERO;
#[derive(Clone)]
pub struct Vecs {
pub indexes_to_difficulty: ComputedVecsFromHeight<StoredF64>,
pub indexes_to_difficultyepoch: ComputedVecsFromDateIndex<DifficultyEpoch>,
pub indexes_to_halvingepoch: ComputedVecsFromDateIndex<HalvingEpoch>,
}
impl Vecs {
pub fn forced_import(
path: &Path,
version: Version,
_computation: Computation,
format: Format,
) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
Ok(Self {
indexes_to_difficulty: ComputedVecsFromHeight::forced_import(
path,
"difficulty",
false,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
)?,
indexes_to_difficultyepoch: ComputedVecsFromDateIndex::forced_import(
path,
"difficultyepoch",
true,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
)?,
indexes_to_halvingepoch: ComputedVecsFromDateIndex::forced_import(
path,
"halvingepoch",
true,
version + VERSION + Version::ZERO,
format,
StorableVecGeneatorOptions::default().add_last(),
)?,
})
}
pub fn compute(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> color_eyre::Result<()> {
let mut height_to_difficultyepoch_iter = indexes.height_to_difficultyepoch.into_iter();
self.indexes_to_difficultyepoch.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
let mut height_count_iter = indexes.dateindex_to_height_count.into_iter();
vec.compute_transform(
starting_indexes.dateindex,
&indexes.dateindex_to_first_height,
|(di, height, ..)| {
(
di,
height_to_difficultyepoch_iter.unwrap_get_inner(
height + (*height_count_iter.unwrap_get_inner(di) - 1),
),
)
},
exit,
)
},
)?;
let mut height_to_halvingepoch_iter = indexes.height_to_halvingepoch.into_iter();
self.indexes_to_halvingepoch.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
let mut height_count_iter = indexes.dateindex_to_height_count.into_iter();
vec.compute_transform(
starting_indexes.dateindex,
&indexes.dateindex_to_first_height,
|(di, height, ..)| {
(
di,
height_to_halvingepoch_iter.unwrap_get_inner(
height + (*height_count_iter.unwrap_get_inner(di) - 1),
),
)
},
exit,
)
},
)?;
self.indexes_to_difficulty.compute_rest(
indexes,
starting_indexes,
exit,
Some(&indexer.vecs().height_to_difficulty),
)?;
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
self.indexes_to_difficulty.vecs(),
self.indexes_to_difficultyepoch.vecs(),
self.indexes_to_halvingepoch.vecs(),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
+203
View File
@@ -0,0 +1,203 @@
use std::{fs, path::Path, thread};
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 log::info;
const VERSION: Version = Version::ONE;
#[derive(Clone)]
pub struct Vecs {
pub indexes: indexes::Vecs,
pub constants: constants::Vecs,
pub blocks: blocks::Vecs,
pub mining: mining::Vecs,
pub market: market::Vecs,
pub transactions: transactions::Vecs,
pub stateful: stateful::Vecs,
pub fetched: Option<fetched::Vecs>,
}
impl Vecs {
pub fn import(
path: &Path,
version: Version,
indexer: &Indexer,
fetch: bool,
computation: Computation,
format: Format,
) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
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())
});
Ok(Self {
blocks: blocks::Vecs::forced_import(
path,
version + VERSION + Version::ZERO,
computation,
format,
)?,
mining: mining::Vecs::forced_import(
path,
version + VERSION + Version::ZERO,
computation,
format,
)?,
constants: constants::Vecs::forced_import(
path,
version + VERSION + Version::ZERO,
computation,
format,
)?,
market: market::Vecs::forced_import(
path,
version + VERSION + Version::ZERO,
computation,
format,
)?,
stateful: stateful::Vecs::forced_import(
path,
version + VERSION + Version::ZERO,
computation,
format,
fetched.as_ref(),
)?,
transactions: transactions::Vecs::forced_import(
path,
version + VERSION + Version::ZERO,
indexer,
&indexes,
computation,
format,
fetched.as_ref(),
)?,
indexes,
fetched,
})
}
pub fn compute(
&mut self,
indexer: &Indexer,
starting_indexes: brk_indexer::Indexes,
fetcher: Option<&mut Fetcher>,
exit: &Exit,
) -> color_eyre::Result<()> {
info!("Computing indexes...");
let starting_indexes = self.indexes.compute(indexer, starting_indexes, exit)?;
info!("Computing constants...");
self.constants
.compute(indexer, &self.indexes, &starting_indexes, exit)?;
info!("Computing blocks...");
self.blocks
.compute(indexer, &self.indexes, &starting_indexes, exit)?;
info!("Computing mining...");
self.mining
.compute(indexer, &self.indexes, &starting_indexes, exit)?;
if let Some(fetched) = self.fetched.as_mut() {
info!("Computing fetched...");
fetched.compute(
indexer,
&self.indexes,
&starting_indexes,
fetcher.unwrap(),
exit,
)?;
}
info!("Computing transactions...");
self.transactions.compute(
indexer,
&self.indexes,
&starting_indexes,
self.fetched.as_ref(),
exit,
)?;
if let Some(fetched) = self.fetched.as_ref() {
info!("Computing market...");
self.market.compute(
indexer,
&self.indexes,
fetched,
&mut self.transactions,
&starting_indexes,
exit,
)?;
}
info!("Computing stateful...");
self.stateful.compute(
indexer,
&self.indexes,
&self.transactions,
self.fetched.as_ref(),
&self.market,
starting_indexes,
exit,
)?;
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
self.constants.vecs(),
self.indexes.vecs(),
self.blocks.vecs(),
self.mining.vecs(),
self.market.vecs(),
self.transactions.vecs(),
self.stateful.vecs(),
self.fetched.as_ref().map_or(vec![], |v| v.vecs()),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,258 @@
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)
})
}
}
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
[package]
name = "brk_core"
description = "The Core (Structs and Errors) of the Bitcoin Research Kit"
version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
[dependencies]
bincode = { workspace = true }
bitcoin = { workspace = true }
bitcoincore-rpc = { workspace = true }
byteview = { workspace = true }
derive_deref = { workspace = true }
fjall = { workspace = true }
jiff = { workspace = true }
log = { workspace = true }
rapidhash = "1.4.0"
rlimit = "0.10.2"
serde = { workspace = true }
serde_bytes = { workspace = true }
serde_json = { workspace = true }
zerocopy = { workspace = true }
zerocopy-derive = { workspace = true }
[package.metadata.cargo-machete]
ignored = ["serde_bytes"]
+34
View File
@@ -0,0 +1,34 @@
# BRK Core
<p align="left">
<a href="https://github.com/bitcoinresearchkit/brk">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/bitcoinresearchkit/brk?style=social">
</a>
<a href="https://github.com/bitcoinresearchkit/brk/blob/main/LICENSE.md">
<img src="https://img.shields.io/crates/l/brk" alt="License" />
</a>
<a href="https://crates.io/crates/brk_core">
<img src="https://img.shields.io/crates/v/brk_core" alt="Version" />
</a>
<a href="https://docs.rs/brk_core">
<img src="https://img.shields.io/docsrs/brk_core" alt="Documentation" />
</a>
<img src="https://img.shields.io/crates/size/brk_core" alt="Size" />
<a href="https://deps.rs/crate/brk_core">
<img src="https://deps.rs/crate/brk_core/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
<img src="https://img.shields.io/badge/nostr-purple?link=https%3A%2F%2Fprimal.net%2Fp%2Fnprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6" alt="Nostr" />
</a>
<a href="https://bsky.app/profile/bitcoinresearchkit.org">
<img src="https://img.shields.io/badge/bluesky-blue?link=https%3A%2F%2Fbsky.app%2Fprofile%2Fbitcoinresearchkit.org" alt="Bluesky" />
</a>
<a href="https://x.com/brkdotorg">
<img src="https://img.shields.io/badge/x.com-black" alt="X" />
</a>
</p>
A list of structs that are used throughout the project as units, think of `Date`, `Height`, `Sats`, `Txindex` or anything that can be either a key and/or a value of a dataset.
+140
View File
@@ -0,0 +1,140 @@
use std::{
fmt::{self, Debug},
io, result, time,
};
use crate::Version;
pub type Result<T, E = Error> = result::Result<T, E>;
#[derive(Debug)]
pub enum Error {
IO(io::Error),
SerdeJson(serde_json::Error),
Jiff(jiff::Error),
Fjall(fjall::Error),
SystemTimeError(time::SystemTimeError),
ZeroCopyError,
BincodeEncodeError(bincode::error::EncodeError),
BincodeDecodeError(bincode::error::DecodeError),
WrongEndian,
DifferentVersion { found: Version, expected: Version },
MmapsVecIsTooSmall,
IndexTooHigh,
EmptyVec,
IndexTooLow,
ExpectFileToHaveIndex,
ExpectVecToHaveIndex,
FailedKeyTryIntoUsize,
UnsupportedUnflushedState,
RangeFromAfterTo(usize, usize),
DifferentCompressionMode,
WrongLength,
WrongAddressType,
UnindexableDate,
String(&'static str),
}
impl From<time::SystemTimeError> for Error {
fn from(value: time::SystemTimeError) -> Self {
Self::SystemTimeError(value)
}
}
impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Self::IO(value)
}
}
impl From<jiff::Error> for Error {
fn from(value: jiff::Error) -> Self {
Self::Jiff(value)
}
}
impl From<fjall::Error> for Error {
fn from(value: fjall::Error) -> Self {
Self::Fjall(value)
}
}
impl<A, B, C> From<zerocopy::error::ConvertError<A, B, C>> for Error {
fn from(_: zerocopy::error::ConvertError<A, B, C>) -> Self {
Self::ZeroCopyError
}
}
impl<A, B> From<zerocopy::error::SizeError<A, B>> for Error {
fn from(_: zerocopy::error::SizeError<A, B>) -> Self {
Self::ZeroCopyError
}
}
impl From<serde_json::Error> for Error {
fn from(error: serde_json::Error) -> Self {
Self::SerdeJson(error)
}
}
impl From<bincode::error::DecodeError> for Error {
fn from(error: bincode::error::DecodeError) -> Self {
Self::BincodeDecodeError(error)
}
}
impl From<bincode::error::EncodeError> for Error {
fn from(error: bincode::error::EncodeError) -> Self {
Self::BincodeEncodeError(error)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::IO(error) => Debug::fmt(&error, f),
Error::SystemTimeError(error) => Debug::fmt(&error, f),
Error::SerdeJson(error) => Debug::fmt(&error, f),
Error::Jiff(error) => Debug::fmt(&error, f),
Error::Fjall(error) => Debug::fmt(&error, f),
Error::BincodeDecodeError(error) => Debug::fmt(&error, f),
Error::BincodeEncodeError(error) => Debug::fmt(&error, f),
Error::ZeroCopyError => write!(f, "ZeroCopy error"),
Error::WrongEndian => write!(f, "Wrong endian"),
Error::DifferentVersion { found, expected } => {
write!(
f,
"Different version; found: {found:?}, expected: {expected:?}"
)
}
Error::MmapsVecIsTooSmall => write!(f, "Mmaps vec is too small"),
Error::IndexTooHigh => write!(f, "Index too high"),
Error::IndexTooLow => write!(f, "Index too low"),
Error::ExpectFileToHaveIndex => write!(f, "Expect file to have index"),
Error::ExpectVecToHaveIndex => write!(f, "Expect vec to have index"),
Error::FailedKeyTryIntoUsize => write!(f, "Failed to convert key to usize"),
Error::UnsupportedUnflushedState => {
write!(
f,
"Unsupported unflush state, please flush before using this function"
)
}
Error::RangeFromAfterTo(from, to) => write!(f, "Range, from {from} is after to {to}"),
Error::DifferentCompressionMode => write!(f, "Different compression mode chosen"),
Error::EmptyVec => write!(f, "The Vec is empty, maybe wait for a bit"),
Error::WrongLength => write!(f, "Wrong length"),
Error::WrongAddressType => write!(f, "Wrong address type"),
Error::UnindexableDate => write!(
f,
"Date cannot be indexed, must be 2009-01-03, 2009-01-09 or greater"
),
Error::String(s) => write!(f, "{s}"),
}
}
}
impl std::error::Error for Error {}
+5
View File
@@ -0,0 +1,5 @@
mod error;
mod value;
pub use error::*;
pub use value::*;
+39
View File
@@ -0,0 +1,39 @@
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,
}
}
}
+9
View File
@@ -0,0 +1,9 @@
#![doc = include_str!("../README.md")]
mod enums;
mod structs;
mod utils;
pub use enums::*;
pub use structs::*;
pub use utils::*;
@@ -0,0 +1,89 @@
use std::ops::Add;
use byteview::ByteView;
use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::Error;
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct AddressIndex(u32);
impl AddressIndex {
pub const BYTES: usize = size_of::<Self>();
pub fn increment(&mut self) {
self.0 += 1;
}
pub fn incremented(self) -> Self {
Self(self.0 + 1)
}
}
impl From<u32> for AddressIndex {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<u64> for AddressIndex {
fn from(value: u64) -> Self {
Self(value as u32)
}
}
impl From<AddressIndex> for u64 {
fn from(value: AddressIndex) -> Self {
value.0 as u64
}
}
impl From<usize> for AddressIndex {
fn from(value: usize) -> Self {
Self(value as u32)
}
}
impl From<AddressIndex> for usize {
fn from(value: AddressIndex) -> Self {
value.0 as usize
}
}
impl From<ByteView> for AddressIndex {
fn from(value: ByteView) -> Self {
Ok(Self::read_from_bytes(&value)?)
}
}
impl From<AddressIndex> for ByteView {
fn from(value: AddressIndex) -> Self {
Self::new(value.as_bytes())
}
}
impl Add<usize> for AddressIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(self.0 + rhs as u32)
}
}
impl Add<AddressIndex> for AddressIndex {
type Output = Self;
fn add(self, rhs: AddressIndex) -> Self::Output {
Self(self.0 + rhs.0)
}
}
@@ -0,0 +1,27 @@
use byteview::ByteView;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::Error;
use super::{AddressIndex, Outputindex};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Immutable, IntoBytes, KnownLayout, FromBytes,
)]
#[repr(C)]
pub struct AddressIndexOutputIndex {
addressindex: AddressIndex,
_padding: u32,
outputindex: Outputindex,
}
impl From<ByteView> for AddressIndexOutputIndex {
fn from(value: ByteView) -> Self {
Ok(Self::read_from_bytes(&value)?)
}
}
impl From<AddressIndexOutputIndex> for ByteView {
fn from(value: AddressIndexOutputIndex) -> Self {
Self::new(value.as_bytes())
}
}
@@ -0,0 +1,48 @@
use serde::Serialize;
use zerocopy_derive::{Immutable, IntoBytes, KnownLayout, TryFromBytes};
use super::OutputType;
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
TryFromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
#[repr(u8)]
pub enum AddressType {
P2PK65,
P2PK33,
P2PKH,
P2SH,
P2WPKH,
P2WSH,
P2TR,
P2A,
}
impl From<OutputType> for AddressType {
fn from(value: OutputType) -> Self {
match value {
OutputType::P2A => Self::P2A,
OutputType::P2PK33 => Self::P2PK33,
OutputType::P2PK65 => Self::P2PK65,
OutputType::P2PKH => Self::P2PKH,
OutputType::P2SH => Self::P2SH,
OutputType::P2TR => Self::P2TR,
OutputType::P2WPKH => Self::P2WPKH,
OutputType::P2WSH => Self::P2WSH,
OutputType::Empty | OutputType::OpReturn | OutputType::P2MS | OutputType::Unknown => {
unreachable!()
}
}
}
}
+448
View File
@@ -0,0 +1,448 @@
use std::fmt;
use bitcoin::{
Address, Network, ScriptBuf,
hex::{Case, DisplayHex},
opcodes,
script::Builder,
};
use derive_deref::{Deref, DerefMut};
use serde::{Serialize, Serializer};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::Error;
use super::OutputType;
#[derive(Debug, PartialEq, Eq)]
pub enum AddressBytes {
P2PK65(P2PK65Bytes),
P2PK33(P2PK33Bytes),
P2PKH(P2PKHBytes),
P2SH(P2SHBytes),
P2WPKH(P2WPKHBytes),
P2WSH(P2WSHBytes),
P2TR(P2TRBytes),
P2A(P2ABytes),
}
impl AddressBytes {
pub fn as_slice(&self) -> &[u8] {
match self {
AddressBytes::P2PK65(bytes) => &bytes[..],
AddressBytes::P2PK33(bytes) => &bytes[..],
AddressBytes::P2PKH(bytes) => &bytes[..],
AddressBytes::P2SH(bytes) => &bytes[..],
AddressBytes::P2WPKH(bytes) => &bytes[..],
AddressBytes::P2WSH(bytes) => &bytes[..],
AddressBytes::P2TR(bytes) => &bytes[..],
AddressBytes::P2A(bytes) => &bytes[..],
}
}
}
impl TryFrom<(&ScriptBuf, OutputType)> for AddressBytes {
type Error = Error;
fn try_from(tuple: (&ScriptBuf, OutputType)) -> Result<Self, Self::Error> {
let (script, outputtype) = tuple;
match outputtype {
OutputType::P2PK65 => {
let bytes = script.as_bytes();
let bytes = match bytes.len() {
67 => &bytes[1..66],
_ => {
dbg!(bytes);
return Err(Error::WrongLength);
}
};
Ok(Self::P2PK65(P2PK65Bytes(U8x65::from(bytes))))
}
OutputType::P2PK33 => {
let bytes = script.as_bytes();
let bytes = match bytes.len() {
35 => &bytes[1..34],
_ => {
dbg!(bytes);
return Err(Error::WrongLength);
}
};
Ok(Self::P2PK33(P2PK33Bytes(U8x33::from(bytes))))
}
OutputType::P2PKH => {
let bytes = &script.as_bytes()[3..23];
Ok(Self::P2PKH(P2PKHBytes(U8x20::from(bytes))))
}
OutputType::P2SH => {
let bytes = &script.as_bytes()[2..22];
Ok(Self::P2SH(P2SHBytes(U8x20::from(bytes))))
}
OutputType::P2WPKH => {
let bytes = &script.as_bytes()[2..];
Ok(Self::P2WPKH(P2WPKHBytes(U8x20::from(bytes))))
}
OutputType::P2WSH => {
let bytes = &script.as_bytes()[2..];
Ok(Self::P2WSH(P2WSHBytes(U8x32::from(bytes))))
}
OutputType::P2TR => {
let bytes = &script.as_bytes()[2..];
Ok(Self::P2TR(P2TRBytes(U8x32::from(bytes))))
}
OutputType::P2A => {
let bytes = &script.as_bytes()[2..];
Ok(Self::P2A(P2ABytes(U8x2::from(bytes))))
}
OutputType::P2MS => Err(Error::WrongAddressType),
OutputType::Unknown => Err(Error::WrongAddressType),
OutputType::Empty => Err(Error::WrongAddressType),
OutputType::OpReturn => Err(Error::WrongAddressType),
}
}
}
#[derive(Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes)]
pub struct P2PK65Bytes(U8x65);
impl fmt::Display for P2PK65Bytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_hex_string(Case::Lower))
}
}
impl Serialize for P2PK65Bytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&self.to_string())
}
}
impl From<P2PK65Bytes> for AddressBytes {
fn from(value: P2PK65Bytes) -> Self {
Self::P2PK65(value)
}
}
#[derive(Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes)]
pub struct P2PK33Bytes(U8x33);
impl fmt::Display for P2PK33Bytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_hex_string(Case::Lower))
}
}
impl Serialize for P2PK33Bytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&self.to_string())
}
}
impl From<P2PK33Bytes> for AddressBytes {
fn from(value: P2PK33Bytes) -> Self {
Self::P2PK33(value)
}
}
#[derive(Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes)]
pub struct P2PKHBytes(U8x20);
impl fmt::Display for P2PKHBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let script = Builder::new()
.push_opcode(opcodes::all::OP_DUP)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(*self.0)
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script();
let address = Address::from_script(&script, Network::Bitcoin).unwrap();
write!(f, "{}", address)
}
}
impl Serialize for P2PKHBytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&self.to_string())
}
}
impl From<P2PKHBytes> for AddressBytes {
fn from(value: P2PKHBytes) -> Self {
Self::P2PKH(value)
}
}
#[derive(Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes)]
pub struct P2SHBytes(U8x20);
impl fmt::Display for P2SHBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let script = Builder::new()
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(*self.0)
.push_opcode(opcodes::all::OP_EQUAL)
.into_script();
let address = Address::from_script(&script, Network::Bitcoin).unwrap();
write!(f, "{}", address)
}
}
impl Serialize for P2SHBytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&self.to_string())
}
}
impl From<P2SHBytes> for AddressBytes {
fn from(value: P2SHBytes) -> Self {
Self::P2SH(value)
}
}
#[derive(Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes)]
pub struct P2WPKHBytes(U8x20);
impl fmt::Display for P2WPKHBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let script = Builder::new().push_int(0).push_slice(*self.0).into_script();
let address = Address::from_script(&script, Network::Bitcoin).unwrap();
write!(f, "{}", address)
}
}
impl Serialize for P2WPKHBytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&self.to_string())
}
}
impl From<P2WPKHBytes> for AddressBytes {
fn from(value: P2WPKHBytes) -> Self {
Self::P2WPKH(value)
}
}
#[derive(Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes)]
pub struct P2WSHBytes(U8x32);
impl fmt::Display for P2WSHBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let script = Builder::new().push_int(0).push_slice(*self.0).into_script();
let address = Address::from_script(&script, Network::Bitcoin).unwrap();
write!(f, "{}", address)
}
}
impl Serialize for P2WSHBytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&self.to_string())
}
}
impl From<P2WSHBytes> for AddressBytes {
fn from(value: P2WSHBytes) -> Self {
Self::P2WSH(value)
}
}
#[derive(Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes)]
pub struct P2TRBytes(U8x32);
impl fmt::Display for P2TRBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let script = Builder::new().push_int(1).push_slice(*self.0).into_script();
let address = Address::from_script(&script, Network::Bitcoin).unwrap();
write!(f, "{}", address)
}
}
impl Serialize for P2TRBytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&self.to_string())
}
}
impl From<P2TRBytes> for AddressBytes {
fn from(value: P2TRBytes) -> Self {
Self::P2TR(value)
}
}
#[derive(Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes)]
pub struct P2ABytes(U8x2);
impl fmt::Display for P2ABytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let script = Builder::new().push_int(1).push_slice(*self.0).into_script();
let address = Address::from_script(&script, Network::Bitcoin).unwrap();
write!(f, "{}", address)
}
}
impl Serialize for P2ABytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&self.to_string())
}
}
impl From<P2ABytes> for AddressBytes {
fn from(value: P2ABytes) -> Self {
Self::P2A(value)
}
}
#[derive(
Debug,
Clone,
Deref,
DerefMut,
PartialEq,
Eq,
Immutable,
IntoBytes,
KnownLayout,
FromBytes,
Serialize,
)]
pub struct U8x2([u8; 2]);
impl From<&[u8]> for U8x2 {
fn from(slice: &[u8]) -> Self {
let mut arr = [0; 2];
arr.copy_from_slice(slice);
Self(arr)
}
}
#[derive(
Debug,
Clone,
Deref,
DerefMut,
PartialEq,
Eq,
Immutable,
IntoBytes,
KnownLayout,
FromBytes,
Serialize,
)]
pub struct U8x20([u8; 20]);
impl From<&[u8]> for U8x20 {
fn from(slice: &[u8]) -> Self {
let mut arr = [0; 20];
arr.copy_from_slice(slice);
Self(arr)
}
}
#[derive(
Debug,
Clone,
Deref,
DerefMut,
PartialEq,
Eq,
Immutable,
IntoBytes,
KnownLayout,
FromBytes,
Serialize,
)]
pub struct U8x32([u8; 32]);
impl From<&[u8]> for U8x32 {
fn from(slice: &[u8]) -> Self {
let mut arr = [0; 32];
arr.copy_from_slice(slice);
Self(arr)
}
}
#[derive(
Debug,
Clone,
Deref,
DerefMut,
PartialEq,
Eq,
Immutable,
IntoBytes,
KnownLayout,
FromBytes,
Serialize,
)]
pub struct U8x33(#[serde(with = "serde_bytes")] [u8; 33]);
impl From<&[u8]> for U8x33 {
fn from(slice: &[u8]) -> Self {
let mut arr = [0; 33];
arr.copy_from_slice(slice);
Self(arr)
}
}
#[derive(
Debug,
Clone,
Deref,
DerefMut,
PartialEq,
Eq,
Immutable,
IntoBytes,
KnownLayout,
FromBytes,
Serialize,
)]
pub struct U8x64(#[serde(with = "serde_bytes")] [u8; 64]);
impl From<&[u8]> for U8x64 {
fn from(slice: &[u8]) -> Self {
let mut arr = [0; 64];
arr.copy_from_slice(slice);
Self(arr)
}
}
#[derive(
Debug,
Clone,
Deref,
DerefMut,
PartialEq,
Eq,
Immutable,
IntoBytes,
KnownLayout,
FromBytes,
Serialize,
)]
pub struct U8x65(#[serde(with = "serde_bytes")] [u8; 65]);
impl From<&[u8]> for U8x65 {
fn from(slice: &[u8]) -> Self {
let mut arr = [0; 65];
arr.copy_from_slice(slice);
Self(arr)
}
}
@@ -0,0 +1,58 @@
use std::hash::Hasher;
use byteview::ByteView;
use derive_deref::Deref;
use zerocopy::{FromBytes, IntoBytes};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use super::{AddressBytes, OutputType};
#[derive(
Debug,
Deref,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
)]
pub struct AddressBytesHash([u8; 8]);
impl From<(&AddressBytes, OutputType)> for AddressBytesHash {
fn from((address_bytes, outputtype): (&AddressBytes, OutputType)) -> Self {
let mut hasher = rapidhash::RapidHasher::default();
hasher.write(address_bytes.as_slice());
let mut slice = hasher.finish().to_le_bytes();
slice[0] = slice[0].wrapping_add(outputtype as u8);
Self(slice)
}
}
impl From<[u8; 8]> for AddressBytesHash {
fn from(value: [u8; 8]) -> Self {
Self(value)
}
}
impl From<ByteView> for AddressBytesHash {
fn from(value: ByteView) -> Self {
Self::read_from_bytes(&value).unwrap()
}
}
impl From<&AddressBytesHash> for ByteView {
fn from(value: &AddressBytesHash) -> Self {
Self::new(value.as_bytes())
}
}
impl From<AddressBytesHash> for ByteView {
fn from(value: AddressBytesHash) -> Self {
Self::from(&value)
}
}
+118
View File
@@ -0,0 +1,118 @@
use std::{
cmp::Ordering,
ops::{Add, Div, Mul},
};
use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::{Sats, StoredF64};
#[derive(Debug, Default, Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout, Serialize)]
pub struct Bitcoin(f64);
impl Add for Bitcoin {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::from(Sats::from(self) + Sats::from(rhs))
}
}
impl Mul for Bitcoin {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self::from(Sats::from(self) * Sats::from(rhs))
}
}
impl Mul<usize> for Bitcoin {
type Output = Self;
fn mul(self, rhs: usize) -> Self::Output {
Self::from(Sats::from(self) * rhs)
}
}
impl Div<Bitcoin> for Bitcoin {
type Output = StoredF64;
fn div(self, rhs: Bitcoin) -> Self::Output {
StoredF64::from(self.0 / rhs.0)
// Self::from(Sats::from(self) / Sats::from(rhs))
}
}
impl Div<usize> for Bitcoin {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self::from(Sats::from(self) / rhs)
}
}
impl From<Sats> for Bitcoin {
fn from(value: Sats) -> Self {
Self(f64::from(value) / (f64::from(Sats::ONE_BTC)))
}
}
impl From<f64> for Bitcoin {
fn from(value: f64) -> Self {
Self(value)
}
}
impl From<StoredF64> for Bitcoin {
fn from(value: StoredF64) -> Self {
Self(*value)
}
}
impl From<Bitcoin> for f64 {
fn from(value: Bitcoin) -> Self {
value.0
}
}
impl From<usize> for Bitcoin {
fn from(value: usize) -> Self {
Self(value as f64)
}
}
impl PartialEq for Bitcoin {
fn eq(&self, other: &Self) -> bool {
match (self.0.is_nan(), other.0.is_nan()) {
(true, true) => true,
(true, false) => false,
(false, true) => false,
(false, false) => self.0 == other.0,
}
}
}
impl Eq for Bitcoin {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl PartialOrd for Bitcoin {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for Bitcoin {
fn cmp(&self, other: &Self) -> Ordering {
match (self.0.is_nan(), other.0.is_nan()) {
(true, true) => Ordering::Equal,
(true, false) => Ordering::Less,
(false, true) => Ordering::Greater,
(false, false) => self.0.partial_cmp(&other.0).unwrap(),
}
}
}
impl CheckedSub<usize> for Bitcoin {
fn checked_sub(self, rhs: usize) -> Option<Self> {
Some(Self(self.0 - rhs as f64))
}
}
+52
View File
@@ -0,0 +1,52 @@
use std::{fmt, mem};
use bitcoin::hashes::Hash;
use bitcoincore_rpc::{Client, RpcApi};
use derive_deref::Deref;
use serde::{Serialize, Serializer};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use super::Height;
#[derive(Debug, Deref, Clone, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes)]
pub struct BlockHash([u8; 32]);
impl From<bitcoin::BlockHash> for BlockHash {
fn from(value: bitcoin::BlockHash) -> Self {
unsafe { mem::transmute(value) }
}
}
impl From<BlockHash> for bitcoin::BlockHash {
fn from(value: BlockHash) -> Self {
unsafe { mem::transmute(value) }
}
}
impl From<&BlockHash> for bitcoin::BlockHash {
fn from(value: &BlockHash) -> Self {
bitcoin::BlockHash::from_slice(&value.0).unwrap()
}
}
impl TryFrom<(&Client, Height)> for BlockHash {
type Error = bitcoincore_rpc::Error;
fn try_from((rpc, height): (&Client, Height)) -> Result<Self, Self::Error> {
Ok(Self::from(rpc.get_block_hash(u64::from(height))?))
}
}
impl fmt::Display for BlockHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", bitcoin::BlockHash::from(self))
}
}
impl Serialize for BlockHash {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
@@ -0,0 +1,54 @@
use byteview::ByteView;
use derive_deref::Deref;
use zerocopy::{FromBytes, IntoBytes};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::copy_first_8bytes;
use super::BlockHash;
#[derive(
Debug,
Deref,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
)]
pub struct BlockHashPrefix([u8; 8]);
impl From<BlockHash> for BlockHashPrefix {
fn from(value: BlockHash) -> Self {
Self::from(&value)
}
}
impl From<&BlockHash> for BlockHashPrefix {
fn from(value: &BlockHash) -> Self {
Self(copy_first_8bytes(&value[..]).unwrap())
}
}
impl From<ByteView> for BlockHashPrefix {
fn from(value: ByteView) -> Self {
Self::read_from_bytes(&value).unwrap()
}
}
impl From<&BlockHashPrefix> for ByteView {
fn from(value: &BlockHashPrefix) -> Self {
Self::new(value.as_bytes())
}
}
impl From<BlockHashPrefix> for ByteView {
fn from(value: BlockHashPrefix) -> Self {
Self::from(&value)
}
}
+151
View File
@@ -0,0 +1,151 @@
use std::ops::{Add, Div, Mul};
use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::Dollars;
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct Cents(i64);
impl Cents {
pub const fn mint(value: i64) -> Self {
Self(value)
}
}
impl From<Dollars> for Cents {
fn from(value: Dollars) -> Self {
Self((*value * 100.0).round() as i64)
}
}
impl From<Cents> for f64 {
fn from(value: Cents) -> Self {
value.0 as f64
}
}
impl From<i64> for Cents {
fn from(value: i64) -> Self {
Self(value)
}
}
impl From<u64> for Cents {
fn from(value: u64) -> Self {
Self(value as i64)
}
}
impl From<Cents> for usize {
fn from(value: Cents) -> Self {
if value.0 < 0 {
panic!()
}
value.0 as usize
}
}
impl From<usize> for Cents {
fn from(value: usize) -> Self {
Self(value as i64)
}
}
impl From<Cents> for i64 {
fn from(value: Cents) -> Self {
value.0
}
}
impl From<Cents> for u64 {
fn from(value: Cents) -> Self {
if value.0 < 0 {
panic!("Shouldn't convert neg cents to u64")
}
value.0 as u64
}
}
impl Add for Cents {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Div<Cents> for Cents {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0)
}
}
impl Div<usize> for Cents {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs as i64)
}
}
impl From<u128> for Cents {
fn from(value: u128) -> Self {
if value > i64::MAX as u128 {
panic!("u128 bigger than i64")
}
Self(value as i64)
}
}
impl From<Cents> for u128 {
fn from(value: Cents) -> Self {
if value.0 < 0 {
panic!("Shouldn't convert neg cents to u128")
}
value.0 as u128
}
}
impl Mul<Cents> for Cents {
type Output = Cents;
fn mul(self, rhs: Cents) -> Self::Output {
Self(self.0.checked_mul(rhs.0).unwrap())
}
}
impl Mul<i64> for Cents {
type Output = Cents;
fn mul(self, rhs: i64) -> Self::Output {
Self(self.0 * rhs)
}
}
impl Mul<usize> for Cents {
type Output = Cents;
fn mul(self, rhs: usize) -> Self::Output {
Self(self.0 * rhs as i64)
}
}
impl CheckedSub for Cents {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Cents::from)
}
}
+94
View File
@@ -0,0 +1,94 @@
use jiff::{Span, civil::Date as Date_, tz::TimeZone};
use serde::{Serialize, Serializer};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use super::{DateIndex, Timestamp};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromBytes, Immutable, IntoBytes, KnownLayout,
)]
pub struct Date(u32);
impl Date {
pub const INDEX_ZERO: Self = Self(20090103);
pub const INDEX_ZERO_: Date_ = Date_::constant(2009, 1, 3);
pub const INDEX_ONE: Self = Self(20090109);
pub const INDEX_ONE_: Date_ = Date_::constant(2009, 1, 9);
pub const MIN_RATIO: Self = Self(20120101);
pub fn new(year: u16, month: u8, day: u8) -> Self {
Self(year as u32 * 1_00_00 + month as u32 * 1_00 + day as u32)
}
pub fn year(&self) -> u16 {
(self.0 / 1_00_00) as u16
}
pub fn month(&self) -> u8 {
((self.0 % 1_00_00) / 1_00) as u8
}
pub fn day(&self) -> u8 {
(self.0 % 1_00) as u8
}
}
impl Default for Date {
fn default() -> Self {
Self::INDEX_ZERO
}
}
impl From<Date_> for Date {
fn from(value: Date_) -> Self {
Self::new(value.year() as u16, value.month() as u8, value.day() as u8)
}
}
impl From<Date> for Date_ {
fn from(value: Date) -> Self {
Self::new(value.year() as i16, value.month() as i8, value.day() as i8).unwrap()
}
}
impl From<Timestamp> for Date {
fn from(value: Timestamp) -> Self {
Self::from(Date_::from(
jiff::Timestamp::from(value).to_zoned(TimeZone::UTC),
))
}
}
impl From<DateIndex> for Date {
fn from(value: DateIndex) -> Self {
if value == DateIndex::default() {
Date::INDEX_ZERO
} else {
Self::from(
Self::INDEX_ONE_
.checked_add(Span::new().days(i64::from(value) - 1))
.unwrap(),
)
}
}
}
impl std::fmt::Display for Date {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&format!(
"{}-{:0>2}-{:0>2}",
self.year(),
self.month(),
self.day()
))
}
}
impl Serialize for Date {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
+95
View File
@@ -0,0 +1,95 @@
use std::{
fmt,
ops::{Add, Rem},
};
use serde::Serialize;
// use color_eyre::eyre::eyre;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::{CheckedSub, Error};
use super::Date;
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct DateIndex(u16);
impl DateIndex {
pub const BYTES: usize = size_of::<Self>();
}
impl From<DateIndex> for usize {
fn from(value: DateIndex) -> Self {
value.0 as usize
}
}
impl From<usize> for DateIndex {
fn from(value: usize) -> Self {
Self(value as u16)
}
}
impl From<DateIndex> for i64 {
fn from(value: DateIndex) -> Self {
value.0 as i64
}
}
impl Add<usize> for DateIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(self.0 + rhs as u16)
}
}
impl TryFrom<Date> for DateIndex {
type Error = Error;
fn try_from(value: Date) -> Result<Self, Self::Error> {
let value_ = jiff::civil::Date::from(value);
if value_ < Date::INDEX_ZERO_ {
Err(Error::UnindexableDate)
} else if value == Date::INDEX_ZERO {
Ok(Self(0))
} else if value_ < Date::INDEX_ONE_ {
Err(Error::UnindexableDate)
} else if value == Date::INDEX_ONE {
Ok(Self(1))
} else {
Ok(Self(Date::INDEX_ONE_.until(value_)?.get_days() as u16 + 1))
}
}
}
impl CheckedSub for DateIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
}
impl Rem<usize> for DateIndex {
type Output = Self;
fn rem(self, rhs: usize) -> Self::Output {
Self(self.0 % rhs as u16)
}
}
impl fmt::Display for DateIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
@@ -0,0 +1,85 @@
use std::{fmt::Debug, ops::Add};
use serde::{Deserialize, Serialize};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::{Date, DateIndex, YearIndex};
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
Serialize,
Deserialize,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
)]
pub struct DecadeIndex(u8);
impl From<u8> for DecadeIndex {
fn from(value: u8) -> Self {
Self(value)
}
}
impl From<usize> for DecadeIndex {
fn from(value: usize) -> Self {
Self(value as u8)
}
}
impl From<DecadeIndex> for usize {
fn from(value: DecadeIndex) -> Self {
value.0 as usize
}
}
impl Add<usize> for DecadeIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self::from(self.0 + rhs as u8)
}
}
impl From<DateIndex> for DecadeIndex {
fn from(value: DateIndex) -> Self {
Self::from(Date::from(value))
}
}
impl From<Date> for DecadeIndex {
fn from(value: Date) -> Self {
let year = value.year();
if year < 2000 {
panic!("unsupported")
}
Self(((year - 2000) / 10) as u8)
}
}
impl CheckedSub for DecadeIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
}
impl From<YearIndex> for DecadeIndex {
fn from(value: YearIndex) -> Self {
let v = usize::from(value);
if v == 0 {
Self(0)
} else {
Self((((v - 1) / 10) + 1) as u8)
}
}
}
@@ -0,0 +1,82 @@
use std::{
fmt::Debug,
ops::{Add, Div},
};
use serde::{Deserialize, Serialize};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::Height;
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
Serialize,
Deserialize,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
)]
pub struct DifficultyEpoch(u16);
impl From<u16> for DifficultyEpoch {
fn from(value: u16) -> Self {
Self(value)
}
}
impl From<usize> for DifficultyEpoch {
fn from(value: usize) -> Self {
Self(value as u16)
}
}
impl From<DifficultyEpoch> for usize {
fn from(value: DifficultyEpoch) -> Self {
value.0 as usize
}
}
impl Add for DifficultyEpoch {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::from(self.0 + rhs.0)
}
}
impl Add<usize> for DifficultyEpoch {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self::from(self.0 + rhs as u16)
}
}
impl Div<usize> for DifficultyEpoch {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self::from(self.0 as usize / rhs)
}
}
impl From<Height> for DifficultyEpoch {
fn from(value: Height) -> Self {
Self((u32::from(value) / 2016) as u16)
}
}
impl CheckedSub for DifficultyEpoch {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
}
+336
View File
@@ -0,0 +1,336 @@
use std::{
cmp::Ordering,
f64,
ops::{Add, AddAssign, Div, Mul},
};
use bincode::{Decode, Encode};
use byteview::ByteView;
use derive_deref::Deref;
use serde::{Deserialize, Serialize};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::{CheckedSub, copy_first_8bytes};
use super::{Bitcoin, Cents, Close, High, Sats, StoredF32, StoredF64};
#[derive(
Debug,
Default,
Clone,
Copy,
Deref,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
Deserialize,
Encode,
Decode,
)]
pub struct Dollars(f64);
impl Dollars {
pub const ZERO: Self = Self(0.0);
pub const NAN: Self = Self(f64::NAN);
pub const fn mint(dollars: f64) -> Self {
Self(dollars)
}
}
impl From<f32> for Dollars {
fn from(value: f32) -> Self {
Self(value as f64)
}
}
impl From<f64> for Dollars {
fn from(value: f64) -> Self {
Self(value)
}
}
impl From<Cents> for Dollars {
fn from(value: Cents) -> Self {
Self(f64::from(value) / 100.0)
}
}
impl From<Dollars> for f32 {
fn from(value: Dollars) -> Self {
value.0 as f32
}
}
impl From<Dollars> for f64 {
fn from(value: Dollars) -> Self {
value.0
}
}
impl From<Close<Dollars>> for Dollars {
fn from(value: Close<Dollars>) -> Self {
Self(value.0)
}
}
impl From<High<Dollars>> for Dollars {
fn from(value: High<Dollars>) -> Self {
Self(value.0)
}
}
impl From<usize> for Dollars {
fn from(value: usize) -> Self {
Self(value as f64)
}
}
impl Add for Dollars {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::from(Cents::from(self) + Cents::from(rhs))
}
}
impl Div<Dollars> for Dollars {
type Output = StoredF64;
fn div(self, rhs: Dollars) -> Self::Output {
if self.is_nan() || rhs == Dollars::ZERO {
StoredF64::NAN
} else {
StoredF64::from(f64::from(self) / f64::from(rhs))
}
}
}
impl Div<Close<Dollars>> for Dollars {
type Output = StoredF64;
fn div(self, rhs: Close<Dollars>) -> Self::Output {
if self.is_nan() || *rhs == Dollars::ZERO {
StoredF64::NAN
} else {
StoredF64::from(f64::from(self) / f64::from(*rhs))
}
}
}
impl Div<Dollars> for Close<Dollars> {
type Output = StoredF64;
fn div(self, rhs: Dollars) -> Self::Output {
if self.is_nan() || rhs == Dollars::ZERO {
StoredF64::NAN
} else {
StoredF64::from(f64::from(*self) / f64::from(rhs))
}
}
}
impl Div<usize> for Dollars {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
if self.is_nan() || rhs == 0 {
Dollars::NAN
} else {
Self::from(Cents::from(self) / rhs)
}
}
}
impl Div<Bitcoin> for Dollars {
type Output = Self;
fn div(self, rhs: Bitcoin) -> Self::Output {
if self.is_nan() {
self
} else {
Self(f64::from(self) / f64::from(rhs))
}
}
}
impl Mul<Dollars> for Dollars {
type Output = Self;
fn mul(self, rhs: Dollars) -> Self::Output {
Self::from(Cents::from(self) * Cents::from(rhs))
}
}
impl Mul<Close<Dollars>> for Dollars {
type Output = Self;
fn mul(self, rhs: Close<Dollars>) -> Self::Output {
Self::from(Cents::from(self) * Cents::from(*rhs))
}
}
impl Mul<Dollars> for Close<Dollars> {
type Output = Dollars;
fn mul(self, rhs: Dollars) -> Self::Output {
Dollars::from(Cents::from(*self) * Cents::from(rhs))
}
}
impl Mul<usize> for Close<Dollars> {
type Output = Dollars;
fn mul(self, rhs: usize) -> Self::Output {
Dollars::from(Cents::from(*self) * rhs)
}
}
impl Mul<Bitcoin> for Dollars {
type Output = Self;
fn mul(self, rhs: Bitcoin) -> Self::Output {
self * Sats::from(rhs)
}
}
impl Mul<Bitcoin> for Close<Dollars> {
type Output = Dollars;
fn mul(self, rhs: Bitcoin) -> Self::Output {
*self * Sats::from(rhs)
}
}
impl Mul<Sats> for Dollars {
type Output = Self;
fn mul(self, rhs: Sats) -> Self::Output {
if self.is_nan() {
self
} else {
Self::from(Cents::from(
u128::from(rhs) * u128::from(Cents::from(self)) / u128::from(Sats::ONE_BTC),
))
}
}
}
impl Mul<StoredF32> for Dollars {
type Output = Self;
fn mul(self, rhs: StoredF32) -> Self::Output {
if rhs.fract() != 0.0 {
Self::from(self.0 * *rhs as f64)
} else {
self * *rhs as i64
}
}
}
impl Mul<i64> for Dollars {
type Output = Self;
fn mul(self, rhs: i64) -> Self::Output {
Self::from(Cents::from(self) * rhs)
}
}
impl Mul<usize> for Dollars {
type Output = Self;
fn mul(self, rhs: usize) -> Self::Output {
if self.is_nan() {
self
} else {
Self::from(Cents::from(self) * rhs)
}
}
}
impl From<u128> for Dollars {
fn from(value: u128) -> Self {
Self::from(Cents::from(value))
}
}
impl From<StoredF64> for Dollars {
fn from(value: StoredF64) -> Self {
Self(*value)
}
}
impl From<Close<Dollars>> for u128 {
fn from(value: Close<Dollars>) -> Self {
u128::from(*value)
}
}
impl From<Dollars> for u128 {
fn from(value: Dollars) -> Self {
u128::from(Cents::from(value))
}
}
impl AddAssign for Dollars {
fn add_assign(&mut self, rhs: Self) {
*self = Dollars::from(Cents::from(*self) + Cents::from(rhs));
}
}
impl CheckedSub for Dollars {
fn checked_sub(self, rhs: Self) -> Option<Self> {
if self.is_nan() {
Some(self)
} else {
Cents::from(self)
.checked_sub(Cents::from(rhs))
.map(Dollars::from)
}
}
}
impl CheckedSub<usize> for Dollars {
fn checked_sub(self, rhs: usize) -> Option<Self> {
Some(Dollars::from(
Cents::from(self).checked_sub(Cents::from(rhs)).unwrap(),
))
}
}
impl PartialEq for Dollars {
fn eq(&self, other: &Self) -> bool {
match (self.0.is_nan(), other.0.is_nan()) {
(true, true) => true,
(true, false) => false,
(false, true) => false,
(false, false) => self.0 == other.0,
}
}
}
impl Eq for Dollars {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl PartialOrd for Dollars {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for Dollars {
fn cmp(&self, other: &Self) -> Ordering {
match (self.0.is_nan(), other.0.is_nan()) {
(true, true) => Ordering::Equal,
(true, false) => Ordering::Less,
(false, true) => Ordering::Greater,
(false, false) => self.0.partial_cmp(&other.0).unwrap(),
}
}
}
impl From<ByteView> for Dollars {
fn from(value: ByteView) -> Self {
let bytes = copy_first_8bytes(&value).unwrap();
Self::from(f64::from_be_bytes(bytes))
}
}
impl From<Dollars> for ByteView {
fn from(value: Dollars) -> Self {
Self::from(&value)
}
}
impl From<&Dollars> for ByteView {
fn from(value: &Dollars) -> Self {
Self::new(&value.to_be_bytes())
}
}
+81
View File
@@ -0,0 +1,81 @@
use std::{
cmp::Ordering,
ops::{Add, Div},
};
use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use super::{Sats, StoredUsize};
#[derive(Debug, Clone, Copy, Serialize, FromBytes, Immutable, IntoBytes, KnownLayout)]
pub struct Feerate(f32);
impl From<(Sats, StoredUsize)> for Feerate {
fn from((sats, vsize): (Sats, StoredUsize)) -> Self {
Self((f64::from(sats) / f64::from(vsize)) as f32)
}
}
impl From<f64> for Feerate {
fn from(value: f64) -> Self {
Self(value as f32)
}
}
impl From<Feerate> for f64 {
fn from(value: Feerate) -> Self {
value.0 as f64
}
}
impl Add for Feerate {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Div<usize> for Feerate {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self((self.0 as f64 / rhs as f64) as f32)
}
}
impl From<usize> for Feerate {
fn from(value: usize) -> Self {
Self(value as f32)
}
}
impl PartialEq for Feerate {
fn eq(&self, other: &Self) -> bool {
match (self.0.is_nan(), other.0.is_nan()) {
(true, true) => true,
(true, false) => false,
(false, true) => false,
(false, false) => self.0 == other.0,
}
}
}
impl Eq for Feerate {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl PartialOrd for Feerate {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for Feerate {
fn cmp(&self, other: &Self) -> Ordering {
match (self.0.is_nan(), other.0.is_nan()) {
(true, true) => Ordering::Equal,
(true, false) => Ordering::Less,
(false, true) => Ordering::Greater,
(false, false) => self.0.partial_cmp(&other.0).unwrap(),
}
}
}
@@ -0,0 +1,88 @@
use std::{
fmt::Debug,
ops::{Add, Div},
};
use serde::{Deserialize, Serialize};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::Height;
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
Serialize,
Deserialize,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
)]
pub struct HalvingEpoch(u8);
impl HalvingEpoch {
pub fn new(value: u8) -> Self {
Self(value)
}
}
impl From<u8> for HalvingEpoch {
fn from(value: u8) -> Self {
Self(value)
}
}
impl From<usize> for HalvingEpoch {
fn from(value: usize) -> Self {
Self(value as u8)
}
}
impl From<HalvingEpoch> for usize {
fn from(value: HalvingEpoch) -> Self {
value.0 as usize
}
}
impl Add for HalvingEpoch {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::from(self.0 + rhs.0)
}
}
impl Add<usize> for HalvingEpoch {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self::from(self.0 + rhs as u8)
}
}
impl From<Height> for HalvingEpoch {
fn from(value: Height) -> Self {
Self((u32::from(value) / 210_000) as u8)
}
}
impl CheckedSub for HalvingEpoch {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
}
impl Div<usize> for HalvingEpoch {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self::from(self.0 as usize / rhs)
}
}
+211
View File
@@ -0,0 +1,211 @@
use std::{
fmt::{self, Debug},
ops::{Add, AddAssign, Rem},
};
use bitcoincore_rpc::{Client, RpcApi};
use byteview::ByteView;
use serde::{Deserialize, Serialize};
use zerocopy::{FromBytes, IntoBytes};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::StoredUsize;
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
Serialize,
Deserialize,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
)]
pub struct Height(u32);
impl Height {
pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(u32::MAX);
pub fn new(height: u32) -> Self {
Self(height)
}
pub fn write(&self, path: &std::path::Path) -> Result<(), std::io::Error> {
std::fs::write(path, self.as_bytes())
}
pub fn increment(&mut self) {
self.0 += 1;
}
pub fn incremented(self) -> Self {
Self(self.0 + 1)
}
pub fn decrement(&mut self) {
*self = self.decremented().unwrap();
}
pub fn decremented(self) -> Option<Self> {
self.checked_sub(1_u32)
}
pub fn is_zero(self) -> bool {
self == Self::ZERO
}
}
impl PartialEq<u64> for Height {
fn eq(&self, other: &u64) -> bool {
self.0 == *other as u32
}
}
impl Add<Height> for Height {
type Output = Self;
fn add(self, rhs: Height) -> Self::Output {
Self::from(self.0 + rhs.0)
}
}
impl Add<u32> for Height {
type Output = Self;
fn add(self, rhs: u32) -> Self::Output {
Self::from(self.0 + rhs)
}
}
impl Add<usize> for Height {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self::from(self.0 + rhs as u32)
}
}
impl CheckedSub<Height> for Height {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
}
impl CheckedSub<u32> for Height {
fn checked_sub(self, rhs: u32) -> Option<Self> {
self.0.checked_sub(rhs).map(Height::from)
}
}
impl CheckedSub<usize> for Height {
fn checked_sub(self, rhs: usize) -> Option<Self> {
self.0.checked_sub(rhs as u32).map(Height::from)
}
}
impl AddAssign<usize> for Height {
fn add_assign(&mut self, rhs: usize) {
*self = self.add(rhs);
}
}
impl Rem<Height> for Height {
type Output = Self;
fn rem(self, rhs: Height) -> Self::Output {
Self(self.0.rem(rhs.0))
}
}
impl Rem<usize> for Height {
type Output = Self;
fn rem(self, rhs: usize) -> Self::Output {
Self(self.0.rem(Height::from(rhs).0))
}
}
impl fmt::Display for Height {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<u32> for Height {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<u64> for Height {
fn from(value: u64) -> Self {
Self(value as u32)
}
}
impl From<StoredUsize> for Height {
fn from(value: StoredUsize) -> Self {
Self(*value as u32)
}
}
impl From<usize> for Height {
fn from(value: usize) -> Self {
Self(value as u32)
}
}
impl From<Height> for usize {
fn from(value: Height) -> Self {
value.0 as usize
}
}
impl From<Height> for u32 {
fn from(value: Height) -> Self {
value.0
}
}
impl From<Height> for u64 {
fn from(value: Height) -> Self {
value.0 as u64
}
}
impl TryFrom<&Client> for Height {
type Error = bitcoincore_rpc::Error;
fn try_from(value: &Client) -> Result<Self, Self::Error> {
Ok((value.get_block_count()? as usize).into())
}
}
impl From<bitcoin::locktime::absolute::Height> for Height {
fn from(value: bitcoin::locktime::absolute::Height) -> Self {
Self(value.to_consensus_u32())
}
}
impl TryFrom<&std::path::Path> for Height {
type Error = crate::Error;
fn try_from(value: &std::path::Path) -> Result<Self, Self::Error> {
Ok(Self::read_from_bytes(std::fs::read(value)?.as_slice())?.to_owned())
}
}
impl From<ByteView> for Height {
fn from(value: byteview::ByteView) -> Self {
Self::read_from_bytes(&value).unwrap()
}
}
impl From<Height> for byteview::ByteView {
fn from(value: Height) -> Self {
Self::new(value.as_bytes())
}
}
+98
View File
@@ -0,0 +1,98 @@
use std::ops::{Add, AddAssign};
use derive_deref::{Deref, DerefMut};
use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::Vin;
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct InputIndex(u64);
impl InputIndex {
pub fn incremented(self) -> Self {
Self(*self + 1)
}
}
impl Add<InputIndex> for InputIndex {
type Output = Self;
fn add(self, rhs: InputIndex) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Add<Vin> for InputIndex {
type Output = Self;
fn add(self, rhs: Vin) -> Self::Output {
Self(self.0 + u64::from(rhs))
}
}
impl Add<usize> for InputIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(self.0 + rhs as u64)
}
}
impl AddAssign<InputIndex> for InputIndex {
fn add_assign(&mut self, rhs: InputIndex) {
self.0 += rhs.0
}
}
impl CheckedSub<InputIndex> for InputIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self::from)
}
}
impl From<InputIndex> for u32 {
fn from(value: InputIndex) -> Self {
if value.0 > u32::MAX as u64 {
panic!()
}
value.0 as u32
}
}
impl From<u64> for InputIndex {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<InputIndex> for u64 {
fn from(value: InputIndex) -> Self {
value.0
}
}
impl From<usize> for InputIndex {
fn from(value: usize) -> Self {
Self(value as u64)
}
}
impl From<InputIndex> for usize {
fn from(value: InputIndex) -> Self {
value.0 as usize
}
}
+91
View File
@@ -0,0 +1,91 @@
mod addressbytes;
mod addressbyteshash;
// mod addressindex;
// mod addressindexoutputindex;
// mod addresstype;
mod bitcoin;
mod blockhash;
mod blockhashprefix;
mod cents;
mod date;
mod dateindex;
mod decadeindex;
mod difficultyepoch;
mod dollars;
mod feerate;
mod halvingepoch;
mod height;
mod inputindex;
mod monthindex;
mod ohlc;
mod outputindex;
mod outputtype;
mod outputtypeindex;
mod quarterindex;
mod rawlocktime;
mod sats;
mod stored_f32;
mod stored_f64;
mod stored_u32;
mod stored_u64;
mod stored_u8;
mod stored_usize;
mod timestamp;
mod txid;
mod txidprefix;
mod txindex;
mod txversion;
mod unit;
mod version;
mod vin;
mod vout;
mod weekindex;
mod weight;
mod yearindex;
pub use addressbytes::*;
pub use addressbyteshash::*;
// pub use addressindex::*;
// pub use addressindexoutputindex::*;
// pub use addresstype::*;
pub use bitcoin::*;
pub use blockhash::*;
pub use blockhashprefix::*;
pub use cents::*;
pub use date::*;
pub use dateindex::*;
pub use decadeindex::*;
pub use difficultyepoch::*;
pub use dollars::*;
pub use feerate::*;
pub use halvingepoch::*;
pub use height::*;
pub use inputindex::*;
pub use monthindex::*;
pub use ohlc::*;
pub use outputindex::*;
pub use outputtype::*;
pub use outputtypeindex::*;
pub use quarterindex::*;
pub use rawlocktime::*;
pub use sats::*;
pub use stored_f32::*;
pub use stored_f64::*;
pub use stored_u8::*;
pub use stored_u32::*;
pub use stored_u64::*;
pub use stored_usize::*;
pub use timestamp::*;
pub use txid::*;
pub use txidprefix::*;
pub use txindex::*;
pub use txversion::*;
pub use unit::*;
pub use version::*;
pub use vin::*;
pub use vout::*;
pub use weekindex::*;
pub use weight::*;
pub use yearindex::*;
pub use rlimit;
+70
View File
@@ -0,0 +1,70 @@
use std::{fmt::Debug, ops::Add};
use serde::{Deserialize, Serialize};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::{Date, DateIndex, YearIndex};
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
Serialize,
Deserialize,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
)]
pub struct MonthIndex(u16);
impl From<u16> for MonthIndex {
fn from(value: u16) -> Self {
Self(value)
}
}
impl From<usize> for MonthIndex {
fn from(value: usize) -> Self {
Self(value as u16)
}
}
impl From<MonthIndex> for usize {
fn from(value: MonthIndex) -> Self {
value.0 as usize
}
}
impl Add<usize> for MonthIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self::from(self.0 + rhs as u16)
}
}
impl From<DateIndex> for MonthIndex {
fn from(value: DateIndex) -> Self {
Self::from(Date::from(value))
}
}
impl From<Date> for MonthIndex {
fn from(value: Date) -> Self {
Self(u16::from(YearIndex::from(value)) * 12 + value.month() as u16 - 1)
}
}
impl CheckedSub for MonthIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
}
+522
View File
@@ -0,0 +1,522 @@
use std::ops::{Add, Div};
use derive_deref::Deref;
use serde::{Serialize, Serializer, ser::SerializeTuple};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use super::{Cents, Dollars, Sats};
#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
#[repr(C)]
pub struct OHLCCents {
pub open: Open<Cents>,
pub high: High<Cents>,
pub low: Low<Cents>,
pub close: Close<Cents>,
}
impl From<(Open<Cents>, High<Cents>, Low<Cents>, Close<Cents>)> for OHLCCents {
fn from(value: (Open<Cents>, High<Cents>, Low<Cents>, Close<Cents>)) -> Self {
Self {
open: value.0,
high: value.1,
low: value.2,
close: value.3,
}
}
}
impl From<Close<Cents>> for OHLCCents {
fn from(value: Close<Cents>) -> Self {
Self {
open: Open::from(value),
high: High::from(value),
low: Low::from(value),
close: value,
}
}
}
impl Serialize for OHLCCents {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut tup = serializer.serialize_tuple(4)?;
tup.serialize_element(&self.open)?;
tup.serialize_element(&self.high)?;
tup.serialize_element(&self.low)?;
tup.serialize_element(&self.close)?;
tup.end()
}
}
#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
#[repr(C)]
pub struct OHLCDollars {
pub open: Open<Dollars>,
pub high: High<Dollars>,
pub low: Low<Dollars>,
pub close: Close<Dollars>,
}
impl Serialize for OHLCDollars {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut tup = serializer.serialize_tuple(4)?;
tup.serialize_element(&self.open)?;
tup.serialize_element(&self.high)?;
tup.serialize_element(&self.low)?;
tup.serialize_element(&self.close)?;
tup.end()
}
}
impl From<(Open<Dollars>, High<Dollars>, Low<Dollars>, Close<Dollars>)> for OHLCDollars {
fn from(value: (Open<Dollars>, High<Dollars>, Low<Dollars>, Close<Dollars>)) -> Self {
Self {
open: value.0,
high: value.1,
low: value.2,
close: value.3,
}
}
}
impl From<Close<Dollars>> for OHLCDollars {
fn from(value: Close<Dollars>) -> Self {
Self {
open: Open::from(value),
high: High::from(value),
low: Low::from(value),
close: value,
}
}
}
impl From<OHLCCents> for OHLCDollars {
fn from(value: OHLCCents) -> Self {
Self::from(&value)
}
}
impl From<&OHLCCents> for OHLCDollars {
fn from(value: &OHLCCents) -> Self {
Self {
open: value.open.into(),
high: value.high.into(),
low: value.low.into(),
close: value.close.into(),
}
}
}
#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
#[repr(C)]
pub struct OHLCSats {
pub open: Open<Sats>,
pub high: High<Sats>,
pub low: Low<Sats>,
pub close: Close<Sats>,
}
impl Serialize for OHLCSats {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut tup = serializer.serialize_tuple(4)?;
tup.serialize_element(&self.open)?;
tup.serialize_element(&self.high)?;
tup.serialize_element(&self.low)?;
tup.serialize_element(&self.close)?;
tup.end()
}
}
impl From<(Open<Sats>, High<Sats>, Low<Sats>, Close<Sats>)> for OHLCSats {
fn from(value: (Open<Sats>, High<Sats>, Low<Sats>, Close<Sats>)) -> Self {
Self {
open: value.0,
high: value.1,
low: value.2,
close: value.3,
}
}
}
impl From<Close<Sats>> for OHLCSats {
fn from(value: Close<Sats>) -> Self {
Self {
open: Open::from(value),
high: High::from(value),
low: Low::from(value),
close: value,
}
}
}
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Deref,
Serialize,
)]
#[repr(C)]
pub struct Open<T>(T);
impl<T> Open<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T> From<usize> for Open<T>
where
T: From<usize>,
{
fn from(value: usize) -> Self {
Self(T::from(value))
}
}
impl<T> From<f64> for Open<T>
where
T: From<f64>,
{
fn from(value: f64) -> Self {
Self(T::from(value))
}
}
impl<T> From<Open<T>> for f64
where
f64: From<T>,
{
fn from(value: Open<T>) -> Self {
Self::from(value.0)
}
}
impl<T> From<Close<T>> for Open<T>
where
T: Copy,
{
fn from(value: Close<T>) -> Self {
Self(*value)
}
}
impl From<Open<Cents>> for Open<Dollars> {
fn from(value: Open<Cents>) -> Self {
Self(Dollars::from(*value))
}
}
impl<T> Add for Open<T>
where
T: Add<Output = T>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl<T> Div<usize> for Open<T>
where
T: Div<usize, Output = T>,
{
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs)
}
}
#[derive(
Debug,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Deref,
Serialize,
)]
#[repr(C)]
pub struct High<T>(T);
impl<T> High<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T> From<usize> for High<T>
where
T: From<usize>,
{
fn from(value: usize) -> Self {
Self(T::from(value))
}
}
impl<T> From<f64> for High<T>
where
T: From<f64>,
{
fn from(value: f64) -> Self {
Self(T::from(value))
}
}
impl<T> From<High<T>> for f64
where
f64: From<T>,
{
fn from(value: High<T>) -> Self {
Self::from(value.0)
}
}
impl<T> From<Close<T>> for High<T>
where
T: Copy,
{
fn from(value: Close<T>) -> Self {
Self(*value)
}
}
impl From<High<Cents>> for High<Dollars> {
fn from(value: High<Cents>) -> Self {
Self(Dollars::from(*value))
}
}
impl<T> Add for High<T>
where
T: Add<Output = T>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl<T> Div<usize> for High<T>
where
T: Div<usize, Output = T>,
{
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs)
}
}
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Deref,
Serialize,
)]
#[repr(C)]
pub struct Low<T>(T);
impl<T> Low<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T> From<usize> for Low<T>
where
T: From<usize>,
{
fn from(value: usize) -> Self {
Self(T::from(value))
}
}
impl<T> From<f64> for Low<T>
where
T: From<f64>,
{
fn from(value: f64) -> Self {
Self(T::from(value))
}
}
impl<T> From<Low<T>> for f64
where
f64: From<T>,
{
fn from(value: Low<T>) -> Self {
Self::from(value.0)
}
}
impl<T> From<Close<T>> for Low<T>
where
T: Copy,
{
fn from(value: Close<T>) -> Self {
Self(*value)
}
}
impl From<Low<Cents>> for Low<Dollars> {
fn from(value: Low<Cents>) -> Self {
Self(Dollars::from(*value))
}
}
impl<T> Add for Low<T>
where
T: Add<Output = T>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl<T> Div<usize> for Low<T>
where
T: Div<usize, Output = T>,
{
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs)
}
}
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Deref,
Serialize,
)]
#[repr(C)]
pub struct Close<T>(T);
impl<T> Close<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T> From<usize> for Close<T>
where
T: From<usize>,
{
fn from(value: usize) -> Self {
Self(T::from(value))
}
}
impl<T> From<f32> for Close<T>
where
T: From<f32>,
{
fn from(value: f32) -> Self {
Self(T::from(value))
}
}
impl<T> From<f64> for Close<T>
where
T: From<f64>,
{
fn from(value: f64) -> Self {
Self(T::from(value))
}
}
impl<T> From<Close<T>> for f32
where
f32: From<T>,
{
fn from(value: Close<T>) -> Self {
Self::from(value.0)
}
}
impl<T> From<Close<T>> for f64
where
f64: From<T>,
{
fn from(value: Close<T>) -> Self {
Self::from(value.0)
}
}
// impl<A, B> From<Close<A>> for Close<B>
// where
// B: From<A>,
// {
// fn from(value: Close<A>) -> Self {
// Self(B::from(*value))
impl From<Close<Cents>> for Close<Dollars> {
fn from(value: Close<Cents>) -> Self {
Self(Dollars::from(*value))
}
}
impl<T> Add for Close<T>
where
T: Add<Output = T>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl<T> Div<usize> for Close<T>
where
T: Div<usize, Output = T>,
{
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs)
}
}
+104
View File
@@ -0,0 +1,104 @@
use std::ops::{Add, AddAssign};
use derive_deref::{Deref, DerefMut};
use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::Vout;
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct OutputIndex(u64);
impl OutputIndex {
pub const COINBASE: Self = Self(u64::MAX);
pub fn incremented(self) -> Self {
Self(*self + 1)
}
pub fn is_coinbase(self) -> bool {
self == Self::COINBASE
}
}
impl Add<OutputIndex> for OutputIndex {
type Output = Self;
fn add(self, rhs: OutputIndex) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Add<Vout> for OutputIndex {
type Output = Self;
fn add(self, rhs: Vout) -> Self::Output {
Self(self.0 + u64::from(rhs))
}
}
impl Add<usize> for OutputIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(self.0 + rhs as u64)
}
}
impl AddAssign<OutputIndex> for OutputIndex {
fn add_assign(&mut self, rhs: OutputIndex) {
self.0 += rhs.0
}
}
impl CheckedSub<OutputIndex> for OutputIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self::from)
}
}
impl From<OutputIndex> for u32 {
fn from(value: OutputIndex) -> Self {
if value.0 > u32::MAX as u64 {
panic!()
}
value.0 as u32
}
}
impl From<u64> for OutputIndex {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<OutputIndex> for u64 {
fn from(value: OutputIndex) -> Self {
value.0
}
}
impl From<usize> for OutputIndex {
fn from(value: usize) -> Self {
Self(value as u64)
}
}
impl From<OutputIndex> for usize {
fn from(value: OutputIndex) -> Self {
value.0 as usize
}
}
+114
View File
@@ -0,0 +1,114 @@
use bitcoin::{ScriptBuf, opcodes::all::OP_PUSHBYTES_2};
use serde::Serialize;
use zerocopy_derive::{Immutable, IntoBytes, KnownLayout, TryFromBytes};
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
TryFromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
#[repr(u8)]
pub enum OutputType {
P2PK65,
P2PK33,
P2PKH,
P2MS,
P2SH,
OpReturn,
P2WPKH,
P2WSH,
P2TR,
P2A,
Empty = 254,
Unknown = 255,
}
impl OutputType {
pub fn is_spendable(&self) -> bool {
match self {
Self::P2PK65 => true,
Self::P2PK33 => true,
Self::P2PKH => true,
Self::P2MS => true,
Self::P2SH => true,
Self::OpReturn => false,
Self::P2WPKH => true,
Self::P2WSH => true,
Self::P2TR => true,
Self::P2A => true,
Self::Empty => true,
Self::Unknown => true,
}
}
pub fn is_unspendable(&self) -> bool {
!self.is_spendable()
}
pub fn as_vec() -> Vec<Self> {
vec![
Self::P2PK65,
Self::P2PK33,
Self::P2PKH,
Self::P2MS,
Self::P2SH,
Self::OpReturn,
Self::P2WPKH,
Self::P2WSH,
Self::P2TR,
Self::P2A,
Self::Empty,
Self::Unknown,
]
}
}
impl From<&ScriptBuf> for OutputType {
fn from(script: &ScriptBuf) -> Self {
if script.is_p2pk() {
let bytes = script.as_bytes();
match bytes.len() {
67 => Self::P2PK65,
35 => Self::P2PK33,
_ => {
dbg!(bytes);
unreachable!()
}
}
} else if script.is_p2pkh() {
Self::P2PKH
} else if script.is_multisig() {
Self::P2MS
} else if script.is_p2sh() {
Self::P2SH
} else if script.is_op_return() {
Self::OpReturn
} else if script.is_p2wpkh() {
Self::P2WPKH
} else if script.is_p2wsh() {
Self::P2WSH
} else if script.is_p2tr() {
Self::P2TR
} else if script.witness_version() == Some(bitcoin::WitnessVersion::V1)
&& script.len() == 4
&& script.as_bytes()[1] == OP_PUSHBYTES_2.to_u8()
&& script.as_bytes()[2..4] == [78, 115]
{
Self::P2A
} else if script.is_empty() {
Self::Empty
} else {
Self::Unknown
}
}
}
@@ -0,0 +1,634 @@
use std::ops::Add;
use byteview::ByteView;
use derive_deref::{Deref, DerefMut};
use serde::Serialize;
use zerocopy::{FromBytes, IntoBytes};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct OutputTypeIndex(u32);
impl OutputTypeIndex {
pub fn increment(&mut self) {
self.0 += 1;
}
pub fn incremented(self) -> Self {
Self(self.0 + 1)
}
pub fn copy_then_increment(&mut self) -> Self {
let i = *self;
self.increment();
i
}
}
impl From<u32> for OutputTypeIndex {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<u64> for OutputTypeIndex {
fn from(value: u64) -> Self {
Self(value as u32)
}
}
impl From<OutputTypeIndex> for u64 {
fn from(value: OutputTypeIndex) -> Self {
value.0 as u64
}
}
impl From<usize> for OutputTypeIndex {
fn from(value: usize) -> Self {
Self(value as u32)
}
}
impl From<OutputTypeIndex> for usize {
fn from(value: OutputTypeIndex) -> Self {
value.0 as usize
}
}
impl Add<usize> for OutputTypeIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(self.0 + rhs as u32)
}
}
impl Add<OutputTypeIndex> for OutputTypeIndex {
type Output = Self;
fn add(self, rhs: OutputTypeIndex) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl From<ByteView> for OutputTypeIndex {
fn from(value: ByteView) -> Self {
Self::read_from_bytes(&value).unwrap()
}
}
impl From<OutputTypeIndex> for ByteView {
fn from(value: OutputTypeIndex) -> Self {
Self::new(value.as_bytes())
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct EmptyOutputIndex(OutputTypeIndex);
impl From<OutputTypeIndex> for EmptyOutputIndex {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<EmptyOutputIndex> for usize {
fn from(value: EmptyOutputIndex) -> Self {
Self::from(*value)
}
}
impl From<usize> for EmptyOutputIndex {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for EmptyOutputIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<EmptyOutputIndex> for EmptyOutputIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct P2MSIndex(OutputTypeIndex);
impl From<OutputTypeIndex> for P2MSIndex {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<P2MSIndex> for usize {
fn from(value: P2MSIndex) -> Self {
Self::from(*value)
}
}
impl From<usize> for P2MSIndex {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for P2MSIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<P2MSIndex> for P2MSIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct P2AIndex(OutputTypeIndex);
impl From<OutputTypeIndex> for P2AIndex {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<P2AIndex> for usize {
fn from(value: P2AIndex) -> Self {
Self::from(*value)
}
}
impl From<usize> for P2AIndex {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for P2AIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<P2AIndex> for P2AIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct OpReturnIndex(OutputTypeIndex);
impl From<OutputTypeIndex> for OpReturnIndex {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<OpReturnIndex> for usize {
fn from(value: OpReturnIndex) -> Self {
Self::from(*value)
}
}
impl From<usize> for OpReturnIndex {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for OpReturnIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<OpReturnIndex> for OpReturnIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct UnknownOutputIndex(OutputTypeIndex);
impl From<OutputTypeIndex> for UnknownOutputIndex {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<UnknownOutputIndex> for usize {
fn from(value: UnknownOutputIndex) -> Self {
Self::from(*value)
}
}
impl From<usize> for UnknownOutputIndex {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for UnknownOutputIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<UnknownOutputIndex> for UnknownOutputIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct P2PK33Index(OutputTypeIndex);
impl From<OutputTypeIndex> for P2PK33Index {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<P2PK33Index> for usize {
fn from(value: P2PK33Index) -> Self {
Self::from(*value)
}
}
impl From<usize> for P2PK33Index {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for P2PK33Index {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<P2PK33Index> for P2PK33Index {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct P2PK65Index(OutputTypeIndex);
impl From<OutputTypeIndex> for P2PK65Index {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<P2PK65Index> for usize {
fn from(value: P2PK65Index) -> Self {
Self::from(*value)
}
}
impl From<usize> for P2PK65Index {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for P2PK65Index {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<P2PK65Index> for P2PK65Index {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct P2PKHIndex(OutputTypeIndex);
impl From<OutputTypeIndex> for P2PKHIndex {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<P2PKHIndex> for usize {
fn from(value: P2PKHIndex) -> Self {
Self::from(*value)
}
}
impl From<usize> for P2PKHIndex {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for P2PKHIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<P2PKHIndex> for P2PKHIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct P2SHIndex(OutputTypeIndex);
impl From<OutputTypeIndex> for P2SHIndex {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<P2SHIndex> for usize {
fn from(value: P2SHIndex) -> Self {
Self::from(*value)
}
}
impl From<usize> for P2SHIndex {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for P2SHIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<P2SHIndex> for P2SHIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct P2TRIndex(OutputTypeIndex);
impl From<OutputTypeIndex> for P2TRIndex {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<P2TRIndex> for usize {
fn from(value: P2TRIndex) -> Self {
Self::from(*value)
}
}
impl From<usize> for P2TRIndex {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for P2TRIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<P2TRIndex> for P2TRIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct P2WPKHIndex(OutputTypeIndex);
impl From<OutputTypeIndex> for P2WPKHIndex {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<P2WPKHIndex> for usize {
fn from(value: P2WPKHIndex) -> Self {
Self::from(*value)
}
}
impl From<usize> for P2WPKHIndex {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for P2WPKHIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<P2WPKHIndex> for P2WPKHIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct P2WSHIndex(OutputTypeIndex);
impl From<OutputTypeIndex> for P2WSHIndex {
fn from(value: OutputTypeIndex) -> Self {
Self(value)
}
}
impl From<P2WSHIndex> for usize {
fn from(value: P2WSHIndex) -> Self {
Self::from(*value)
}
}
impl From<usize> for P2WSHIndex {
fn from(value: usize) -> Self {
Self(OutputTypeIndex::from(value))
}
}
impl Add<usize> for P2WSHIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(*self + rhs)
}
}
impl CheckedSub<P2WSHIndex> for P2WSHIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.0.checked_sub(rhs.0.0).map(OutputTypeIndex).map(Self)
}
}
@@ -0,0 +1,64 @@
use std::{fmt::Debug, ops::Add};
use serde::{Deserialize, Serialize};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::MonthIndex;
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
Serialize,
Deserialize,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
)]
pub struct QuarterIndex(u16);
impl From<u16> for QuarterIndex {
fn from(value: u16) -> Self {
Self(value)
}
}
impl From<usize> for QuarterIndex {
fn from(value: usize) -> Self {
Self(value as u16)
}
}
impl From<QuarterIndex> for usize {
fn from(value: QuarterIndex) -> Self {
value.0 as usize
}
}
impl Add<usize> for QuarterIndex {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self::from(self.0 + rhs as u16)
}
}
impl From<MonthIndex> for QuarterIndex {
fn from(value: MonthIndex) -> Self {
Self((usize::from(value) / 3) as u16)
}
}
impl CheckedSub for QuarterIndex {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
}
@@ -0,0 +1,29 @@
use bitcoin::absolute::LockTime;
use serde::Serialize;
use zerocopy_derive::{Immutable, IntoBytes, KnownLayout, TryFromBytes};
#[derive(Debug, Immutable, Clone, Copy, IntoBytes, KnownLayout, TryFromBytes, Serialize)]
pub struct RawLockTime(u32);
impl From<LockTime> for RawLockTime {
fn from(value: LockTime) -> Self {
Self(value.to_consensus_u32())
}
}
const CONSENSUS_DELIMITER: u32 = 500_000_000;
impl From<RawLockTime> for LockTime {
fn from(value: RawLockTime) -> Self {
let value = value.0;
if value >= CONSENSUS_DELIMITER {
bitcoin::locktime::absolute::Height::from_consensus(value)
.unwrap()
.into()
} else {
bitcoin::locktime::absolute::Time::from_consensus(value)
.unwrap()
.into()
}
}
}
+244
View File
@@ -0,0 +1,244 @@
use std::{
iter::Sum,
ops::{Add, AddAssign, Div, Mul, SubAssign},
};
use bincode::{Decode, Encode};
use bitcoin::Amount;
use byteview::ByteView;
use serde::{Deserialize, Serialize};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::{CheckedSub, copy_first_8bytes};
use super::{Bitcoin, Cents, Dollars, Height};
#[derive(
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Default,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
Deserialize,
Encode,
Decode,
)]
pub struct Sats(u64);
#[allow(clippy::inconsistent_digit_grouping)]
impl Sats {
pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(u64::MAX);
pub const ONE_BTC: Self = Self(1_00_000_000);
pub const FIFTY_BTC: Self = Self(50_00_000_000);
pub fn new(sats: u64) -> Self {
Self(sats)
}
pub fn is_zero(&self) -> bool {
*self == Self::ZERO
}
pub fn is_not_zero(&self) -> bool {
*self != Self::ZERO
}
}
impl Add for Sats {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::from(self.0 + rhs.0)
}
}
impl AddAssign for Sats {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl CheckedSub<Sats> for Sats {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self::from)
}
}
impl CheckedSub<usize> for Sats {
fn checked_sub(self, rhs: usize) -> Option<Self> {
self.0.checked_sub(rhs as u64).map(Self::from)
}
}
impl SubAssign for Sats {
fn sub_assign(&mut self, rhs: Self) {
*self = self.checked_sub(rhs).unwrap();
}
}
impl Mul<Sats> for Sats {
type Output = Self;
fn mul(self, rhs: Sats) -> Self::Output {
Sats::from(self.0.checked_mul(rhs.0).unwrap())
}
}
impl Mul<usize> for Sats {
type Output = Self;
fn mul(self, rhs: usize) -> Self::Output {
Sats::from(self.0.checked_mul(rhs as u64).unwrap())
}
}
impl Mul<u64> for Sats {
type Output = Self;
fn mul(self, rhs: u64) -> Self::Output {
Sats::from(self.0.checked_mul(rhs).unwrap())
}
}
impl Mul<Height> for Sats {
type Output = Self;
fn mul(self, rhs: Height) -> Self::Output {
Sats::from(self.0.checked_mul(u64::from(rhs)).unwrap())
}
}
impl Sum for Sats {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
let sats: u64 = iter.map(|sats| sats.0).sum();
Sats::from(sats)
}
}
impl Div<Dollars> for Sats {
type Output = Self;
fn div(self, rhs: Dollars) -> Self::Output {
let raw_cents = u64::from(Cents::from(rhs));
if raw_cents != 0 {
Self(self.0 * 100 / raw_cents)
} else {
Self::MAX
}
}
}
impl Div<Sats> for Sats {
type Output = Self;
fn div(self, rhs: Sats) -> Self::Output {
if rhs.0 == 0 {
Self(0)
} else {
Self(self.0 / rhs.0)
}
}
}
impl Div<usize> for Sats {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs as u64)
}
}
impl From<u64> for Sats {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<usize> for Sats {
fn from(value: usize) -> Self {
Self(value as u64)
}
}
impl From<f64> for Sats {
fn from(value: f64) -> Self {
Self(value.round() as u64)
}
}
impl From<Sats> for f64 {
fn from(value: Sats) -> Self {
value.0 as f64
}
}
impl From<Sats> for usize {
fn from(value: Sats) -> Self {
value.0 as usize
}
}
impl From<Amount> for Sats {
fn from(value: Amount) -> Self {
Self(value.to_sat())
}
}
impl From<Sats> for Amount {
fn from(value: Sats) -> Self {
Self::from_sat(value.0)
}
}
impl From<Bitcoin> for Sats {
fn from(value: Bitcoin) -> Self {
Self((f64::from(value) * (Sats::ONE_BTC.0 as f64)).round() as u64)
}
}
impl From<Sats> for u64 {
fn from(value: Sats) -> Self {
value.0
}
}
impl From<u128> for Sats {
fn from(value: u128) -> Self {
if value > u64::MAX as u128 {
panic!("u128 bigger than u64")
}
Self(value as u64)
}
}
impl From<Sats> for u128 {
fn from(value: Sats) -> Self {
value.0 as u128
}
}
impl From<ByteView> for Sats {
fn from(value: ByteView) -> Self {
let bytes = copy_first_8bytes(&value).unwrap();
Self::from(u64::from_be_bytes(bytes))
}
}
impl From<&Sats> for ByteView {
fn from(value: &Sats) -> Self {
Self::new(&value.0.to_be_bytes())
}
}
impl From<Sats> for ByteView {
fn from(value: Sats) -> Self {
Self::from(&value)
}
}
impl Mul<Sats> for usize {
type Output = Sats;
fn mul(self, rhs: Sats) -> Self::Output {
Self::Output::from(rhs.0 * self as u64)
}
}
+150
View File
@@ -0,0 +1,150 @@
use core::panic;
use std::{
cmp::Ordering,
ops::{Add, Div, Mul, Sub},
};
use derive_deref::Deref;
use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::{Dollars, StoredF64};
#[derive(
Debug, Deref, Default, Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout, Serialize,
)]
pub struct StoredF32(f32);
impl From<f32> for StoredF32 {
fn from(value: f32) -> Self {
Self(value)
}
}
impl From<f64> for StoredF32 {
fn from(value: f64) -> Self {
if value > f32::MAX as f64 {
panic!("f64 is too big")
}
Self(value as f32)
}
}
impl From<StoredF32> for f64 {
fn from(value: StoredF32) -> Self {
value.0 as f64
}
}
impl From<StoredF64> for StoredF32 {
fn from(value: StoredF64) -> Self {
Self(*value as f32)
}
}
impl From<usize> for StoredF32 {
fn from(value: usize) -> Self {
Self(value as f32)
}
}
impl CheckedSub<StoredF32> for StoredF32 {
fn checked_sub(self, rhs: Self) -> Option<Self> {
Some(Self(self.0 - rhs.0))
}
}
impl Div<usize> for StoredF32 {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs as f32)
}
}
impl Add for StoredF32 {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl From<StoredF32> for f32 {
fn from(value: StoredF32) -> Self {
value.0
}
}
impl From<Dollars> for StoredF32 {
fn from(value: Dollars) -> Self {
StoredF32::from(f64::from(value))
}
}
impl Div<Dollars> for StoredF32 {
type Output = Self;
fn div(self, rhs: Dollars) -> Self::Output {
Self::from(self.0 as f64 / *rhs)
}
}
impl Div<StoredF32> for StoredF32 {
type Output = Self;
fn div(self, rhs: StoredF32) -> Self::Output {
Self::from(self.0 / rhs.0)
}
}
impl Mul<usize> for StoredF32 {
type Output = Self;
fn mul(self, rhs: usize) -> Self::Output {
Self(self.0 * rhs as f32)
}
}
impl Mul<StoredF32> for usize {
type Output = StoredF32;
fn mul(self, rhs: StoredF32) -> Self::Output {
StoredF32(self as f32 * rhs.0)
}
}
impl Sub<StoredF32> for StoredF32 {
type Output = Self;
fn sub(self, rhs: StoredF32) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl PartialEq for StoredF32 {
fn eq(&self, other: &Self) -> bool {
match (self.0.is_nan(), other.0.is_nan()) {
(true, true) => true,
(true, false) => false,
(false, true) => false,
(false, false) => self.0 == other.0,
}
}
}
impl Eq for StoredF32 {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl PartialOrd for StoredF32 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for StoredF32 {
fn cmp(&self, other: &Self) -> Ordering {
match (self.0.is_nan(), other.0.is_nan()) {
(true, true) => Ordering::Equal,
(true, false) => Ordering::Less,
(false, true) => Ordering::Greater,
(false, false) => self.0.partial_cmp(&other.0).unwrap(),
}
}
}
+115
View File
@@ -0,0 +1,115 @@
use std::{
cmp::Ordering,
f64,
ops::{Add, Div, Mul},
};
use derive_deref::Deref;
use serde::Serialize;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::{Bitcoin, CheckedSub, Dollars};
#[derive(
Debug, Deref, Default, Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout, Serialize,
)]
pub struct StoredF64(f64);
impl StoredF64 {
pub const NAN: Self = Self(f64::NAN);
}
impl From<f64> for StoredF64 {
fn from(value: f64) -> Self {
Self(value)
}
}
impl From<usize> for StoredF64 {
fn from(value: usize) -> Self {
Self(value as f64)
}
}
impl CheckedSub<StoredF64> for StoredF64 {
fn checked_sub(self, rhs: Self) -> Option<Self> {
Some(Self(self.0 - rhs.0))
}
}
impl Mul<usize> for StoredF64 {
type Output = Self;
fn mul(self, rhs: usize) -> Self::Output {
Self(self.0 * rhs as f64)
}
}
impl Div<usize> for StoredF64 {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs as f64)
}
}
impl Add for StoredF64 {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl From<StoredF64> for f64 {
fn from(value: StoredF64) -> Self {
value.0
}
}
impl From<Dollars> for StoredF64 {
fn from(value: Dollars) -> Self {
Self(f64::from(value))
}
}
impl CheckedSub<usize> for StoredF64 {
fn checked_sub(self, rhs: usize) -> Option<Self> {
Some(Self(self.0 - rhs as f64))
}
}
impl PartialEq for StoredF64 {
fn eq(&self, other: &Self) -> bool {
match (self.0.is_nan(), other.0.is_nan()) {
(true, true) => true,
(true, false) => false,
(false, true) => false,
(false, false) => self.0 == other.0,
}
}
}
impl Eq for StoredF64 {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl PartialOrd for StoredF64 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for StoredF64 {
fn cmp(&self, other: &Self) -> Ordering {
match (self.0.is_nan(), other.0.is_nan()) {
(true, true) => Ordering::Equal,
(true, false) => Ordering::Less,
(false, true) => Ordering::Greater,
(false, false) => self.0.partial_cmp(&other.0).unwrap(),
}
}
}
impl From<Bitcoin> for StoredF64 {
fn from(value: Bitcoin) -> Self {
Self(f64::from(value))
}
}

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