Compare commits

...

61 Commits

Author SHA1 Message Date
nym21 366ac33e23 release: v0.3.0-alpha.3 2026-04-04 01:05:04 +02:00
nym21 b5a7023bd3 docs: update generated docs 2026-04-04 01:04:38 +02:00
nym21 883b38c77c global: snap 2026-04-04 00:59:37 +02:00
nym21 59c767a9e2 release: v0.3.0-alpha.2 2026-04-03 17:56:37 +02:00
nym21 9b5bb848f7 docs: update generated docs 2026-04-03 17:56:12 +02:00
nym21 5bf06530ce store: replace fjal reset by dir nuking 2026-04-03 17:49:46 +02:00
nym21 768e6870cb global: snap 2026-04-03 15:51:27 +02:00
nym21 79829ddd53 changelog: updated 2026-04-03 01:19:47 +02:00
nym21 78082801c6 clients: bump versions 2026-04-03 00:52:09 +02:00
nym21 50771ddccc release: v0.3.0-alpha.1 2026-04-03 00:16:03 +02:00
nym21 3a8a9ddecc docs: update generated docs 2026-04-03 00:15:43 +02:00
nym21 6cd45c1f1f deps: bumped 2026-04-02 23:54:12 +02:00
nym21 1a2db43cf5 fmt: global 2026-04-02 23:50:01 +02:00
nym21 4840e564f4 server: moved params from brk_types 2026-04-02 23:49:01 +02:00
nym21 744dce932c types: docs 2026-04-02 23:08:25 +02:00
nym21 8dfc1bc932 server: ms endpoint fixes 2026-04-02 22:37:34 +02:00
nym21 d92cf43c57 server: reorg 2026-04-02 13:19:56 +02:00
nym21 099699872e global: fixes 2026-04-02 12:39:20 +02:00
nym21 5099903043 clients: bump versions 2026-04-01 23:00:31 +02:00
nym21 982fe47a33 pr: #32
v0.3.0-alpha.0
2026-04-01 22:41:39 +02:00
nym21 65d5fadd13 release: v0.3.0-alpha.0 2026-04-01 22:17:00 +02:00
nym21 b55f5255ad docs: update generated docs 2026-04-01 22:16:40 +02:00
nym21 83edef4806 coinbase: lossy to latin 2026-04-01 21:54:02 +02:00
nym21 d4936d889a clients: regened 2026-04-01 21:37:16 +02:00
nym21 c938cc8eae types: coinbase lossy format 2026-04-01 21:15:45 +02:00
nym21 0558834eef global: fixes 2026-04-01 21:11:20 +02:00
nym21 098950fdde deps: bumped 2026-04-01 18:25:11 +02:00
nym21 91e68a1d1e global: fixes 2026-04-01 18:18:56 +02:00
nym21 7172ddb247 global: snapshot 2026-04-01 17:51:50 +02:00
nym21 96f2e058f7 global: snapshot 2026-04-01 15:50:49 +02:00
nym21 8782944191 readme: add supporter 2026-03-31 23:00:15 +02:00
nym21 ae26db6df2 global: snapshot 2026-03-31 22:53:25 +02:00
nym21 d038141a8a global: snapshot 2026-03-29 23:10:31 +02:00
nym21 f6960c61d6 clients: regened 2026-03-29 17:47:47 +02:00
nym21 07fa2d2c9a release: v0.2.5 2026-03-29 17:33:10 +02:00
nym21 82c6d69a0b docs: update generated docs 2026-03-29 17:32:44 +02:00
nym21 d4dc1b9e49 deps: bumped 2026-03-29 17:26:48 +02:00
nym21 24d2b7b142 global: fmt 2026-03-28 11:56:51 +01:00
nym21 b6e56c4e9f Merge pull request #30 from yashbhutwala/codex/brk-website-static-serving-tests
[codex] add brk_website static serving tests
2026-03-27 23:02:39 +01:00
nym21 45c77a4c3b global: delay compaction 2026-03-27 23:02:31 +01:00
Yash Bhutwala 09af190ac0 add brk_website serving tests 2026-03-27 10:00:37 -04:00
nym21 d24f3691cb release: v0.2.4 2026-03-27 12:55:49 +01:00
nym21 daaaa15483 docs: update generated docs 2026-03-27 12:55:25 +01:00
nym21 041652d85d changelog + website: fixes 2026-03-27 12:50:35 +01:00
nym21 17570e12b8 release: v0.2.3 2026-03-26 23:56:58 +01:00
nym21 78172734db docs: update generated docs 2026-03-26 23:56:38 +01:00
nym21 19d4a193ff computer: renames 2026-03-26 23:39:28 +01:00
nym21 66680368b6 deps: bump vecdb 2026-03-26 22:06:16 +01:00
nym21 b4ded21ea3 website: chart fix 2026-03-26 22:02:15 +01:00
nym21 7412373d8a global: snapshot 2026-03-26 21:53:00 +01:00
nym21 259960b80b global: snapshot 2026-03-26 20:23:09 +01:00
nym21 18bb4186a8 global: snapshot 2026-03-26 15:57:22 +01:00
nym21 6d3307c0df docker: update heath check 2026-03-25 10:33:14 +01:00
nym21 6eea20b89a release: v0.2.2 2026-03-23 20:24:48 +01:00
nym21 5077cefda8 docs: update generated docs 2026-03-23 20:24:23 +01:00
nym21 14d7adfdd5 website: better llm files 2026-03-23 20:19:02 +01:00
nym21 000027fab8 changelog: updated 2026-03-23 18:30:32 +01:00
nym21 ade23795b8 clients: bump versions 2026-03-23 16:57:43 +01:00
nym21 67ad33b07a release: v0.2.1 2026-03-23 16:49:24 +01:00
nym21 d54874d3a4 docs: update generated docs 2026-03-23 16:49:20 +01:00
nym21 ec6420254a rpc: default corepc feature 2026-03-23 16:48:48 +01:00
993 changed files with 18063 additions and 9710 deletions
Generated
+132 -135
View File
@@ -338,7 +338,7 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
name = "brk"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"brk_bencher",
"brk_bindgen",
@@ -399,7 +399,7 @@ dependencies = [
[[package]]
name = "brk_alloc"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"libmimalloc-sys",
"mimalloc",
@@ -407,7 +407,7 @@ dependencies = [
[[package]]
name = "brk_bencher"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"brk_error",
"brk_logger",
@@ -417,14 +417,14 @@ dependencies = [
[[package]]
name = "brk_bencher_visualizer"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"plotters",
]
[[package]]
name = "brk_bindgen"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"brk_cohort",
"brk_query",
@@ -437,14 +437,13 @@ dependencies = [
[[package]]
name = "brk_cli"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"anyhow",
"brk_alloc",
"brk_computer",
"brk_error",
"brk_indexer",
"brk_iterator",
"brk_logger",
"brk_mempool",
"brk_query",
@@ -463,7 +462,7 @@ dependencies = [
[[package]]
name = "brk_client"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"brk_cohort",
"brk_types",
@@ -474,7 +473,7 @@ dependencies = [
[[package]]
name = "brk_cohort"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"brk_error",
"brk_traversable",
@@ -486,7 +485,7 @@ dependencies = [
[[package]]
name = "brk_computer"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"bitcoin",
"brk_alloc",
@@ -494,12 +493,10 @@ dependencies = [
"brk_cohort",
"brk_error",
"brk_indexer",
"brk_iterator",
"brk_logger",
"brk_oracle",
"brk_reader",
"brk_rpc",
"brk_store",
"brk_traversable",
"brk_types",
"color-eyre",
@@ -517,7 +514,7 @@ dependencies = [
[[package]]
name = "brk_error"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -534,7 +531,7 @@ dependencies = [
[[package]]
name = "brk_fetcher"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"brk_error",
"brk_logger",
@@ -546,14 +543,13 @@ dependencies = [
[[package]]
name = "brk_indexer"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"bitcoin",
"brk_alloc",
"brk_bencher",
"brk_cohort",
"brk_error",
"brk_iterator",
"brk_logger",
"brk_reader",
"brk_rpc",
@@ -562,6 +558,7 @@ dependencies = [
"brk_types",
"color-eyre",
"fjall",
"parking_lot",
"rayon",
"rlimit",
"rustc-hash",
@@ -573,7 +570,7 @@ dependencies = [
[[package]]
name = "brk_iterator"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"brk_error",
"brk_reader",
@@ -583,7 +580,7 @@ dependencies = [
[[package]]
name = "brk_logger"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"jiff",
"owo-colors",
@@ -594,7 +591,7 @@ dependencies = [
[[package]]
name = "brk_mempool"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"brk_error",
"brk_logger",
@@ -609,7 +606,7 @@ dependencies = [
[[package]]
name = "brk_oracle"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"brk_indexer",
"brk_types",
@@ -619,7 +616,7 @@ dependencies = [
[[package]]
name = "brk_query"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"bitcoin",
"brk_computer",
@@ -641,7 +638,7 @@ dependencies = [
[[package]]
name = "brk_reader"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"bitcoin",
"brk_error",
@@ -656,7 +653,7 @@ dependencies = [
[[package]]
name = "brk_rpc"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -673,7 +670,7 @@ dependencies = [
[[package]]
name = "brk_server"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"aide",
"axum",
@@ -708,7 +705,7 @@ dependencies = [
[[package]]
name = "brk_store"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"brk_error",
"brk_types",
@@ -719,7 +716,7 @@ dependencies = [
[[package]]
name = "brk_traversable"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"brk_traversable_derive",
"brk_types",
@@ -732,7 +729,7 @@ dependencies = [
[[package]]
name = "brk_traversable_derive"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"proc-macro2",
"quote",
@@ -741,7 +738,7 @@ dependencies = [
[[package]]
name = "brk_types"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"bitcoin",
"brk_error",
@@ -764,7 +761,7 @@ dependencies = [
[[package]]
name = "brk_website"
version = "0.2.0"
version = "0.3.0-alpha.3"
dependencies = [
"axum",
"brk_logger",
@@ -836,9 +833,9 @@ checksum = "1c53ba0f290bfc610084c05582d9c5d421662128fc69f4bf236707af6fd321b9"
[[package]]
name = "cc"
version = "1.2.57"
version = "1.2.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -1308,9 +1305,9 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "fjall"
version = "3.1.2"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a9530ff159bc3ad3a15da746da0f6e95375c2ac64708cbb85ec1ebd26761a84"
checksum = "0ebf22b812878dcd767879cb19e03124fd62563dce6410f96538175fba0c132d"
dependencies = [
"byteorder-lite",
"byteview",
@@ -1318,7 +1315,7 @@ dependencies = [
"flume",
"log",
"lsm-tree",
"lz4_flex",
"lz4_flex 0.11.6",
"tempfile",
"xxhash-rust",
]
@@ -1672,9 +1669,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.8.1"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
dependencies = [
"atomic-waker",
"bytes",
@@ -1686,7 +1683,6 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
"pin-utils",
"smallvec",
"tokio",
]
@@ -1732,12 +1728,13 @@ dependencies = [
[[package]]
name = "icu_collections"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
dependencies = [
"displaydoc",
"potential_utf",
"utf8_iter",
"yoke",
"zerofrom",
"zerovec",
@@ -1745,9 +1742,9 @@ dependencies = [
[[package]]
name = "icu_locale_core"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
dependencies = [
"displaydoc",
"litemap",
@@ -1758,9 +1755,9 @@ dependencies = [
[[package]]
name = "icu_normalizer"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
dependencies = [
"icu_collections",
"icu_normalizer_data",
@@ -1772,15 +1769,15 @@ dependencies = [
[[package]]
name = "icu_normalizer_data"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
[[package]]
name = "icu_properties"
version = "2.1.2"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec"
checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
dependencies = [
"icu_collections",
"icu_locale_core",
@@ -1792,15 +1789,15 @@ dependencies = [
[[package]]
name = "icu_properties_data"
version = "2.1.2"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af"
checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
[[package]]
name = "icu_provider"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
dependencies = [
"displaydoc",
"icu_locale_core",
@@ -1967,9 +1964,9 @@ checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07"
[[package]]
name = "js-sys"
version = "0.3.91"
version = "0.3.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -2007,9 +2004,9 @@ checksum = "803ec87c9cfb29b9d2633f20cba1f488db3fd53f2158b1024cbefb47ba05d413"
[[package]]
name = "libc"
version = "0.2.183"
version = "0.2.184"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
[[package]]
name = "libloading"
@@ -2045,9 +2042,9 @@ dependencies = [
[[package]]
name = "libredox"
version = "0.1.14"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08"
dependencies = [
"libc",
]
@@ -2060,9 +2057,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "litemap"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
name = "litrs"
@@ -2087,9 +2084,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lsm-tree"
version = "3.1.2"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d67f95fd716870329c30aaeedf87f23d426564e6ce46efa045a91444faf2a19"
checksum = "e9bfd2a6ea0c1d430c13643002f35800a87f200fc8ac4827f18a2db9d9fd0644"
dependencies = [
"byteorder-lite",
"byteview",
@@ -2097,7 +2094,7 @@ dependencies = [
"enum_dispatch",
"interval-heap",
"log",
"lz4_flex",
"lz4_flex 0.11.6",
"quick_cache",
"rustc-hash",
"self_cell",
@@ -2109,13 +2106,19 @@ dependencies = [
[[package]]
name = "lz4_flex"
version = "0.13.0"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db9a0d582c2874f68138a16ce1867e0ffde6c0bb0a0df85e1f36d04146db488a"
checksum = "373f5eceeeab7925e0c1098212f2fbc4d416adec9d35051a6ab251e824c1854a"
dependencies = [
"twox-hash",
]
[[package]]
name = "lz4_flex"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db9a0d582c2874f68138a16ce1867e0ffde6c0bb0a0df85e1f36d04146db488a"
[[package]]
name = "matchit"
version = "0.8.4"
@@ -2180,9 +2183,9 @@ dependencies = [
[[package]]
name = "mio"
version = "1.1.1"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
dependencies = [
"libc",
"wasi",
@@ -2201,9 +2204,9 @@ dependencies = [
[[package]]
name = "num-conv"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
[[package]]
name = "num-traits"
@@ -2216,9 +2219,9 @@ dependencies = [
[[package]]
name = "oas3"
version = "0.20.1"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f67c885c7b19aaf652e84102035258ecb4e0425a4f71037e187798e367bd87"
checksum = "05ed0821ab10d7703415a06df039c2493f3a7667999d8b4e104731de0c53796f"
dependencies = [
"derive_more",
"http",
@@ -2324,12 +2327,6 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.32"
@@ -2412,9 +2409,9 @@ dependencies = [
[[package]]
name = "potential_utf"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
dependencies = [
"zerovec",
]
@@ -2545,9 +2542,9 @@ dependencies = [
[[package]]
name = "rawdb"
version = "0.7.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "912a9c6f76a5f141057139d510b969b082ff74f39a72a1c27178d8e1eeec95dc"
checksum = "f23b5d5fae99af33e8d0c82763b890c469dcf18b48600ed78b0d70fce4dbe189"
dependencies = [
"libc",
"log",
@@ -2678,9 +2675,9 @@ checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
[[package]]
name = "rustc-hash"
version = "2.1.1"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
[[package]]
name = "rustc_version"
@@ -2916,9 +2913,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "1.0.4"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
dependencies = [
"serde_core",
]
@@ -2963,9 +2960,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd-adler32"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "slab"
@@ -3134,9 +3131,9 @@ dependencies = [
[[package]]
name = "tinystr"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
dependencies = [
"displaydoc",
"zerovec",
@@ -3182,9 +3179,9 @@ dependencies = [
[[package]]
name = "toml"
version = "1.0.7+spec-1.1.0"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd28d57d8a6f6e458bc0b8784f8fdcc4b99a437936056fa122cb234f18656a96"
checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
dependencies = [
"indexmap",
"serde_core",
@@ -3197,27 +3194,27 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "1.0.1+spec-1.1.0"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9"
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.0.10+spec-1.1.0"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.0.7+spec-1.1.0"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d"
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
[[package]]
name = "tower"
@@ -3352,9 +3349,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
[[package]]
name = "unicode-xid"
@@ -3439,14 +3436,14 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23"
[[package]]
name = "vecdb"
version = "0.7.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f89be182f86511ee28832cc04039d818564b88c63b14093fcf1871c732c0dd"
checksum = "a5fe60956ddba8c141ca8020aaf5bea55683475b83d19006c5f44b85c71bf974"
dependencies = [
"itoa",
"libc",
"log",
"lz4_flex",
"lz4_flex 0.13.0",
"parking_lot",
"pco",
"rawdb",
@@ -3462,9 +3459,9 @@ dependencies = [
[[package]]
name = "vecdb_derive"
version = "0.7.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42915a5ca404d941e3e6d024f794403c481e5aeb96b1867d12d1f1e972d1433f"
checksum = "789897c1999d5d74f977020ad3d449846df046194103a4afcbac6d49baeaaffc"
dependencies = [
"quote",
"syn",
@@ -3512,9 +3509,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
version = "0.2.114"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
dependencies = [
"cfg-if",
"once_cell",
@@ -3525,9 +3522,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.114"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3535,9 +3532,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.114"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -3548,9 +3545,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.114"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b"
dependencies = [
"unicode-ident",
]
@@ -3591,9 +3588,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.91"
version = "0.3.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9"
checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -3788,9 +3785,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
[[package]]
name = "wio"
@@ -3891,9 +3888,9 @@ dependencies = [
[[package]]
name = "writeable"
version = "0.6.2"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "xxhash-rust"
@@ -3914,9 +3911,9 @@ dependencies = [
[[package]]
name = "yoke"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
dependencies = [
"stable_deref_trait",
"yoke-derive",
@@ -3925,9 +3922,9 @@ dependencies = [
[[package]]
name = "yoke-derive"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [
"proc-macro2",
"quote",
@@ -3937,18 +3934,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.47"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.47"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
@@ -3957,18 +3954,18 @@ dependencies = [
[[package]]
name = "zerofrom"
version = "0.1.6"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.6"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [
"proc-macro2",
"quote",
@@ -3984,9 +3981,9 @@ checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
[[package]]
name = "zerotrie"
version = "0.2.3"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
dependencies = [
"displaydoc",
"yoke",
@@ -3995,9 +3992,9 @@ dependencies = [
[[package]]
name = "zerovec"
version = "0.11.5"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
dependencies = [
"yoke",
"zerofrom",
@@ -4006,9 +4003,9 @@ dependencies = [
[[package]]
name = "zerovec-derive"
version = "0.11.2"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
dependencies = [
"proc-macro2",
"quote",
+28 -27
View File
@@ -4,7 +4,7 @@ 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.2.0"
package.version = "0.3.0-alpha.3"
package.homepage = "https://bitcoinresearchkit.org"
package.repository = "https://github.com/bitcoinresearchkit/brk"
package.readme = "README.md"
@@ -40,42 +40,42 @@ aide = { version = "0.16.0-alpha.3", features = ["axum-json", "axum-query"] }
axum = { version = "0.8.8", default-features = false, features = ["http1", "json", "query", "tokio", "tracing"] }
bitcoin = { version = "0.32.8", features = ["serde"] }
bitcoincore-rpc = "0.19.0"
brk_alloc = { version = "0.2.0", path = "crates/brk_alloc" }
brk_bencher = { version = "0.2.0", path = "crates/brk_bencher" }
brk_bindgen = { version = "0.2.0", path = "crates/brk_bindgen" }
brk_cli = { version = "0.2.0", path = "crates/brk_cli" }
brk_client = { version = "0.2.0", path = "crates/brk_client" }
brk_cohort = { version = "0.2.0", path = "crates/brk_cohort" }
brk_computer = { version = "0.2.0", path = "crates/brk_computer" }
brk_error = { version = "0.2.0", path = "crates/brk_error" }
brk_fetcher = { version = "0.2.0", path = "crates/brk_fetcher" }
brk_indexer = { version = "0.2.0", path = "crates/brk_indexer" }
brk_iterator = { version = "0.2.0", path = "crates/brk_iterator" }
brk_logger = { version = "0.2.0", path = "crates/brk_logger" }
brk_mempool = { version = "0.2.0", path = "crates/brk_mempool" }
brk_oracle = { version = "0.2.0", path = "crates/brk_oracle" }
brk_query = { version = "0.2.0", path = "crates/brk_query", features = ["tokio"] }
brk_reader = { version = "0.2.0", path = "crates/brk_reader" }
brk_rpc = { version = "0.2.0", path = "crates/brk_rpc" }
brk_server = { version = "0.2.0", path = "crates/brk_server" }
brk_store = { version = "0.2.0", path = "crates/brk_store" }
brk_traversable = { version = "0.2.0", path = "crates/brk_traversable", features = ["pco", "derive"] }
brk_traversable_derive = { version = "0.2.0", path = "crates/brk_traversable_derive" }
brk_types = { version = "0.2.0", path = "crates/brk_types" }
brk_website = { version = "0.2.0", path = "crates/brk_website" }
brk_alloc = { version = "0.3.0-alpha.3", path = "crates/brk_alloc" }
brk_bencher = { version = "0.3.0-alpha.3", path = "crates/brk_bencher" }
brk_bindgen = { version = "0.3.0-alpha.3", path = "crates/brk_bindgen" }
brk_cli = { version = "0.3.0-alpha.3", path = "crates/brk_cli" }
brk_client = { version = "0.3.0-alpha.3", path = "crates/brk_client" }
brk_cohort = { version = "0.3.0-alpha.3", path = "crates/brk_cohort" }
brk_computer = { version = "0.3.0-alpha.3", path = "crates/brk_computer" }
brk_error = { version = "0.3.0-alpha.3", path = "crates/brk_error" }
brk_fetcher = { version = "0.3.0-alpha.3", path = "crates/brk_fetcher" }
brk_indexer = { version = "0.3.0-alpha.3", path = "crates/brk_indexer" }
brk_iterator = { version = "0.3.0-alpha.3", path = "crates/brk_iterator" }
brk_logger = { version = "0.3.0-alpha.3", path = "crates/brk_logger" }
brk_mempool = { version = "0.3.0-alpha.3", path = "crates/brk_mempool" }
brk_oracle = { version = "0.3.0-alpha.3", path = "crates/brk_oracle" }
brk_query = { version = "0.3.0-alpha.3", path = "crates/brk_query", features = ["tokio"] }
brk_reader = { version = "0.3.0-alpha.3", path = "crates/brk_reader" }
brk_rpc = { version = "0.3.0-alpha.3", path = "crates/brk_rpc" }
brk_server = { version = "0.3.0-alpha.3", path = "crates/brk_server" }
brk_store = { version = "0.3.0-alpha.3", path = "crates/brk_store" }
brk_traversable = { version = "0.3.0-alpha.3", path = "crates/brk_traversable", features = ["pco", "derive"] }
brk_traversable_derive = { version = "0.3.0-alpha.3", path = "crates/brk_traversable_derive" }
brk_types = { version = "0.3.0-alpha.3", path = "crates/brk_types" }
brk_website = { version = "0.3.0-alpha.3", path = "crates/brk_website" }
byteview = "0.10.1"
color-eyre = "0.6.5"
corepc-client = { package = "brk-corepc-client", version = "0.11.0", features = ["client-sync"] }
corepc-jsonrpc = { package = "brk-corepc-jsonrpc", version = "0.19.0", features = ["simple_http"], default-features = false }
derive_more = { version = "2.1.1", features = ["deref", "deref_mut"] }
fjall = "3.1.2"
fjall = "=3.0.4"
indexmap = { version = "2.13.0", features = ["serde"] }
jiff = { version = "0.2.23", features = ["perf-inline", "tz-system"], default-features = false }
owo-colors = "4.3.0"
parking_lot = "0.12.5"
pco = "1.0.1"
rayon = "1.11.0"
rustc-hash = "2.1.1"
rustc-hash = "2.1.2"
schemars = { version = "1.2.1", features = ["indexmap2"] }
serde = "1.0.228"
serde_bytes = "0.11.19"
@@ -87,7 +87,7 @@ tower-http = { version = "0.6.8", features = ["catch-panic", "compression-br", "
tower-layer = "0.3"
tracing = { version = "0.1", default-features = false, features = ["std"] }
ureq = { version = "3.3.0", features = ["json"] }
vecdb = { version = "0.7.2", features = ["derive", "serde_json", "pco", "schemars"] }
vecdb = { version = "0.9.3", features = ["derive", "serde_json", "pco", "schemars"] }
# vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] }
[workspace.metadata.release]
@@ -95,6 +95,7 @@ shared-version = true
tag-name = "v{{version}}"
pre-release-commit-message = "release: v{{version}}"
tag-message = "release: v{{version}}"
allow-branch = ["main", "next"]
[workspace.metadata.dist]
cargo-dist-version = "0.30.2"
+1 -1
View File
@@ -12,6 +12,6 @@ brk_cohort = { workspace = true }
brk_query = { workspace = true }
brk_types = { workspace = true }
indexmap = { workspace = true }
oas3 = "0.20"
oas3 = "0.21"
serde = { workspace = true }
serde_json = { workspace = true }
+293 -66
View File
@@ -108,7 +108,13 @@ fn fill_mixed_empty_field_parts(
// Recurse first (bottom-up)
for (field_name, child_node) in children {
let child_path = build_child_path(path, field_name);
fill_mixed_empty_field_parts(child_node, &child_path, pattern_lookup, patterns, node_bases);
fill_mixed_empty_field_parts(
child_node,
&child_path,
pattern_lookup,
patterns,
node_bases,
);
}
// Check if this node has mixed empty/non-empty field_parts
@@ -351,16 +357,18 @@ fn try_embedded_disc(
}
/// Strategy 2: suffix discriminator (e.g., all field_parts differ by `_4y` suffix)
fn try_suffix_disc(
majority: &[&InstanceAnalysis],
fields: &[PatternField],
) -> Option<PatternMode> {
fn try_suffix_disc(majority: &[&InstanceAnalysis], fields: &[PatternField]) -> Option<PatternMode> {
let first = &majority[0];
// Use a non-empty field to detect the suffix
let ref_field = fields
.iter()
.find(|f| first.field_parts.get(&f.name).is_some_and(|v| !v.is_empty()))
.find(|f| {
first
.field_parts
.get(&f.name)
.is_some_and(|v| !v.is_empty())
})
.map(|f| &f.name)?;
let ref_first = first.field_parts.get(ref_field)?;
@@ -763,19 +771,51 @@ mod tests {
fn test_embedded_disc_percentile_bands() {
use std::collections::BTreeSet;
let fields = vec![
PatternField { name: "bps".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField { name: "price".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField { name: "ratio".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField {
name: "bps".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
PatternField {
name: "price".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
PatternField {
name: "ratio".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
];
let pct99 = InstanceAnalysis {
base: "realized_price".into(),
field_parts: [("bps".into(), "ratio_pct99_bps".into()), ("price".into(), "pct99".into()), ("ratio".into(), "ratio_pct99".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [
("bps".into(), "ratio_pct99_bps".into()),
("price".into(), "pct99".into()),
("ratio".into(), "ratio_pct99".into()),
]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let pct1 = InstanceAnalysis {
base: "realized_price".into(),
field_parts: [("bps".into(), "ratio_pct1_bps".into()), ("price".into(), "pct1".into()), ("ratio".into(), "ratio_pct1".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [
("bps".into(), "ratio_pct1_bps".into()),
("price".into(), "pct1".into()),
("ratio".into(), "ratio_pct1".into()),
]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let mode = determine_pattern_mode(&[pct99, pct1], &fields);
assert!(mode.is_some());
@@ -793,19 +833,51 @@ mod tests {
fn test_suffix_disc_period_windows() {
use std::collections::BTreeSet;
let fields = vec![
PatternField { name: "p1sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField { name: "sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField { name: "zscore".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField {
name: "p1sd".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
PatternField {
name: "sd".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
PatternField {
name: "zscore".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
];
let all_time = InstanceAnalysis {
base: "realized_price".into(),
field_parts: [("p1sd".into(), "p1sd".into()), ("sd".into(), "ratio_sd".into()), ("zscore".into(), "ratio_zscore".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [
("p1sd".into(), "p1sd".into()),
("sd".into(), "ratio_sd".into()),
("zscore".into(), "ratio_zscore".into()),
]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let four_year = InstanceAnalysis {
base: "realized_price".into(),
field_parts: [("p1sd".into(), "p1sd_4y".into()), ("sd".into(), "ratio_sd_4y".into()), ("zscore".into(), "ratio_zscore_4y".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [
("p1sd".into(), "p1sd_4y".into()),
("sd".into(), "ratio_sd_4y".into()),
("zscore".into(), "ratio_zscore_4y".into()),
]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let mode = determine_pattern_mode(&[all_time, four_year], &fields);
assert!(mode.is_some());
@@ -823,18 +895,39 @@ mod tests {
fn test_suffix_disc_with_empty_fields() {
use std::collections::BTreeSet;
let fields = vec![
PatternField { name: "band".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField { name: "sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField {
name: "band".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
PatternField {
name: "sd".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
];
let all_time = InstanceAnalysis {
base: "price".into(),
field_parts: [("band".into(), "".into()), ("sd".into(), "ratio_sd".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [("band".into(), "".into()), ("sd".into(), "ratio_sd".into())]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let four_year = InstanceAnalysis {
base: "price".into(),
field_parts: [("band".into(), "".into()), ("sd".into(), "ratio_sd_4y".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [
("band".into(), "".into()),
("sd".into(), "ratio_sd_4y".into()),
]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let mode = determine_pattern_mode(&[all_time, four_year], &fields);
assert!(mode.is_some());
@@ -851,18 +944,39 @@ mod tests {
fn test_suffix_disc_empty_to_nonempty() {
use std::collections::BTreeSet;
let fields = vec![
PatternField { name: "all".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField { name: "sth".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField {
name: "all".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
PatternField {
name: "sth".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
];
let regular = InstanceAnalysis {
base: "supply".into(),
field_parts: [("all".into(), "".into()), ("sth".into(), "sth_".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [("all".into(), "".into()), ("sth".into(), "sth_".into())]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let profitability = InstanceAnalysis {
base: "utxos_in_profit".into(),
field_parts: [("all".into(), "supply".into()), ("sth".into(), "sth_supply".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [
("all".into(), "supply".into()),
("sth".into(), "sth_supply".into()),
]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let mode = determine_pattern_mode(&[regular, profitability], &fields);
assert!(mode.is_some());
@@ -879,43 +993,91 @@ mod tests {
fn test_outlier_rejects_pattern() {
use std::collections::BTreeSet;
let fields = vec![
PatternField { name: "ratio".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField { name: "value".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField {
name: "ratio".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
PatternField {
name: "value".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
];
// SOPR case: one instance has outlier naming (no common prefix)
let normal = InstanceAnalysis {
base: "series".into(),
field_parts: [("ratio".into(), "ratio".into()), ("value".into(), "value".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [
("ratio".into(), "ratio".into()),
("value".into(), "value".into()),
]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let outlier = InstanceAnalysis {
base: "".into(),
field_parts: [("ratio".into(), "asopr".into()), ("value".into(), "adj_value".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: true,
field_parts: [
("ratio".into(), "asopr".into()),
("value".into(), "adj_value".into()),
]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: true,
};
let mode = determine_pattern_mode(&[normal, outlier], &fields);
assert!(mode.is_some(), "Outlier should be filtered out, leaving a valid pattern from non-outlier instances");
assert!(
mode.is_some(),
"Outlier should be filtered out, leaving a valid pattern from non-outlier instances"
);
}
#[test]
fn test_unanimity_rejects_disagreeing_instances() {
use std::collections::BTreeSet;
let fields = vec![
PatternField { name: "a".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField { name: "b".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField {
name: "a".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
PatternField {
name: "b".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
];
let inst1 = InstanceAnalysis {
base: "x".into(),
field_parts: [("a".into(), "foo".into()), ("b".into(), "bar".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [("a".into(), "foo".into()), ("b".into(), "bar".into())]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let inst2 = InstanceAnalysis {
base: "y".into(),
field_parts: [("a".into(), "baz".into()), ("b".into(), "qux".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [("a".into(), "baz".into()), ("b".into(), "qux".into())]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let mode = determine_pattern_mode(&[inst1, inst2], &fields);
assert!(mode.is_none(), "Should be non-parameterizable when no pattern detected");
assert!(
mode.is_none(),
"Should be non-parameterizable when no pattern detected"
);
}
#[test]
@@ -925,20 +1087,43 @@ mod tests {
// Should keep identity (empty parts) so both children receive acc unchanged.
use std::collections::BTreeSet;
let fields = vec![
PatternField { name: "absolute".into(), rust_type: "TypeA".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField { name: "rate".into(), rust_type: "TypeB".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField {
name: "absolute".into(),
rust_type: "TypeA".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
PatternField {
name: "rate".into(),
rust_type: "TypeB".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
];
let inst = InstanceAnalysis {
base: "supply_delta".into(),
field_parts: [("absolute".into(), "".into()), ("rate".into(), "".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: false,
field_parts: [("absolute".into(), "".into()), ("rate".into(), "".into())]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: false,
};
let mode = determine_pattern_mode(&[inst], &fields);
assert!(mode.is_some());
match mode.unwrap() {
PatternMode::Suffix { relatives } => {
assert_eq!(relatives.get("absolute"), Some(&"".to_string()), "absolute should be identity");
assert_eq!(relatives.get("rate"), Some(&"".to_string()), "rate should be identity");
assert_eq!(
relatives.get("absolute"),
Some(&"".to_string()),
"absolute should be identity"
);
assert_eq!(
relatives.get("rate"),
Some(&"".to_string()),
"rate should be identity"
);
}
other => panic!("Expected Suffix with identity, got {:?}", other),
}
@@ -975,16 +1160,26 @@ mod tests {
// Parent patterns containing non-parameterizable children should also
// be detected via metadata.is_parameterizable (recursive check).
use std::collections::BTreeSet;
let fields = vec![
PatternField { name: "a".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
];
let fields = vec![PatternField {
name: "a".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
}];
let inst = InstanceAnalysis {
base: "".into(),
field_parts: [("a".into(), "standalone_name".into())].into_iter().collect(),
is_suffix_mode: true, has_outlier: true,
field_parts: [("a".into(), "standalone_name".into())]
.into_iter()
.collect(),
is_suffix_mode: true,
has_outlier: true,
};
let mode = determine_pattern_mode(&[inst], &fields);
assert!(mode.is_none(), "Pattern with outlier should be non-parameterizable");
assert!(
mode.is_none(),
"Pattern with outlier should be non-parameterizable"
);
}
#[test]
@@ -998,9 +1193,27 @@ mod tests {
let pattern = StructuralPattern {
name: "TestPattern".into(),
fields: vec![
PatternField { name: "_0sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField { name: "p1sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField { name: "sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
PatternField {
name: "_0sd".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
PatternField {
name: "p1sd".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
PatternField {
name: "sd".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
type_param: None,
},
],
mode: Some(PatternMode::Templated {
templates: [
@@ -1059,9 +1272,15 @@ mod tests {
assert_eq!(analysis.field_parts.get("loss"), Some(&"".to_string()));
assert_eq!(analysis.field_parts.get("supply"), Some(&"".to_string()));
// others should be non-empty
assert_eq!(analysis.field_parts.get("cap"), Some(&"realized_cap".to_string()));
assert_eq!(
analysis.field_parts.get("cap"),
Some(&"realized_cap".to_string())
);
assert_eq!(analysis.field_parts.get("mvrv"), Some(&"mvrv".to_string()));
assert_eq!(analysis.field_parts.get("price"), Some(&"realized_price".to_string()));
assert_eq!(
analysis.field_parts.get("price"),
Some(&"realized_price".to_string())
);
}
#[test]
@@ -1111,12 +1330,20 @@ mod tests {
&mut path_to_pattern,
);
let result = node_bases.get("test").expect("should have node_bases entry");
let result = node_bases
.get("test")
.expect("should have node_bases entry");
assert_eq!(result.base, "utxos");
assert!(!result.has_outlier);
assert_eq!(result.field_parts.get("cap"), Some(&"realized_cap".to_string()));
assert_eq!(
result.field_parts.get("cap"),
Some(&"realized_cap".to_string())
);
assert_eq!(result.field_parts.get("mvrv"), Some(&"mvrv".to_string()));
// loss branch returns base "utxos_realized_loss" which yields field_part "realized_loss"
assert_eq!(result.field_parts.get("loss"), Some(&"realized_loss".to_string()));
assert_eq!(
result.field_parts.get("loss"),
Some(&"realized_loss".to_string())
);
}
}
+7 -4
View File
@@ -6,9 +6,9 @@
use std::collections::BTreeMap;
use brk_cohort::{
AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, CLASS_NAMES, EPOCH_NAMES, LOSS_NAMES,
OVER_AGE_NAMES, OVER_AMOUNT_NAMES, PROFITABILITY_RANGE_NAMES, PROFIT_NAMES,
SPENDABLE_TYPE_NAMES, TERM_NAMES, UNDER_AGE_NAMES, UNDER_AMOUNT_NAMES,
AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, CLASS_NAMES, EPOCH_NAMES, LOSS_NAMES, OVER_AGE_NAMES,
OVER_AMOUNT_NAMES, PROFIT_NAMES, PROFITABILITY_RANGE_NAMES, SPENDABLE_TYPE_NAMES, TERM_NAMES,
UNDER_AGE_NAMES, UNDER_AMOUNT_NAMES,
};
use brk_types::{Index, PoolSlug, pools};
use serde::Serialize;
@@ -64,7 +64,10 @@ impl CohortConstants {
("AMOUNT_RANGE_NAMES", to_value(&AMOUNT_RANGE_NAMES)),
("OVER_AMOUNT_NAMES", to_value(&OVER_AMOUNT_NAMES)),
("UNDER_AMOUNT_NAMES", to_value(&UNDER_AMOUNT_NAMES)),
("PROFITABILITY_RANGE_NAMES", to_value(&PROFITABILITY_RANGE_NAMES)),
(
"PROFITABILITY_RANGE_NAMES",
to_value(&PROFITABILITY_RANGE_NAMES),
),
("PROFIT_NAMES", to_value(&PROFIT_NAMES)),
("LOSS_NAMES", to_value(&LOSS_NAMES)),
]
+4 -4
View File
@@ -8,7 +8,9 @@ use std::fmt::Write;
use brk_types::SeriesLeafWithSchema;
use crate::{ClientMetadata, LanguageSyntax, PatternBaseResult, PatternField, PatternMode, StructuralPattern};
use crate::{
ClientMetadata, LanguageSyntax, PatternBaseResult, PatternField, PatternMode, StructuralPattern,
};
/// Create a path suffix from a name.
fn path_suffix(name: &str) -> String {
@@ -33,9 +35,7 @@ fn compute_parameterized_value<S: LanguageSyntax>(
if let Some(child_pattern) = metadata.find_pattern(&field.rust_type)
&& child_pattern.is_templated()
{
let disc_template = pattern
.get_field_part(&field.name)
.unwrap_or(&field.name);
let disc_template = pattern.get_field_part(&field.name).unwrap_or(&field.name);
let disc_arg = syntax.disc_arg_expr(disc_template);
let acc_arg = syntax.owned_expr("acc");
return syntax.constructor(&field.rust_type, &format!("{acc_arg}, {disc_arg}"));
+2 -2
View File
@@ -125,8 +125,8 @@ pub fn prepare_tree_node<'a>(
p.is_suffix_mode() == base_result.is_suffix_mode
&& p.field_parts_match(&base_result.field_parts)
});
let is_parameterizable = matching_pattern
.is_none_or(|p| metadata.is_parameterizable(&p.name));
let is_parameterizable =
matching_pattern.is_none_or(|p| metadata.is_parameterizable(&p.name));
// should_inline determines if we generate an inline struct type
let should_inline = !is_leaf
@@ -82,18 +82,19 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
} else {
writeln!(output, " const params = new URLSearchParams();").unwrap();
for param in &endpoint.query_params {
let ident = sanitize_ident(&param.name);
if param.required {
writeln!(
output,
" params.set('{}', String({}));",
param.name, param.name
param.name, ident
)
.unwrap();
} else {
writeln!(
output,
" if ({} !== undefined) params.set('{}', String({}));",
param.name, param.name, param.name
ident, param.name, ident
)
.unwrap();
}
@@ -127,14 +128,19 @@ fn endpoint_to_method_name(endpoint: &Endpoint) -> String {
fn build_method_params(endpoint: &Endpoint) -> String {
let mut params = Vec::new();
for param in &endpoint.path_params {
params.push(param.name.clone());
params.push(sanitize_ident(&param.name));
}
for param in &endpoint.query_params {
params.push(param.name.clone());
params.push(sanitize_ident(&param.name));
}
params.join(", ")
}
/// Strip characters invalid in JS identifiers (e.g. `[]` from `txId[]`).
fn sanitize_ident(name: &str) -> String {
name.replace(['[', ']'], "")
}
fn build_path_template(path: &str, path_params: &[Parameter]) -> String {
let mut result = path.to_string();
for param in path_params {
@@ -22,6 +22,20 @@ pub fn generate_base_client(output: &mut String) {
const _isBrowser = typeof window !== 'undefined' && 'caches' in window;
const _runIdle = (/** @type {{VoidFunction}} */ fn) => (globalThis.requestIdleCallback ?? setTimeout)(fn);
const _defaultCacheName = '__BRK_CLIENT__';
/** @param {{*}} v */
const _addCamelGetters = (v) => {{
if (Array.isArray(v)) {{ v.forEach(_addCamelGetters); return v; }}
if (v && typeof v === 'object' && v.constructor === Object) {{
for (const k in v) {{
if (k.includes('_')) {{
const c = k.replace(/_([a-z])/g, (_, l) => l.toUpperCase());
if (!(c in v)) Object.defineProperty(v, c, {{ get() {{ return this[k]; }} }});
}}
_addCamelGetters(v[k]);
}}
}}
return v;
}};
/**
* @param {{string|boolean|undefined}} cache
@@ -418,7 +432,7 @@ class BrkClientBase {{
const cachePromise = cache?.match(url).then(async (res) => {{
cachedRes = res ?? null;
if (!res) return null;
const json = await res.json();
const json = _addCamelGetters(await res.json());
if (!resolved && onUpdate) {{
resolved = true;
onUpdate(json);
@@ -428,7 +442,7 @@ class BrkClientBase {{
const networkPromise = this.get(path).then(async (res) => {{
const cloned = res.clone();
const json = await res.json();
const json = _addCamelGetters(await res.json());
// Skip update if ETag matches and cache already delivered
if (cachedRes?.headers.get('ETag') === res.headers.get('ETag')) {{
if (!resolved && onUpdate) {{
@@ -726,7 +740,12 @@ pub fn generate_structural_patterns(
writeln!(output, " */").unwrap();
if pattern.is_templated() {
writeln!(output, "function create{}(client, acc, disc) {{", pattern.name).unwrap();
writeln!(
output,
"function create{}(client, acc, disc) {{",
pattern.name
)
.unwrap();
} else {
writeln!(output, "function create{}(client, acc) {{", pattern.name).unwrap();
}
+12 -10
View File
@@ -56,11 +56,7 @@ pub fn generate_main_client(output: &mut String, endpoints: &[Endpoint]) {
)
.unwrap();
writeln!(output, " \"\"\"").unwrap();
writeln!(
output,
" return SeriesEndpoint(self, series, index)"
)
.unwrap();
writeln!(output, " return SeriesEndpoint(self, series, index)").unwrap();
writeln!(output).unwrap();
// Generate helper methods
@@ -105,7 +101,7 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
.response_type
.as_deref()
.map(js_type_to_python)
.unwrap_or_else(|| "Any".to_string()),
.unwrap_or_else(|| "str".to_string()),
);
let return_type = if endpoint.supports_csv {
@@ -163,11 +159,17 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
// Build path
let path = build_path_template(&endpoint.path, &endpoint.path_params);
let fetch_method = if endpoint.returns_json() {
"get_json"
} else {
"get_text"
};
if endpoint.query_params.is_empty() {
if endpoint.path_params.is_empty() {
writeln!(output, " return self.get_json('{}')", path).unwrap();
writeln!(output, " return self.{}('{}')", fetch_method, path).unwrap();
} else {
writeln!(output, " return self.get_json(f'{}')", path).unwrap();
writeln!(output, " return self.{}(f'{}')", fetch_method, path).unwrap();
}
} else {
writeln!(output, " params = []").unwrap();
@@ -201,9 +203,9 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
if endpoint.supports_csv {
writeln!(output, " if format == 'csv':").unwrap();
writeln!(output, " return self.get_text(path)").unwrap();
writeln!(output, " return self.get_json(path)").unwrap();
writeln!(output, " return self.{}(path)", fetch_method).unwrap();
} else {
writeln!(output, " return self.get_json(path)").unwrap();
writeln!(output, " return self.{}(path)", fetch_method).unwrap();
}
}
@@ -684,7 +684,6 @@ pub fn generate_structural_patterns(
writeln!(output, "# Reusable structural pattern classes\n").unwrap();
for pattern in patterns {
// Generate class
if pattern.is_generic {
writeln!(output, "class {}(Generic[T]):", pattern.name).unwrap();
+36 -12
View File
@@ -93,7 +93,7 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
.response_type
.as_deref()
.map(js_type_to_rust)
.unwrap_or_else(|| "serde_json::Value".to_string());
.unwrap_or_else(|| "String".to_string());
let return_type = if endpoint.supports_csv {
format!("FormatResponse<{}>", base_return_type)
@@ -132,29 +132,43 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
.unwrap();
let (path, index_arg) = build_path_template(endpoint);
let fetch_method = if endpoint.returns_json() {
"get_json"
} else {
"get_text"
};
if endpoint.query_params.is_empty() {
writeln!(
output,
" self.base.get_json(&format!(\"{}\"{}))",
path, index_arg
" self.base.{}(&format!(\"{}\"{}))",
fetch_method, path, index_arg
)
.unwrap();
} else {
writeln!(output, " let mut query = Vec::new();").unwrap();
for param in &endpoint.query_params {
if param.required {
let ident = sanitize_ident(&param.name);
let is_array = param.param_type.ends_with("[]");
if is_array {
writeln!(
output,
" for v in {} {{ query.push(format!(\"{}={{}}\", v)); }}",
ident, param.name
)
.unwrap();
} else if param.required {
writeln!(
output,
" query.push(format!(\"{}={{}}\", {}));",
param.name, param.name
param.name, ident
)
.unwrap();
} else {
writeln!(
output,
" if let Some(v) = {} {{ query.push(format!(\"{}={{}}\", v)); }}",
param.name, param.name
ident, param.name
)
.unwrap();
}
@@ -177,12 +191,13 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
writeln!(output, " }} else {{").unwrap();
writeln!(
output,
" self.base.get_json(&path).map(FormatResponse::Json)"
" self.base.{}(&path).map(FormatResponse::Json)",
fetch_method
)
.unwrap();
writeln!(output, " }}").unwrap();
} else {
writeln!(output, " self.base.get_json(&path)").unwrap();
writeln!(output, " self.base.{}(&path)", fetch_method).unwrap();
}
}
@@ -198,26 +213,35 @@ fn build_method_params(endpoint: &Endpoint) -> String {
let mut params = Vec::new();
for param in &endpoint.path_params {
let rust_type = param_type_to_rust(&param.param_type);
params.push(format!(", {}: {}", param.name, rust_type));
params.push(format!(", {}: {}", sanitize_ident(&param.name), rust_type));
}
for param in &endpoint.query_params {
let rust_type = param_type_to_rust(&param.param_type);
let name = sanitize_ident(&param.name);
if param.required {
params.push(format!(", {}: {}", param.name, rust_type));
params.push(format!(", {}: {}", name, rust_type));
} else {
params.push(format!(", {}: Option<{}>", param.name, rust_type));
params.push(format!(", {}: Option<{}>", name, rust_type));
}
}
params.join("")
}
/// Strip characters invalid in Rust identifiers (e.g. `[]` from `txId[]`).
fn sanitize_ident(name: &str) -> String {
name.replace(['[', ']'], "")
}
/// Convert parameter type to Rust type for function signatures.
fn param_type_to_rust(param_type: &str) -> String {
if let Some(inner) = param_type.strip_suffix("[]") {
return format!("&[{}]", param_type_to_rust(inner));
}
match param_type {
"string" | "*" => "&str".to_string(),
"integer" | "number" => "i64".to_string(),
"boolean" => "bool".to_string(),
other => other.to_string(), // Domain types like Index, SeriesName, Format
other => other.to_string(),
}
}
+5
View File
@@ -43,6 +43,11 @@ impl Endpoint {
self.method == "GET" && !self.deprecated
}
/// Returns true if this endpoint returns JSON (has a response_type extracted from application/json).
pub fn returns_json(&self) -> bool {
self.response_type.is_some()
}
/// Returns the operation ID or generates one from the path.
/// The returned string uses the raw case from the spec (typically camelCase).
pub fn operation_name(&self) -> String {
+3
View File
@@ -74,6 +74,9 @@ pub fn escape_python_keyword(name: &str) -> String {
"try", "while", "with", "yield",
];
// Strip characters invalid in identifiers (e.g. `[]` from `txId[]`)
let name = name.replace(['[', ']'], "");
// Prefix with underscore if starts with digit
let name = if name.starts_with(|c: char| c.is_ascii_digit()) {
format!("_{}", name)
+1 -2
View File
@@ -13,7 +13,6 @@ brk_alloc = { workspace = true }
brk_computer = { workspace = true }
brk_error = { workspace = true, features = ["tokio", "vecdb"] }
brk_indexer = { workspace = true }
brk_iterator = { workspace = true }
brk_logger = { workspace = true }
brk_mempool = { workspace = true }
brk_query = { workspace = true }
@@ -26,7 +25,7 @@ owo-colors = { workspace = true }
tracing = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
toml = "1.0.7"
toml = "1.1.2"
vecdb = { workspace = true }
[[bin]]
+2 -5
View File
@@ -1,11 +1,8 @@
# BRK CLI
Command-line interface for running a Bitcoin Research Kit instance.
Run your own Bitcoin Research Kit instance. One binary, one command. Full sync in ~4-7h depending on hardware. ~44% disk overhead vs 250% for mempool/electrs.
## Demo
- [bitview.space](https://bitview.space) - web interface
- [bitview.space/api](https://bitview.space/api) - API docs
[bitview.space](https://bitview.space) is the official free hosted instance.
## Requirements
+4 -7
View File
@@ -10,7 +10,6 @@ use brk_alloc::Mimalloc;
use brk_computer::Computer;
use brk_error::Result;
use brk_indexer::Indexer;
use brk_iterator::Blocks;
use brk_mempool::Mempool;
use brk_query::AsyncQuery;
use brk_reader::Reader;
@@ -37,8 +36,6 @@ pub fn main() -> anyhow::Result<()> {
let reader = Reader::new(config.blocksdir(), &client);
let blocks = Blocks::new(&client, &reader);
let mut indexer = Indexer::forced_import(&config.brkdir())?;
#[cfg(not(debug_assertions))]
@@ -52,7 +49,7 @@ pub fn main() -> anyhow::Result<()> {
info!("Indexing {blocks_behind} blocks before starting server...");
info!("---");
sleep(Duration::from_secs(10));
indexer.index(&blocks, &client, &exit)?;
indexer.index(&reader, &client, &exit)?;
drop(indexer);
Mimalloc::collect();
indexer = Indexer::forced_import(&config.brkdir())?;
@@ -102,14 +99,14 @@ pub fn main() -> anyhow::Result<()> {
let total_start = Instant::now();
let starting_indexes = if cfg!(debug_assertions) {
indexer.checked_index(&blocks, &client, &exit)?
indexer.checked_index(&reader, &client, &exit)?
} else {
indexer.index(&blocks, &client, &exit)?
indexer.index(&reader, &client, &exit)?
};
Mimalloc::collect();
computer.compute(&indexer, starting_indexes, &reader, &exit)?;
computer.compute(&indexer, starting_indexes, &exit)?;
info!("Total time: {:?}", total_start.elapsed());
info!("Waiting for new blocks...");
File diff suppressed because it is too large Load Diff
+3
View File
@@ -14,3 +14,6 @@ brk_traversable = { workspace = true }
vecdb = { workspace = true }
rayon = { workspace = true }
serde = { workspace = true }
[package.metadata.cargo-machete]
ignored = ["vecdb"]
+3 -2
View File
@@ -75,7 +75,8 @@ impl<T> AddrGroups<T> {
}
pub fn iter_overlapping_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.under_amount.iter_mut().chain(self.over_amount.iter_mut())
self.under_amount
.iter_mut()
.chain(self.over_amount.iter_mut())
}
}
+23 -1
View File
@@ -201,7 +201,29 @@ impl<T> AgeRange<T> {
}
pub fn from_array(arr: [T; 21]) -> Self {
let [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20] = arr;
let [
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
a8,
a9,
a10,
a11,
a12,
a13,
a14,
a15,
a16,
a17,
a18,
a19,
a20,
] = arr;
Self {
under_1h: a0,
_1h_to_1d: a1,
+5 -1
View File
@@ -84,7 +84,11 @@ pub const AMOUNT_RANGE_NAMES: AmountRange<CohortName> = AmountRange {
_10sats_to_100sats: CohortName::new("10sats_to_100sats", "10-100 sats", "10-100 Sats"),
_100sats_to_1k_sats: CohortName::new("100sats_to_1k_sats", "100-1k sats", "100-1K Sats"),
_1k_sats_to_10k_sats: CohortName::new("1k_sats_to_10k_sats", "1k-10k sats", "1K-10K Sats"),
_10k_sats_to_100k_sats: CohortName::new("10k_sats_to_100k_sats", "10k-100k sats", "10K-100K Sats"),
_10k_sats_to_100k_sats: CohortName::new(
"10k_sats_to_100k_sats",
"10k-100k sats",
"10K-100K Sats",
),
_100k_sats_to_1m_sats: CohortName::new("100k_sats_to_1m_sats", "100k-1M sats", "100K-1M Sats"),
_1m_sats_to_10m_sats: CohortName::new("1m_sats_to_10m_sats", "1M-10M sats", "1M-10M Sats"),
_10m_sats_to_1btc: CohortName::new("10m_sats_to_1btc", "0.1-1 BTC", "0.1-1 BTC"),
+2 -2
View File
@@ -1,8 +1,8 @@
#![doc = include_str!("../README.md")]
mod addr;
mod amount_filter;
mod age_range;
mod amount_filter;
mod amount_range;
mod by_addr_type;
mod by_any_addr;
@@ -30,8 +30,8 @@ mod utxo;
pub use brk_types::{Age, Term};
pub use addr::*;
pub use amount_filter::*;
pub use age_range::*;
pub use amount_filter::*;
pub use amount_range::*;
pub use by_addr_type::*;
pub use by_any_addr::*;
+1 -1
View File
@@ -153,6 +153,6 @@ impl<T> Loss<T> {
.into_iter()
.rev()
.enumerate()
.map(move |(n, threshold)| (threshold, &ranges[len - 1 - n..]))
.map(move |(n, threshold)| (threshold, &ranges[len - 2 - n..]))
}
}
+18 -6
View File
@@ -43,19 +43,31 @@ pub const OVER_AMOUNT_NAMES: OverAmount<CohortName> = OverAmount {
pub const OVER_AMOUNT_FILTERS: OverAmount<Filter> = OverAmount {
_1sat: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1sat)),
_10sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10sats)),
_100sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._100sats)),
_1k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1k_sats)),
_10k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10k_sats)),
_100sats: Filter::Amount(AmountFilter::GreaterOrEqual(
OVER_AMOUNT_THRESHOLDS._100sats,
)),
_1k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
OVER_AMOUNT_THRESHOLDS._1k_sats,
)),
_10k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
OVER_AMOUNT_THRESHOLDS._10k_sats,
)),
_100k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
OVER_AMOUNT_THRESHOLDS._100k_sats,
)),
_1m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1m_sats)),
_10m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10m_sats)),
_1m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
OVER_AMOUNT_THRESHOLDS._1m_sats,
)),
_10m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
OVER_AMOUNT_THRESHOLDS._10m_sats,
)),
_1btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1btc)),
_10btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10btc)),
_100btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._100btc)),
_1k_btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1k_btc)),
_10k_btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10k_btc)),
_10k_btc: Filter::Amount(AmountFilter::GreaterOrEqual(
OVER_AMOUNT_THRESHOLDS._10k_btc,
)),
};
#[derive(Default, Clone, Traversable, Serialize)]
+21 -5
View File
@@ -16,10 +16,26 @@ pub const PROFIT_NAMES: Profit<CohortName> = Profit {
_70pct: CohortName::new("utxos_over_70pct_in_profit", ">=70%", "Over 70% in Profit"),
_80pct: CohortName::new("utxos_over_80pct_in_profit", ">=80%", "Over 80% in Profit"),
_90pct: CohortName::new("utxos_over_90pct_in_profit", ">=90%", "Over 90% in Profit"),
_100pct: CohortName::new("utxos_over_100pct_in_profit", ">=100%", "Over 100% in Profit"),
_200pct: CohortName::new("utxos_over_200pct_in_profit", ">=200%", "Over 200% in Profit"),
_300pct: CohortName::new("utxos_over_300pct_in_profit", ">=300%", "Over 300% in Profit"),
_500pct: CohortName::new("utxos_over_500pct_in_profit", ">=500%", "Over 500% in Profit"),
_100pct: CohortName::new(
"utxos_over_100pct_in_profit",
">=100%",
"Over 100% in Profit",
),
_200pct: CohortName::new(
"utxos_over_200pct_in_profit",
">=200%",
"Over 200% in Profit",
),
_300pct: CohortName::new(
"utxos_over_300pct_in_profit",
">=300%",
"Over 300% in Profit",
),
_500pct: CohortName::new(
"utxos_over_500pct_in_profit",
">=500%",
"Over 500% in Profit",
),
};
/// Number of profit thresholds.
@@ -192,6 +208,6 @@ impl<T> Profit<T> {
.into_iter()
.rev()
.enumerate()
.map(move |(n, threshold)| (threshold, &ranges[..n + 1]))
.map(move |(n, threshold)| (threshold, &ranges[..n + 2]))
}
}
+125 -25
View File
@@ -83,31 +83,131 @@ pub fn compute_profitability_boundaries(spot: Cents) -> [Cents; PROFITABILITY_BO
/// Profitability range names (25 ranges, from most profitable to most in loss)
pub const PROFITABILITY_RANGE_NAMES: ProfitabilityRange<CohortName> = ProfitabilityRange {
over_1000pct_in_profit: CohortName::new("utxos_over_1000pct_in_profit", "+>1000%", "Over 1000% in Profit"),
_500pct_to_1000pct_in_profit: CohortName::new("utxos_500pct_to_1000pct_in_profit", "+500-1000%", "500-1000% in Profit"),
_300pct_to_500pct_in_profit: CohortName::new("utxos_300pct_to_500pct_in_profit", "+300-500%", "300-500% in Profit"),
_200pct_to_300pct_in_profit: CohortName::new("utxos_200pct_to_300pct_in_profit", "+200-300%", "200-300% in Profit"),
_100pct_to_200pct_in_profit: CohortName::new("utxos_100pct_to_200pct_in_profit", "+100-200%", "100-200% in Profit"),
_90pct_to_100pct_in_profit: CohortName::new("utxos_90pct_to_100pct_in_profit", "+90-100%", "90-100% in Profit"),
_80pct_to_90pct_in_profit: CohortName::new("utxos_80pct_to_90pct_in_profit", "+80-90%", "80-90% in Profit"),
_70pct_to_80pct_in_profit: CohortName::new("utxos_70pct_to_80pct_in_profit", "+70-80%", "70-80% in Profit"),
_60pct_to_70pct_in_profit: CohortName::new("utxos_60pct_to_70pct_in_profit", "+60-70%", "60-70% in Profit"),
_50pct_to_60pct_in_profit: CohortName::new("utxos_50pct_to_60pct_in_profit", "+50-60%", "50-60% in Profit"),
_40pct_to_50pct_in_profit: CohortName::new("utxos_40pct_to_50pct_in_profit", "+40-50%", "40-50% in Profit"),
_30pct_to_40pct_in_profit: CohortName::new("utxos_30pct_to_40pct_in_profit", "+30-40%", "30-40% in Profit"),
_20pct_to_30pct_in_profit: CohortName::new("utxos_20pct_to_30pct_in_profit", "+20-30%", "20-30% in Profit"),
_10pct_to_20pct_in_profit: CohortName::new("utxos_10pct_to_20pct_in_profit", "+10-20%", "10-20% in Profit"),
_0pct_to_10pct_in_profit: CohortName::new("utxos_0pct_to_10pct_in_profit", "+0-10%", "0-10% in Profit"),
_0pct_to_10pct_in_loss: CohortName::new("utxos_0pct_to_10pct_in_loss", "-0-10%", "0-10% in Loss"),
_10pct_to_20pct_in_loss: CohortName::new("utxos_10pct_to_20pct_in_loss", "-10-20%", "10-20% in Loss"),
_20pct_to_30pct_in_loss: CohortName::new("utxos_20pct_to_30pct_in_loss", "-20-30%", "20-30% in Loss"),
_30pct_to_40pct_in_loss: CohortName::new("utxos_30pct_to_40pct_in_loss", "-30-40%", "30-40% in Loss"),
_40pct_to_50pct_in_loss: CohortName::new("utxos_40pct_to_50pct_in_loss", "-40-50%", "40-50% in Loss"),
_50pct_to_60pct_in_loss: CohortName::new("utxos_50pct_to_60pct_in_loss", "-50-60%", "50-60% in Loss"),
_60pct_to_70pct_in_loss: CohortName::new("utxos_60pct_to_70pct_in_loss", "-60-70%", "60-70% in Loss"),
_70pct_to_80pct_in_loss: CohortName::new("utxos_70pct_to_80pct_in_loss", "-70-80%", "70-80% in Loss"),
_80pct_to_90pct_in_loss: CohortName::new("utxos_80pct_to_90pct_in_loss", "-80-90%", "80-90% in Loss"),
_90pct_to_100pct_in_loss: CohortName::new("utxos_90pct_to_100pct_in_loss", "-90-100%", "90-100% in Loss"),
over_1000pct_in_profit: CohortName::new(
"utxos_over_1000pct_in_profit",
"+>1000%",
"Over 1000% in Profit",
),
_500pct_to_1000pct_in_profit: CohortName::new(
"utxos_500pct_to_1000pct_in_profit",
"+500-1000%",
"500-1000% in Profit",
),
_300pct_to_500pct_in_profit: CohortName::new(
"utxos_300pct_to_500pct_in_profit",
"+300-500%",
"300-500% in Profit",
),
_200pct_to_300pct_in_profit: CohortName::new(
"utxos_200pct_to_300pct_in_profit",
"+200-300%",
"200-300% in Profit",
),
_100pct_to_200pct_in_profit: CohortName::new(
"utxos_100pct_to_200pct_in_profit",
"+100-200%",
"100-200% in Profit",
),
_90pct_to_100pct_in_profit: CohortName::new(
"utxos_90pct_to_100pct_in_profit",
"+90-100%",
"90-100% in Profit",
),
_80pct_to_90pct_in_profit: CohortName::new(
"utxos_80pct_to_90pct_in_profit",
"+80-90%",
"80-90% in Profit",
),
_70pct_to_80pct_in_profit: CohortName::new(
"utxos_70pct_to_80pct_in_profit",
"+70-80%",
"70-80% in Profit",
),
_60pct_to_70pct_in_profit: CohortName::new(
"utxos_60pct_to_70pct_in_profit",
"+60-70%",
"60-70% in Profit",
),
_50pct_to_60pct_in_profit: CohortName::new(
"utxos_50pct_to_60pct_in_profit",
"+50-60%",
"50-60% in Profit",
),
_40pct_to_50pct_in_profit: CohortName::new(
"utxos_40pct_to_50pct_in_profit",
"+40-50%",
"40-50% in Profit",
),
_30pct_to_40pct_in_profit: CohortName::new(
"utxos_30pct_to_40pct_in_profit",
"+30-40%",
"30-40% in Profit",
),
_20pct_to_30pct_in_profit: CohortName::new(
"utxos_20pct_to_30pct_in_profit",
"+20-30%",
"20-30% in Profit",
),
_10pct_to_20pct_in_profit: CohortName::new(
"utxos_10pct_to_20pct_in_profit",
"+10-20%",
"10-20% in Profit",
),
_0pct_to_10pct_in_profit: CohortName::new(
"utxos_0pct_to_10pct_in_profit",
"+0-10%",
"0-10% in Profit",
),
_0pct_to_10pct_in_loss: CohortName::new(
"utxos_0pct_to_10pct_in_loss",
"-0-10%",
"0-10% in Loss",
),
_10pct_to_20pct_in_loss: CohortName::new(
"utxos_10pct_to_20pct_in_loss",
"-10-20%",
"10-20% in Loss",
),
_20pct_to_30pct_in_loss: CohortName::new(
"utxos_20pct_to_30pct_in_loss",
"-20-30%",
"20-30% in Loss",
),
_30pct_to_40pct_in_loss: CohortName::new(
"utxos_30pct_to_40pct_in_loss",
"-30-40%",
"30-40% in Loss",
),
_40pct_to_50pct_in_loss: CohortName::new(
"utxos_40pct_to_50pct_in_loss",
"-40-50%",
"40-50% in Loss",
),
_50pct_to_60pct_in_loss: CohortName::new(
"utxos_50pct_to_60pct_in_loss",
"-50-60%",
"50-60% in Loss",
),
_60pct_to_70pct_in_loss: CohortName::new(
"utxos_60pct_to_70pct_in_loss",
"-60-70%",
"60-70% in Loss",
),
_70pct_to_80pct_in_loss: CohortName::new(
"utxos_70pct_to_80pct_in_loss",
"-70-80%",
"70-80% in Loss",
),
_80pct_to_90pct_in_loss: CohortName::new(
"utxos_80pct_to_90pct_in_loss",
"-80-90%",
"80-90% in Loss",
),
_90pct_to_100pct_in_loss: CohortName::new(
"utxos_90pct_to_100pct_in_loss",
"-90-100%",
"90-100% in Loss",
),
};
impl ProfitabilityRange<CohortName> {
+2 -2
View File
@@ -2,8 +2,8 @@ use brk_traversable::Traversable;
use rayon::prelude::*;
use crate::{
AgeRange, AmountRange, ByEpoch, OverAmount, UnderAmount, UnderAge, OverAge,
Class, SpendableType, ByTerm, Filter,
AgeRange, AmountRange, ByEpoch, ByTerm, Class, Filter, OverAge, OverAmount, SpendableType,
UnderAge, UnderAmount,
};
#[derive(Default, Clone, Traversable)]
+1 -3
View File
@@ -14,11 +14,8 @@ brk_error = { workspace = true, features = ["vecdb"] }
brk_cohort = { workspace = true }
brk_indexer = { workspace = true }
brk_oracle = { workspace = true }
brk_iterator = { workspace = true }
brk_logger = { workspace = true }
brk_reader = { workspace = true }
brk_rpc = { workspace = true, features = ["corepc"] }
brk_store = { workspace = true }
brk_traversable = { workspace = true }
brk_types = { workspace = true }
derive_more = { workspace = true }
@@ -33,6 +30,7 @@ smallvec = { workspace = true }
vecdb = { workspace = true }
[dev-dependencies]
brk_reader = { workspace = true }
brk_alloc = { workspace = true }
brk_bencher = { workspace = true }
color-eyre = { workspace = true }
+3 -6
View File
@@ -8,7 +8,6 @@ use std::{
use brk_alloc::Mimalloc;
use brk_computer::Computer;
use brk_indexer::Indexer;
use brk_iterator::Blocks;
use brk_reader::Reader;
use brk_rpc::{Auth, Client};
use vecdb::Exit;
@@ -31,8 +30,6 @@ pub fn main() -> color_eyre::Result<()> {
let reader = Reader::new(bitcoin_dir.join("blocks"), &client);
let blocks = Blocks::new(&client, &reader);
let mut indexer = Indexer::forced_import(&outputs_dir)?;
let exit = Exit::new();
@@ -42,7 +39,7 @@ pub fn main() -> color_eyre::Result<()> {
let chain_height = client.get_last_height()?;
let indexed_height = indexer.vecs.starting_height();
if u32::from(chain_height).saturating_sub(u32::from(indexed_height)) > 1000 {
indexer.checked_index(&blocks, &client, &exit)?;
indexer.checked_index(&reader, &client, &exit)?;
drop(indexer);
Mimalloc::collect();
indexer = Indexer::forced_import(&outputs_dir)?;
@@ -52,11 +49,11 @@ pub fn main() -> color_eyre::Result<()> {
loop {
let i = Instant::now();
let starting_indexes = indexer.checked_index(&blocks, &client, &exit)?;
let starting_indexes = indexer.checked_index(&reader, &client, &exit)?;
Mimalloc::collect();
computer.compute(&indexer, starting_indexes, &reader, &exit)?;
computer.compute(&indexer, starting_indexes, &exit)?;
dbg!(i.elapsed());
sleep(Duration::from_secs(10));
}
@@ -5,7 +5,6 @@ use brk_bencher::Bencher;
use brk_computer::Computer;
use brk_error::Result;
use brk_indexer::Indexer;
use brk_iterator::Blocks;
use brk_reader::Reader;
use brk_rpc::{Auth, Client};
use tracing::{debug, info};
@@ -28,8 +27,6 @@ pub fn main() -> Result<()> {
let reader = Reader::new(bitcoin_dir.join("blocks"), &client);
let blocks = Blocks::new(&client, &reader);
let mut indexer = Indexer::forced_import(&outputs_dir)?;
let mut computer = Computer::forced_import(&outputs_benches_dir, &indexer)?;
@@ -47,13 +44,13 @@ pub fn main() -> Result<()> {
});
let i = Instant::now();
let starting_indexes = indexer.index(&blocks, &client, &exit)?;
let starting_indexes = indexer.index(&reader, &client, &exit)?;
info!("Done in {:?}", i.elapsed());
Mimalloc::collect();
let i = Instant::now();
computer.compute(&indexer, starting_indexes, &reader, &exit)?;
computer.compute(&indexer, starting_indexes, &exit)?;
info!("Done in {:?}", i.elapsed());
// We want to benchmark the drop too
+3 -6
View File
@@ -9,7 +9,6 @@ use brk_alloc::Mimalloc;
use brk_bencher::Bencher;
use brk_computer::Computer;
use brk_indexer::Indexer;
use brk_iterator::Blocks;
use brk_reader::Reader;
use brk_rpc::{Auth, Client};
use tracing::{debug, info};
@@ -45,15 +44,13 @@ pub fn main() -> color_eyre::Result<()> {
let reader = Reader::new(bitcoin_dir.join("blocks"), &client);
let blocks = Blocks::new(&client, &reader);
let mut indexer = Indexer::forced_import(&outputs_dir)?;
// Pre-run indexer if too far behind, then drop and reimport to reduce memory
let chain_height = client.get_last_height()?;
let indexed_height = indexer.vecs.starting_height();
if chain_height.saturating_sub(*indexed_height) > 1000 {
indexer.index(&blocks, &client, &exit)?;
indexer.index(&reader, &client, &exit)?;
drop(indexer);
Mimalloc::collect();
indexer = Indexer::forced_import(&outputs_dir)?;
@@ -63,13 +60,13 @@ pub fn main() -> color_eyre::Result<()> {
loop {
let i = Instant::now();
let starting_indexes = indexer.index(&blocks, &client, &exit)?;
let starting_indexes = indexer.index(&reader, &client, &exit)?;
info!("Done in {:?}", i.elapsed());
Mimalloc::collect();
let i = Instant::now();
computer.compute(&indexer, starting_indexes, &reader, &exit)?;
computer.compute(&indexer, starting_indexes, &exit)?;
info!("Done in {:?}", i.elapsed());
sleep(Duration::from_secs(60));
+10 -10
View File
@@ -17,12 +17,10 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
// Sequential: time → lookback (dependency chain)
self.time
.timestamp
.compute(indexer, indexes, starting_indexes, exit)?;
self.lookback
.compute(&self.time, starting_indexes, exit)?;
self.db.sync_bg_tasks()?;
// lookback depends on indexes.timestamp.monotonic
self.lookback.compute(indexes, starting_indexes, exit)?;
// Parallel: remaining sub-modules are independent of each other.
// size depends on lookback (already computed above).
@@ -40,8 +38,7 @@ impl Vecs {
let r1 = s.spawn(|| count.compute(indexer, starting_indexes, exit));
let r2 = s.spawn(|| interval.compute(indexer, starting_indexes, exit));
let r3 = s.spawn(|| weight.compute(indexer, starting_indexes, exit));
let r4 =
s.spawn(|| difficulty.compute(indexer, indexes, starting_indexes, exit));
let r4 = s.spawn(|| difficulty.compute(indexer, indexes, starting_indexes, exit));
let r5 = s.spawn(|| halving.compute(indexes, starting_indexes, exit));
size.compute(indexer, &*lookback, starting_indexes, exit)?;
r1.join().unwrap()?;
@@ -52,8 +49,11 @@ impl Vecs {
Ok(())
})?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact_deferred_default()
});
Ok(())
}
}
+22 -6
View File
@@ -6,8 +6,8 @@ use super::Vecs;
use crate::{
indexes,
internal::{
BlockCountTarget24h, BlockCountTarget1w, BlockCountTarget1m, BlockCountTarget1y,
CachedWindowStarts, PerBlockCumulativeRolling, ConstantVecs, Windows,
BlockCountTarget1m, BlockCountTarget1w, BlockCountTarget1y, BlockCountTarget24h,
CachedWindowStarts, ConstantVecs, PerBlockCumulativeRolling, Windows,
},
};
@@ -20,10 +20,26 @@ impl Vecs {
) -> Result<Self> {
Ok(Self {
target: Windows {
_24h: ConstantVecs::new::<BlockCountTarget24h>("block_count_target_24h", version, indexes),
_1w: ConstantVecs::new::<BlockCountTarget1w>("block_count_target_1w", version, indexes),
_1m: ConstantVecs::new::<BlockCountTarget1m>("block_count_target_1m", version, indexes),
_1y: ConstantVecs::new::<BlockCountTarget1y>("block_count_target_1y", version, indexes),
_24h: ConstantVecs::new::<BlockCountTarget24h>(
"block_count_target_24h",
version,
indexes,
),
_1w: ConstantVecs::new::<BlockCountTarget1w>(
"block_count_target_1w",
version,
indexes,
),
_1m: ConstantVecs::new::<BlockCountTarget1m>(
"block_count_target_1m",
version,
indexes,
),
_1y: ConstantVecs::new::<BlockCountTarget1y>(
"block_count_target_1y",
version,
indexes,
),
},
total: PerBlockCumulativeRolling::forced_import(
db,
+1 -1
View File
@@ -2,7 +2,7 @@ use brk_traversable::Traversable;
use brk_types::{StoredU32, StoredU64};
use vecdb::{Rw, StorageMode};
use crate::internal::{PerBlockCumulativeRolling, ConstantVecs, Windows};
use crate::internal::{ConstantVecs, PerBlockCumulativeRolling, Windows};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
@@ -27,12 +27,8 @@ impl Vecs {
indexes,
);
let blocks_to_retarget = PerBlock::forced_import(
db,
"blocks_to_retarget",
version + v2,
indexes,
)?;
let blocks_to_retarget =
PerBlock::forced_import(db, "blocks_to_retarget", version + v2, indexes)?;
let days_to_retarget = LazyPerBlock::from_computed::<BlocksToDaysF32>(
"days_to_retarget",
@@ -2,7 +2,7 @@ use brk_traversable::Traversable;
use brk_types::{BasisPointsSigned32, Epoch, StoredF32, StoredF64, StoredU32};
use vecdb::{Rw, StorageMode};
use crate::internal::{LazyPerBlock, PerBlock, Resolutions, PercentPerBlock};
use crate::internal::{LazyPerBlock, PerBlock, PercentPerBlock, Resolutions};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub value: Resolutions<StoredF64>,
@@ -16,9 +16,8 @@ impl Vecs {
) -> Result<Self> {
let v2 = Version::TWO;
let blocks_to_halving = PerBlock::forced_import(
db, "blocks_to_halving", version + v2, indexes,
)?;
let blocks_to_halving =
PerBlock::forced_import(db, "blocks_to_halving", version + v2, indexes)?;
let days_to_halving = LazyPerBlock::from_computed::<BlocksToDaysF32>(
"days_to_halving",
+1 -4
View File
@@ -10,8 +10,7 @@ use crate::{
};
use super::{
CountVecs, DifficultyVecs, HalvingVecs, IntervalVecs, LookbackVecs, SizeVecs, TimeVecs, Vecs,
WeightVecs,
CountVecs, DifficultyVecs, HalvingVecs, IntervalVecs, LookbackVecs, SizeVecs, Vecs, WeightVecs,
};
impl Vecs {
@@ -30,7 +29,6 @@ impl Vecs {
let interval = IntervalVecs::forced_import(&db, version, indexes, cached_starts)?;
let size = SizeVecs::forced_import(&db, version, indexes, cached_starts)?;
let weight = WeightVecs::forced_import(&db, version, indexes, cached_starts, &size)?;
let time = TimeVecs::forced_import(&db, version, indexes)?;
let difficulty = DifficultyVecs::forced_import(&db, version, indexer, indexes)?;
let halving = HalvingVecs::forced_import(&db, version, indexes)?;
@@ -41,7 +39,6 @@ impl Vecs {
interval,
size,
weight,
time,
difficulty,
halving,
};
@@ -13,27 +13,26 @@ impl Vecs {
exit: &Exit,
) -> Result<()> {
let mut prev_timestamp = None;
self.0
.compute(starting_indexes.height, exit, |vec| {
vec.compute_transform(
starting_indexes.height,
&indexer.vecs.blocks.timestamp,
|(h, timestamp, ..)| {
let interval = if let Some(prev_h) = h.decremented() {
let prev = prev_timestamp.unwrap_or_else(|| {
indexer.vecs.blocks.timestamp.collect_one(prev_h).unwrap()
});
timestamp.checked_sub(prev).unwrap_or(Timestamp::ZERO)
} else {
Timestamp::ZERO
};
prev_timestamp = Some(timestamp);
(h, interval)
},
exit,
)?;
Ok(())
})?;
self.0.compute(starting_indexes.height, exit, |vec| {
vec.compute_transform(
starting_indexes.height,
&indexer.vecs.blocks.timestamp,
|(h, timestamp, ..)| {
let interval = if let Some(prev_h) = h.decremented() {
let prev = prev_timestamp.unwrap_or_else(|| {
indexer.vecs.blocks.timestamp.collect_one(prev_h).unwrap()
});
timestamp.checked_sub(prev).unwrap_or(Timestamp::ZERO)
} else {
Timestamp::ZERO
};
prev_timestamp = Some(timestamp);
(h, interval)
},
exit,
)?;
Ok(())
})?;
Ok(())
}
@@ -3,7 +3,10 @@ use brk_types::Version;
use vecdb::Database;
use super::Vecs;
use crate::{indexes, internal::{CachedWindowStarts, PerBlockRollingAverage}};
use crate::{
indexes,
internal::{CachedWindowStarts, PerBlockRollingAverage},
};
impl Vecs {
pub(crate) fn forced_import(
+114 -84
View File
@@ -1,11 +1,15 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, Timestamp, Version};
use vecdb::{AnyVec, CachedVec, Cursor, Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw, StorageMode, VecIndex};
use vecdb::{
AnyVec, CachedVec, Cursor, Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw,
StorageMode, VecIndex,
};
use crate::internal::{CachedWindowStarts, Windows, WindowStarts};
use super::time;
use crate::{
indexes,
internal::{CachedWindowStarts, WindowStarts, Windows},
};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
@@ -111,10 +115,49 @@ impl Vecs {
Ok(Self {
cached_window_starts,
_1h, _24h, _3d, _1w, _8d, _9d, _12d, _13d, _2w, _21d, _26d,
_1m, _34d, _55d, _2m, _9w, _12w, _89d, _3m, _14w, _111d, _144d,
_6m, _26w, _200d, _9m, _350d, _12m, _1y, _14m, _2y, _26m, _3y,
_200w, _4y, _5y, _6y, _8y, _9y, _10y, _12y, _14y, _26y,
_1h,
_24h,
_3d,
_1w,
_8d,
_9d,
_12d,
_13d,
_2w,
_21d,
_26d,
_1m,
_34d,
_55d,
_2m,
_9w,
_12w,
_89d,
_3m,
_14w,
_111d,
_144d,
_6m,
_26w,
_200d,
_9m,
_350d,
_12m,
_1y,
_14m,
_2y,
_26m,
_3y,
_200w,
_4y,
_5y,
_6y,
_8y,
_9y,
_10y,
_12y,
_14y,
_26y,
})
}
@@ -127,7 +170,6 @@ impl Vecs {
}
}
pub fn start_vec(&self, days: usize) -> &EagerVec<PcoVec<Height, Height>> {
match days {
1 => &self._24h,
@@ -178,80 +220,60 @@ impl Vecs {
pub(crate) fn compute(
&mut self,
time: &time::Vecs,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.compute_rolling_start_hours(time, starting_indexes, exit, 1, |s| {
&mut s._1h
})?;
self.compute_rolling_start(time, starting_indexes, exit, 1, |s| &mut s._24h)?;
self.compute_rolling_start(time, starting_indexes, exit, 3, |s| &mut s._3d)?;
self.compute_rolling_start(time, starting_indexes, exit, 7, |s| &mut s._1w)?;
self.compute_rolling_start(time, starting_indexes, exit, 8, |s| &mut s._8d)?;
self.compute_rolling_start(time, starting_indexes, exit, 9, |s| &mut s._9d)?;
self.compute_rolling_start(time, starting_indexes, exit, 12, |s| &mut s._12d)?;
self.compute_rolling_start(time, starting_indexes, exit, 13, |s| &mut s._13d)?;
self.compute_rolling_start(time, starting_indexes, exit, 14, |s| &mut s._2w)?;
self.compute_rolling_start(time, starting_indexes, exit, 21, |s| &mut s._21d)?;
self.compute_rolling_start(time, starting_indexes, exit, 26, |s| &mut s._26d)?;
self.compute_rolling_start(time, starting_indexes, exit, 30, |s| &mut s._1m)?;
self.compute_rolling_start(time, starting_indexes, exit, 34, |s| &mut s._34d)?;
self.compute_rolling_start(time, starting_indexes, exit, 55, |s| &mut s._55d)?;
self.compute_rolling_start(time, starting_indexes, exit, 60, |s| &mut s._2m)?;
self.compute_rolling_start(time, starting_indexes, exit, 63, |s| &mut s._9w)?;
self.compute_rolling_start(time, starting_indexes, exit, 84, |s| &mut s._12w)?;
self.compute_rolling_start(time, starting_indexes, exit, 89, |s| &mut s._89d)?;
self.compute_rolling_start(time, starting_indexes, exit, 90, |s| &mut s._3m)?;
self.compute_rolling_start(time, starting_indexes, exit, 98, |s| &mut s._14w)?;
self.compute_rolling_start(time, starting_indexes, exit, 111, |s| {
&mut s._111d
})?;
self.compute_rolling_start(time, starting_indexes, exit, 144, |s| {
&mut s._144d
})?;
self.compute_rolling_start(time, starting_indexes, exit, 180, |s| &mut s._6m)?;
self.compute_rolling_start(time, starting_indexes, exit, 182, |s| &mut s._26w)?;
self.compute_rolling_start(time, starting_indexes, exit, 200, |s| {
&mut s._200d
})?;
self.compute_rolling_start(time, starting_indexes, exit, 270, |s| &mut s._9m)?;
self.compute_rolling_start(time, starting_indexes, exit, 350, |s| {
&mut s._350d
})?;
self.compute_rolling_start(time, starting_indexes, exit, 360, |s| &mut s._12m)?;
self.compute_rolling_start(time, starting_indexes, exit, 365, |s| &mut s._1y)?;
self.compute_rolling_start(time, starting_indexes, exit, 420, |s| &mut s._14m)?;
self.compute_rolling_start(time, starting_indexes, exit, 730, |s| &mut s._2y)?;
self.compute_rolling_start(time, starting_indexes, exit, 780, |s| &mut s._26m)?;
self.compute_rolling_start(time, starting_indexes, exit, 1095, |s| &mut s._3y)?;
self.compute_rolling_start(time, starting_indexes, exit, 1400, |s| {
&mut s._200w
})?;
self.compute_rolling_start(time, starting_indexes, exit, 1460, |s| &mut s._4y)?;
self.compute_rolling_start(time, starting_indexes, exit, 1825, |s| &mut s._5y)?;
self.compute_rolling_start(time, starting_indexes, exit, 2190, |s| &mut s._6y)?;
self.compute_rolling_start(time, starting_indexes, exit, 2920, |s| &mut s._8y)?;
self.compute_rolling_start(time, starting_indexes, exit, 3285, |s| &mut s._9y)?;
self.compute_rolling_start(time, starting_indexes, exit, 3650, |s| {
&mut s._10y
})?;
self.compute_rolling_start(time, starting_indexes, exit, 4380, |s| {
&mut s._12y
})?;
self.compute_rolling_start(time, starting_indexes, exit, 5110, |s| {
&mut s._14y
})?;
self.compute_rolling_start(time, starting_indexes, exit, 9490, |s| {
&mut s._26y
})?;
self.compute_rolling_start_hours(indexes, starting_indexes, exit, 1, |s| &mut s._1h)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 1, |s| &mut s._24h)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 3, |s| &mut s._3d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 7, |s| &mut s._1w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 8, |s| &mut s._8d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 9, |s| &mut s._9d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 12, |s| &mut s._12d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 13, |s| &mut s._13d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 14, |s| &mut s._2w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 21, |s| &mut s._21d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 26, |s| &mut s._26d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 30, |s| &mut s._1m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 34, |s| &mut s._34d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 55, |s| &mut s._55d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 60, |s| &mut s._2m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 63, |s| &mut s._9w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 84, |s| &mut s._12w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 89, |s| &mut s._89d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 90, |s| &mut s._3m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 98, |s| &mut s._14w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 111, |s| &mut s._111d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 144, |s| &mut s._144d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 180, |s| &mut s._6m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 182, |s| &mut s._26w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 200, |s| &mut s._200d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 270, |s| &mut s._9m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 350, |s| &mut s._350d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 360, |s| &mut s._12m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 365, |s| &mut s._1y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 420, |s| &mut s._14m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 730, |s| &mut s._2y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 780, |s| &mut s._26m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 1095, |s| &mut s._3y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 1400, |s| &mut s._200w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 1460, |s| &mut s._4y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 1825, |s| &mut s._5y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 2190, |s| &mut s._6y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 2920, |s| &mut s._8y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 3285, |s| &mut s._9y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 3650, |s| &mut s._10y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 4380, |s| &mut s._12y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 5110, |s| &mut s._14y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 9490, |s| &mut s._26y)?;
Ok(())
}
fn compute_rolling_start<F>(
&mut self,
time: &time::Vecs,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
days: usize,
@@ -260,14 +282,18 @@ impl Vecs {
where
F: FnOnce(&mut Self) -> &mut EagerVec<PcoVec<Height, Height>>,
{
self.compute_rolling_start_inner(time, starting_indexes, exit, get_field, |t, prev_ts| {
t.difference_in_days_between(prev_ts) >= days
})
self.compute_rolling_start_inner(
indexes,
starting_indexes,
exit,
get_field,
|t, prev_ts| t.difference_in_days_between(prev_ts) >= days,
)
}
fn compute_rolling_start_hours<F>(
&mut self,
time: &time::Vecs,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
hours: usize,
@@ -276,14 +302,18 @@ impl Vecs {
where
F: FnOnce(&mut Self) -> &mut EagerVec<PcoVec<Height, Height>>,
{
self.compute_rolling_start_inner(time, starting_indexes, exit, get_field, |t, prev_ts| {
t.difference_in_hours_between(prev_ts) >= hours
})
self.compute_rolling_start_inner(
indexes,
starting_indexes,
exit,
get_field,
|t, prev_ts| t.difference_in_hours_between(prev_ts) >= hours,
)
}
fn compute_rolling_start_inner<F, D>(
&mut self,
time: &time::Vecs,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
get_field: F,
@@ -300,12 +330,12 @@ impl Vecs {
} else {
Height::ZERO
};
let mut cursor = Cursor::new(&time.timestamp_monotonic);
let mut cursor = Cursor::new(&indexes.timestamp.monotonic);
cursor.advance(prev.to_usize());
let mut prev_ts = cursor.next().unwrap();
Ok(field.compute_transform(
starting_indexes.height,
&time.timestamp_monotonic,
&indexes.timestamp.monotonic,
|(h, t, ..)| {
while expired(t, prev_ts) {
prev.increment();
+1 -4
View File
@@ -4,7 +4,6 @@ pub mod halving;
pub mod interval;
pub mod lookback;
pub mod size;
pub mod time;
pub mod weight;
mod compute;
@@ -19,7 +18,6 @@ pub use halving::Vecs as HalvingVecs;
pub use interval::Vecs as IntervalVecs;
pub use lookback::Vecs as LookbackVecs;
pub use size::Vecs as SizeVecs;
pub use time::Vecs as TimeVecs;
pub use weight::Vecs as WeightVecs;
pub const DB_NAME: &str = "blocks";
@@ -37,7 +35,7 @@ pub(crate) const ONE_TERA_HASH: f64 = 1_000_000_000_000.0;
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
#[traversable(skip)]
pub(crate) db: Database,
pub db: Database,
pub count: CountVecs<M>,
pub lookback: LookbackVecs<M>,
@@ -46,7 +44,6 @@ pub struct Vecs<M: StorageMode = Rw> {
pub size: SizeVecs<M>,
#[traversable(flatten)]
pub weight: WeightVecs<M>,
pub time: TimeVecs<M>,
pub difficulty: DifficultyVecs<M>,
pub halving: HalvingVecs<M>,
}
@@ -1,34 +0,0 @@
use brk_error::Result;
use brk_indexer::Indexer;
use vecdb::{Exit, ReadableVec};
use super::Vecs;
impl Vecs {
pub(crate) fn compute(
&mut self,
indexer: &Indexer,
starting_height: brk_types::Height,
exit: &Exit,
) -> Result<()> {
let mut prev_timestamp_monotonic = None;
self.timestamp_monotonic.compute_transform(
starting_height,
&indexer.vecs.blocks.timestamp,
|(h, timestamp, this)| {
if prev_timestamp_monotonic.is_none()
&& let Some(prev_h) = h.decremented()
{
prev_timestamp_monotonic.replace(this.collect_one(prev_h).unwrap());
}
let timestamp_monotonic =
prev_timestamp_monotonic.map_or(timestamp, |prev_d| prev_d.max(timestamp));
prev_timestamp_monotonic.replace(timestamp_monotonic);
(h, timestamp_monotonic)
},
exit,
)?;
Ok(())
}
}
@@ -1,66 +0,0 @@
use brk_error::Result;
use brk_types::{Date, Height, Version};
use vecdb::{Database, EagerVec, ImportableVec, LazyVecFrom1, ReadableCloneableVec};
use super::{TimestampIndexes, Vecs};
use crate::indexes;
impl Vecs {
pub(crate) fn forced_import(
db: &Database,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let timestamp_monotonic = EagerVec::forced_import(db, "timestamp_monotonic", version)?;
Ok(Self {
date: LazyVecFrom1::init(
"date",
version,
timestamp_monotonic.read_only_boxed_clone(),
|_height: Height, timestamp| Date::from(timestamp),
),
timestamp_monotonic,
timestamp: TimestampIndexes::forced_import(db, version, indexes)?,
})
}
}
impl TimestampIndexes {
fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
macro_rules! period {
($field:ident) => {
LazyVecFrom1::init(
"timestamp",
version,
indexes.$field.first_height.read_only_boxed_clone(),
|idx, _: Height| idx.to_timestamp(),
)
};
}
macro_rules! epoch {
($field:ident) => {
ImportableVec::forced_import(db, "timestamp", version)?
};
}
Ok(Self(crate::internal::PerResolution {
minute10: period!(minute10),
minute30: period!(minute30),
hour1: period!(hour1),
hour4: period!(hour4),
hour12: period!(hour12),
day1: period!(day1),
day3: period!(day3),
week1: period!(week1),
month1: period!(month1),
month3: period!(month3),
month6: period!(month6),
year1: period!(year1),
year10: period!(year10),
halving: epoch!(halving),
epoch: epoch!(difficulty),
}))
}
}
@@ -1,5 +0,0 @@
mod compute;
mod import;
mod vecs;
pub use vecs::{TimestampIndexes, Vecs};
@@ -1,80 +0,0 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
Date, Day1, Day3, Epoch, Halving, Height, Hour1, Hour4, Hour12, Indexes,
Minute10, Minute30, Month1, Month3, Month6, Timestamp, Week1, Year1, Year10,
};
use derive_more::{Deref, DerefMut};
use vecdb::{EagerVec, Exit, LazyVecFrom1, PcoVec, ReadableVec, Rw, StorageMode};
use crate::{indexes, internal::PerResolution};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub date: LazyVecFrom1<Height, Date, Height, Timestamp>,
pub timestamp_monotonic: M::Stored<EagerVec<PcoVec<Height, Timestamp>>>,
pub timestamp: TimestampIndexes<M>,
}
/// Per-period timestamp indexes.
///
/// Time-based periods (minute10year10) are lazy: `idx.to_timestamp()` is a pure
/// function of the index, so no storage or decompression is needed.
/// Epoch-based periods (halving, difficulty) are eager: their timestamps
/// come from block data via `compute_indirect_sequential`.
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct TimestampIndexes<M: StorageMode = Rw>(
#[allow(clippy::type_complexity)]
pub PerResolution<
LazyVecFrom1<Minute10, Timestamp, Minute10, Height>,
LazyVecFrom1<Minute30, Timestamp, Minute30, Height>,
LazyVecFrom1<Hour1, Timestamp, Hour1, Height>,
LazyVecFrom1<Hour4, Timestamp, Hour4, Height>,
LazyVecFrom1<Hour12, Timestamp, Hour12, Height>,
LazyVecFrom1<Day1, Timestamp, Day1, Height>,
LazyVecFrom1<Day3, Timestamp, Day3, Height>,
LazyVecFrom1<Week1, Timestamp, Week1, Height>,
LazyVecFrom1<Month1, Timestamp, Month1, Height>,
LazyVecFrom1<Month3, Timestamp, Month3, Height>,
LazyVecFrom1<Month6, Timestamp, Month6, Height>,
LazyVecFrom1<Year1, Timestamp, Year1, Height>,
LazyVecFrom1<Year10, Timestamp, Year10, Height>,
M::Stored<EagerVec<PcoVec<Halving, Timestamp>>>,
M::Stored<EagerVec<PcoVec<Epoch, Timestamp>>>,
>,
);
impl TimestampIndexes {
/// Compute epoch timestamps via indirect lookup from block timestamps.
/// Time-based periods are lazy (idx.to_timestamp()) and need no compute.
pub(crate) fn compute(
&mut self,
indexer: &brk_indexer::Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
let prev_height = starting_indexes.height.decremented().unwrap_or_default();
self.halving.compute_indirect_sequential(
indexes
.height
.halving
.collect_one(prev_height)
.unwrap_or_default(),
&indexes.halving.first_height,
&indexer.vecs.blocks.timestamp,
exit,
)?;
self.epoch.compute_indirect_sequential(
indexes
.height
.epoch
.collect_one(prev_height)
.unwrap_or_default(),
&indexes.epoch.first_height,
&indexer.vecs.blocks.timestamp,
exit,
)?;
Ok(())
}
}
@@ -28,10 +28,18 @@ impl Vecs {
Ok(Self {
coinblocks_created: PerBlockCumulativeRolling::forced_import(
db, "coinblocks_created", version, indexes, cached_starts,
db,
"coinblocks_created",
version,
indexes,
cached_starts,
)?,
coinblocks_stored: PerBlockCumulativeRolling::forced_import(
db, "coinblocks_stored", version, indexes, cached_starts,
db,
"coinblocks_stored",
version,
indexes,
cached_starts,
)?,
liveliness,
vaultedness,
+7 -2
View File
@@ -17,6 +17,8 @@ impl Vecs {
distribution: &distribution::Vecs,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
// Activity computes first (liveliness, vaultedness, etc.)
self.activity
.compute(starting_indexes, distribution, exit)?;
@@ -80,8 +82,11 @@ impl Vecs {
r3?;
r4?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact_deferred_default()
});
Ok(())
}
}
@@ -22,11 +22,8 @@ impl Vecs {
let circulating_supply = &all_metrics.supply.total.btc.height;
let realized_price = &all_metrics.realized.price.cents.height;
self.vaulted.compute_all(
prices,
starting_indexes,
exit,
|v| {
self.vaulted
.compute_all(prices, starting_indexes, exit, |v| {
Ok(v.compute_transform2(
starting_indexes.height,
realized_price,
@@ -36,14 +33,10 @@ impl Vecs {
},
exit,
)?)
},
)?;
})?;
self.active.compute_all(
prices,
starting_indexes,
exit,
|v| {
self.active
.compute_all(prices, starting_indexes, exit, |v| {
Ok(v.compute_transform2(
starting_indexes.height,
realized_price,
@@ -53,14 +46,10 @@ impl Vecs {
},
exit,
)?)
},
)?;
})?;
self.true_market_mean.compute_all(
prices,
starting_indexes,
exit,
|v| {
self.true_market_mean
.compute_all(prices, starting_indexes, exit, |v| {
Ok(v.compute_transform2(
starting_indexes.height,
&cap.investor.cents.height,
@@ -70,14 +59,10 @@ impl Vecs {
},
exit,
)?)
},
)?;
})?;
self.cointime.compute_all(
prices,
starting_indexes,
exit,
|v| {
self.cointime
.compute_all(prices, starting_indexes, exit, |v| {
Ok(v.compute_transform2(
starting_indexes.height,
&cap.cointime.cents.height,
@@ -87,8 +72,7 @@ impl Vecs {
},
exit,
)?)
},
)?;
})?;
Ok(())
}
@@ -3,10 +3,7 @@ use brk_types::Version;
use vecdb::Database;
use super::Vecs;
use crate::{
indexes,
internal::PriceWithRatioExtendedPerBlock,
};
use crate::{indexes, internal::PriceWithRatioExtendedPerBlock};
impl Vecs {
pub(crate) fn forced_import(
@@ -23,7 +23,7 @@ impl Vecs {
self.hodl_bank.compute_cumulative_transformed_binary(
starting_indexes.height,
&prices.spot.usd.height,
&prices.cached_spot_usd,
&self.vocdd_median_1y,
|price, median| StoredF64::from(f64::from(price) - f64::from(median)),
exit,
@@ -31,7 +31,7 @@ impl Vecs {
self.value.height.compute_divide(
starting_indexes.height,
&prices.spot.usd.height,
&prices.cached_spot_usd,
&self.hodl_bank,
exit,
)?;
@@ -38,7 +38,8 @@ impl Vecs {
exit,
)?;
self.vaulted.compute(prices, starting_indexes.height, exit)?;
self.vaulted
.compute(prices, starting_indexes.height, exit)?;
self.active.compute(prices, starting_indexes.height, exit)?;
Ok(())
@@ -12,12 +12,7 @@ impl Vecs {
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self {
vaulted: AmountPerBlock::forced_import(
db,
"vaulted_supply",
version,
indexes,
)?,
vaulted: AmountPerBlock::forced_import(db, "vaulted_supply", version, indexes)?,
active: AmountPerBlock::forced_import(db, "active_supply", version, indexes)?,
})
}
@@ -24,59 +24,56 @@ impl Vecs {
.compute(starting_indexes.height, exit, |vec| {
vec.compute_multiply(
starting_indexes.height,
&prices.spot.usd.height,
&prices.cached_spot_usd,
&coinblocks_destroyed.block,
exit,
)?;
Ok(())
})?;
self.created
.compute(starting_indexes.height, exit, |vec| {
vec.compute_multiply(
starting_indexes.height,
&prices.spot.usd.height,
&activity.coinblocks_created.block,
exit,
)?;
Ok(())
})?;
self.created.compute(starting_indexes.height, exit, |vec| {
vec.compute_multiply(
starting_indexes.height,
&prices.cached_spot_usd,
&activity.coinblocks_created.block,
exit,
)?;
Ok(())
})?;
self.stored
.compute(starting_indexes.height, exit, |vec| {
vec.compute_multiply(
starting_indexes.height,
&prices.spot.usd.height,
&activity.coinblocks_stored.block,
exit,
)?;
Ok(())
})?;
self.stored.compute(starting_indexes.height, exit, |vec| {
vec.compute_multiply(
starting_indexes.height,
&prices.cached_spot_usd,
&activity.coinblocks_stored.block,
exit,
)?;
Ok(())
})?;
// VOCDD: Value of Coin Days Destroyed = price × (CDD / circulating_supply)
// Supply-adjusted to account for growing supply over time
// This is a key input for Reserve Risk / HODL Bank calculation
self.vocdd
.compute(starting_indexes.height, exit, |vec| {
vec.compute_transform3(
starting_indexes.height,
&prices.spot.usd.height,
&coindays_destroyed.block,
circulating_supply,
|(i, price, cdd, supply, _): (_, Dollars, StoredF64, Bitcoin, _)| {
let supply_f64 = f64::from(supply);
if supply_f64 == 0.0 {
(i, StoredF64::from(0.0))
} else {
// VOCDD = price × (CDD / supply)
let vocdd = f64::from(price) * f64::from(cdd) / supply_f64;
(i, StoredF64::from(vocdd))
}
},
exit,
)?;
Ok(())
})?;
self.vocdd.compute(starting_indexes.height, exit, |vec| {
vec.compute_transform3(
starting_indexes.height,
&prices.cached_spot_usd,
&coindays_destroyed.block,
circulating_supply,
|(i, price, cdd, supply, _): (_, Dollars, StoredF64, Bitcoin, _)| {
let supply_f64 = f64::from(supply);
if supply_f64 == 0.0 {
(i, StoredF64::from(0.0))
} else {
// VOCDD = price × (CDD / supply)
let vocdd = f64::from(price) * f64::from(cdd) / supply_f64;
(i, StoredF64::from(vocdd))
}
},
exit,
)?;
Ok(())
})?;
Ok(())
}
@@ -148,11 +148,7 @@ impl ActivityCountVecs {
self.both.block.push(counts.both.into());
}
pub(crate) fn compute_rest(
&mut self,
max_from: Height,
exit: &Exit,
) -> Result<()> {
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> {
self.reactivated.compute_rest(max_from, exit)?;
self.sending.compute_rest(max_from, exit)?;
self.receiving.compute_rest(max_from, exit)?;
@@ -180,8 +176,8 @@ impl AddrTypeToActivityCountVecs {
indexes: &indexes::Vecs,
cached_starts: &CachedWindowStarts,
) -> Result<Self> {
Ok(Self::from(
ByAddrType::<ActivityCountVecs>::new_with_name(|type_name| {
Ok(Self::from(ByAddrType::<ActivityCountVecs>::new_with_name(
|type_name| {
ActivityCountVecs::forced_import(
db,
&format!("{type_name}_{name}"),
@@ -189,8 +185,8 @@ impl AddrTypeToActivityCountVecs {
indexes,
cached_starts,
)
})?,
))
},
)?))
}
pub(crate) fn min_stateful_len(&self) -> usize {
@@ -221,11 +217,7 @@ impl AddrTypeToActivityCountVecs {
Ok(())
}
pub(crate) fn compute_rest(
&mut self,
max_from: Height,
exit: &Exit,
) -> Result<()> {
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> {
for type_vecs in self.0.values_mut() {
type_vecs.compute_rest(max_from, exit)?;
}
@@ -259,7 +251,11 @@ impl AddrActivityVecs {
Ok(Self {
all: ActivityCountVecs::forced_import(db, name, version, indexes, cached_starts)?,
by_addr_type: AddrTypeToActivityCountVecs::forced_import(
db, name, version, indexes, cached_starts,
db,
name,
version,
indexes,
cached_starts,
)?,
})
}
@@ -284,11 +280,7 @@ impl AddrActivityVecs {
Ok(())
}
pub(crate) fn compute_rest(
&mut self,
max_from: Height,
exit: &Exit,
) -> Result<()> {
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> {
self.all.compute_rest(max_from, exit)?;
self.by_addr_type.compute_rest(max_from, exit)?;
Ok(())
@@ -12,9 +12,7 @@ use vecdb::{
use crate::{indexes, internal::PerBlock};
#[derive(Deref, DerefMut, Traversable)]
pub struct AddrCountVecs<M: StorageMode = Rw>(
#[traversable(flatten)] pub PerBlock<StoredU64, M>,
);
pub struct AddrCountVecs<M: StorageMode = Rw>(#[traversable(flatten)] pub PerBlock<StoredU64, M>);
impl AddrCountVecs {
pub(crate) fn forced_import(
@@ -23,9 +21,7 @@ impl AddrCountVecs {
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self(PerBlock::forced_import(
db, name, version, indexes,
)?))
Ok(Self(PerBlock::forced_import(db, name, version, indexes)?))
}
}
@@ -57,42 +53,17 @@ impl From<(&AddrTypeToAddrCountVecs, Height)> for AddrTypeToAddrCount {
.collect_one(prev_height)
.unwrap()
.into(),
p2pkh: groups
.p2pkh
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2sh: groups
.p2sh
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2pkh: groups.p2pkh.height.collect_one(prev_height).unwrap().into(),
p2sh: groups.p2sh.height.collect_one(prev_height).unwrap().into(),
p2wpkh: groups
.p2wpkh
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2wsh: groups
.p2wsh
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2tr: groups
.p2tr
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2a: groups
.p2a
.height
.collect_one(prev_height)
.unwrap()
.into(),
p2wsh: groups.p2wsh.height.collect_one(prev_height).unwrap().into(),
p2tr: groups.p2tr.height.collect_one(prev_height).unwrap().into(),
p2a: groups.p2a.height.collect_one(prev_height).unwrap().into(),
})
} else {
Default::default()
@@ -177,7 +148,10 @@ impl AddrCountsVecs {
}
pub(crate) fn min_stateful_len(&self) -> usize {
self.all.height.len().min(self.by_addr_type.min_stateful_len())
self.all
.height
.len()
.min(self.by_addr_type.min_stateful_len())
}
pub(crate) fn par_iter_height_mut(
@@ -199,11 +173,7 @@ impl AddrCountsVecs {
self.by_addr_type.push_height(addr_counts);
}
pub(crate) fn compute_rest(
&mut self,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
pub(crate) fn compute_rest(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
let sources = self.by_addr_type.by_height();
self.all
.height
@@ -1,8 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex, Height,
};
use brk_types::{EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex, Height};
use rayon::prelude::*;
use vecdb::{AnyStoredVec, BytesVec, Rw, Stamp, StorageMode, WritableVec};
@@ -45,9 +45,6 @@ impl DeltaVecs {
)
});
Self {
all,
by_addr_type,
}
Self { all, by_addr_type }
}
}
@@ -5,8 +5,8 @@ use brk_error::{Error, Result};
use brk_traversable::Traversable;
use brk_types::{
AnyAddrIndex, Height, OutputType, P2AAddrIndex, P2PK33AddrIndex, P2PK65AddrIndex,
P2PKHAddrIndex, P2SHAddrIndex, P2TRAddrIndex, P2WPKHAddrIndex, P2WSHAddrIndex,
TypeIndex, Version,
P2PKHAddrIndex, P2SHAddrIndex, P2TRAddrIndex, P2WPKHAddrIndex, P2WSHAddrIndex, TypeIndex,
Version,
};
use rayon::prelude::*;
use rustc_hash::FxHashMap;
@@ -44,10 +44,7 @@ impl NewAddrCountVecs {
)
})?;
Ok(Self {
all,
by_addr_type,
})
Ok(Self { all, by_addr_type })
}
pub(crate) fn compute(
@@ -24,20 +24,11 @@ impl TotalAddrCountVecs {
) -> Result<Self> {
let all = PerBlock::forced_import(db, "total_addr_count", version, indexes)?;
let by_addr_type: ByAddrType<PerBlock<StoredU64>> =
ByAddrType::new_with_name(|name| {
PerBlock::forced_import(
db,
&format!("{name}_total_addr_count"),
version,
indexes,
)
})?;
let by_addr_type: ByAddrType<PerBlock<StoredU64>> = ByAddrType::new_with_name(|name| {
PerBlock::forced_import(db, &format!("{name}_total_addr_count"), version, indexes)
})?;
Ok(Self {
all,
by_addr_type,
})
Ok(Self { all, by_addr_type })
}
/// Eagerly compute total = addr_count + empty_addr_count.
+1 -3
View File
@@ -103,9 +103,7 @@ pub(crate) fn load_uncached_addr_data(
// Check if this is a new address (type_index >= first for this height)
let first = *first_addr_indexes.get(addr_type).unwrap();
if first <= type_index {
return Ok(Some(WithAddrDataSource::New(
FundedAddrData::default(),
)));
return Ok(Some(WithAddrDataSource::New(FundedAddrData::default())));
}
// Skip if already in cache
+1 -4
View File
@@ -26,10 +26,7 @@ impl<'a> AddrLookup<'a> {
&mut self,
output_type: OutputType,
type_index: TypeIndex,
) -> (
&mut WithAddrDataSource<FundedAddrData>,
TrackingStatus,
) {
) -> (&mut WithAddrDataSource<FundedAddrData>, TrackingStatus) {
use std::collections::hash_map::Entry;
let map = self.funded.get_mut(output_type).unwrap();
@@ -1,7 +1,7 @@
use brk_error::Result;
use brk_types::{
AnyAddrIndex, EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex,
OutputType, TypeIndex,
AnyAddrIndex, EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex, OutputType,
TypeIndex,
};
use vecdb::AnyVec;
@@ -26,7 +26,11 @@ pub(crate) fn process_received(
empty_addr_count: &mut ByAddrType<u64>,
activity_counts: &mut AddrTypeToActivityCounts,
) {
let max_type_len = received_data.iter().map(|(_, v)| v.len()).max().unwrap_or(0);
let max_type_len = received_data
.iter()
.map(|(_, v)| v.len())
.max()
.unwrap_or(0);
let mut aggregated: FxHashMap<TypeIndex, AggregatedReceive> =
FxHashMap::with_capacity_and_hasher(max_type_len, Default::default());
@@ -41,10 +41,7 @@ pub(crate) fn update_tx_counts(
.get_mut(&type_index)
{
addr_data.tx_count += tx_count;
} else if let Some(addr_data) = empty_cache
.get_mut(addr_type)
.unwrap()
.get_mut(&type_index)
} else if let Some(addr_data) = empty_cache.get_mut(addr_type).unwrap().get_mut(&type_index)
{
addr_data.tx_count += tx_count;
}
@@ -90,9 +90,7 @@ pub(crate) fn process_inputs(
};
let items: Vec<_> = if input_count < 128 {
(0..input_count)
.map(map_fn)
.collect::<Result<Vec<_>>>()?
(0..input_count).map(map_fn).collect::<Result<Vec<_>>>()?
} else {
(0..input_count)
.into_par_iter()
@@ -109,10 +107,9 @@ pub(crate) fn process_inputs(
Default::default(),
);
let mut sent_data = HeightToAddrTypeToVec::with_capacity(estimated_unique_heights);
let mut addr_data =
AddrTypeToTypeIndexMap::<WithAddrDataSource<FundedAddrData>>::with_capacity(
estimated_per_type,
);
let mut addr_data = AddrTypeToTypeIndexMap::<WithAddrDataSource<FundedAddrData>>::with_capacity(
estimated_per_type,
);
let mut tx_index_vecs =
AddrTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
@@ -5,9 +5,7 @@ use rayon::prelude::*;
use smallvec::SmallVec;
use crate::distribution::{
addr::{
AddrTypeToTypeIndexMap, AddrTypeToVec, AddrsDataVecs, AnyAddrIndexesVecs,
},
addr::{AddrTypeToTypeIndexMap, AddrTypeToVec, AddrsDataVecs, AnyAddrIndexesVecs},
compute::{TxOutData, VecsReaders},
state::Transacted,
};
@@ -79,9 +77,7 @@ pub(crate) fn process_outputs(
};
let items: Vec<_> = if output_count < 128 {
(0..output_count)
.map(map_fn)
.collect::<Result<Vec<_>>>()?
(0..output_count).map(map_fn).collect::<Result<Vec<_>>>()?
} else {
(0..output_count)
.into_par_iter()
@@ -93,10 +89,9 @@ pub(crate) fn process_outputs(
let estimated_per_type = (output_count / 8).max(8);
let mut transacted = Transacted::default();
let mut received_data = AddrTypeToVec::with_capacity(estimated_per_type);
let mut addr_data =
AddrTypeToTypeIndexMap::<WithAddrDataSource<FundedAddrData>>::with_capacity(
estimated_per_type,
);
let mut addr_data = AddrTypeToTypeIndexMap::<WithAddrDataSource<FundedAddrData>>::with_capacity(
estimated_per_type,
);
let mut tx_index_vecs =
AddrTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
@@ -1,8 +1,6 @@
use std::path::Path;
use brk_cohort::{
AddrGroups, AmountRange, OverAmount, UnderAmount, Filter, Filtered,
};
use brk_cohort::{AddrGroups, AmountRange, Filter, Filtered, OverAmount, UnderAmount};
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, StoredU64, Version};
@@ -161,9 +161,7 @@ impl DynCohortVecs for AddrCohortVecs {
}
if let Some(state) = self.state.as_ref() {
self.addr_count
.height
.push(state.addr_count.into());
self.addr_count.height.push(state.addr_count.into());
self.metrics.supply.push_state(&state.inner);
self.metrics.outputs.push_state(&state.inner);
self.metrics.activity.push_state(&state.inner);
@@ -1,4 +1,4 @@
use brk_types::{CostBasisSnapshot, Cents, Height, Timestamp};
use brk_types::{Cents, CostBasisSnapshot, Height, Timestamp};
use vecdb::Rw;
use crate::distribution::state::Transacted;
@@ -34,10 +34,16 @@ impl UTXOCohorts<Rw> {
.unwrap()
.receive_utxo_snapshot(&supply_state, &snapshot);
if let Some(v) = self.epoch.mut_vec_from_height(height) {
v.state.as_mut().unwrap().receive_utxo_snapshot(&supply_state, &snapshot);
v.state
.as_mut()
.unwrap()
.receive_utxo_snapshot(&supply_state, &snapshot);
}
if let Some(v) = self.class.mut_vec_from_timestamp(timestamp) {
v.state.as_mut().unwrap().receive_utxo_snapshot(&supply_state, &snapshot);
v.state
.as_mut()
.unwrap()
.receive_utxo_snapshot(&supply_state, &snapshot);
}
// Update output type cohorts (skip types with no outputs this block)
@@ -64,10 +64,16 @@ impl UTXOCohorts<Rw> {
.unwrap()
.send_utxo_precomputed(&sent.spendable_supply, &pre);
if let Some(v) = self.epoch.mut_vec_from_height(receive_height) {
v.state.as_mut().unwrap().send_utxo_precomputed(&sent.spendable_supply, &pre);
v.state
.as_mut()
.unwrap()
.send_utxo_precomputed(&sent.spendable_supply, &pre);
}
if let Some(v) = self.class.mut_vec_from_timestamp(block_state.timestamp) {
v.state.as_mut().unwrap().send_utxo_precomputed(&sent.spendable_supply, &pre);
v.state
.as_mut()
.unwrap()
.send_utxo_precomputed(&sent.spendable_supply, &pre);
}
} else if sent.spendable_supply.utxo_count > 0 {
// Zero-value UTXOs: just subtract supply
@@ -67,9 +67,7 @@ impl UTXOCohorts<Rw> {
idx
} else {
let mut idx = cached[boundary_idx];
while idx < chain_state.len()
&& *chain_state[idx].timestamp <= lower_timestamp
{
while idx < chain_state.len() && *chain_state[idx].timestamp <= lower_timestamp {
idx += 1;
}
cached[boundary_idx] = idx;
@@ -84,8 +82,7 @@ impl UTXOCohorts<Rw> {
// Move supply from younger cohort to older cohort
for block_state in &chain_state[start_idx..end_idx] {
let snapshot =
CostBasisSnapshot::from_utxo(block_state.price, &block_state.supply);
let snapshot = CostBasisSnapshot::from_utxo(block_state.price, &block_state.supply);
if let Some(state) = age_cohorts[boundary_idx].as_mut() {
state.decrement_snapshot(&snapshot);
}
@@ -3,7 +3,10 @@ use brk_error::Result;
use brk_types::{Cents, Height, Indexes, Version};
use vecdb::{Exit, ReadableVec};
use crate::{distribution::{cohorts::traits::DynCohortVecs, metrics::CoreCohortMetrics}, prices};
use crate::{
distribution::{cohorts::traits::DynCohortVecs, metrics::CoreCohortMetrics},
prices,
};
use super::UTXOCohortVecs;
@@ -45,12 +48,8 @@ impl DynCohortVecs for UTXOCohortVecs<CoreCohortMetrics> {
if let Some(state) = self.state.as_mut() {
state.apply_pending();
let unrealized_state = state.compute_unrealized_state(height_price);
self.metrics
.unrealized
.push_state(&unrealized_state);
self.metrics
.supply
.push_profitability(&unrealized_state);
self.metrics.unrealized.push_state(&unrealized_state);
self.metrics.supply.push_profitability(&unrealized_state);
}
}
@@ -3,7 +3,9 @@ use brk_error::Result;
use brk_types::{Cents, Height, Indexes, Version};
use vecdb::{Exit, ReadableVec};
use crate::{distribution::cohorts::traits::DynCohortVecs, distribution::metrics::TypeCohortMetrics, prices};
use crate::{
distribution::cohorts::traits::DynCohortVecs, distribution::metrics::TypeCohortMetrics, prices,
};
use super::UTXOCohortVecs;
@@ -45,12 +47,8 @@ impl DynCohortVecs for UTXOCohortVecs<TypeCohortMetrics> {
if let Some(state) = self.state.as_mut() {
state.apply_pending();
let unrealized_state = state.compute_unrealized_state(height_price);
self.metrics
.unrealized
.push_state(&unrealized_state);
self.metrics
.supply
.push_profitability(&unrealized_state);
self.metrics.unrealized.push_state(&unrealized_state);
self.metrics.supply.push_profitability(&unrealized_state);
}
}
@@ -7,7 +7,7 @@ use brk_types::{
use rayon::prelude::*;
use rustc_hash::FxHashSet;
use tracing::{debug, info};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec, unlikely};
use crate::{
distribution::{
@@ -144,42 +144,50 @@ pub(crate) fn process_blocks(
let first_p2a_vec = indexer
.vecs
.addrs
.p2a.first_index
.p2a
.first_index
.collect_range_at(start_usize, end_usize);
let first_p2pk33_vec = indexer
.vecs
.addrs
.p2pk33.first_index
.p2pk33
.first_index
.collect_range_at(start_usize, end_usize);
let first_p2pk65_vec = indexer
.vecs
.addrs
.p2pk65.first_index
.p2pk65
.first_index
.collect_range_at(start_usize, end_usize);
let first_p2pkh_vec = indexer
.vecs
.addrs
.p2pkh.first_index
.p2pkh
.first_index
.collect_range_at(start_usize, end_usize);
let first_p2sh_vec = indexer
.vecs
.addrs
.p2sh.first_index
.p2sh
.first_index
.collect_range_at(start_usize, end_usize);
let first_p2tr_vec = indexer
.vecs
.addrs
.p2tr.first_index
.p2tr
.first_index
.collect_range_at(start_usize, end_usize);
let first_p2wpkh_vec = indexer
.vecs
.addrs
.p2wpkh.first_index
.p2wpkh
.first_index
.collect_range_at(start_usize, end_usize);
let first_p2wsh_vec = indexer
.vecs
.addrs
.p2wsh.first_index
.p2wsh
.first_index
.collect_range_at(start_usize, end_usize);
// Track running totals - recover from previous height if resuming
@@ -187,10 +195,8 @@ pub(crate) fn process_blocks(
let (mut addr_counts, mut empty_addr_counts) = if starting_height > Height::ZERO {
let addr_counts =
AddrTypeToAddrCount::from((&vecs.addrs.funded.by_addr_type, starting_height));
let empty_addr_counts = AddrTypeToAddrCount::from((
&vecs.addrs.empty.by_addr_type,
starting_height,
));
let empty_addr_counts =
AddrTypeToAddrCount::from((&vecs.addrs.empty.by_addr_type, starting_height));
(addr_counts, empty_addr_counts)
} else {
(
@@ -237,7 +243,11 @@ pub(crate) fn process_blocks(
for height in starting_height.to_usize()..=last_height.to_usize() {
let height = Height::from(height);
info!("Processing chain at {}...", height);
if unlikely(height.is_multiple_of(100)) {
info!("Processing chain at {}...", height);
} else {
debug!("Processing chain at {}...", height);
}
// Get block metadata from pre-collected vecs
let offset = height.to_usize() - start_usize;
@@ -274,12 +284,18 @@ pub(crate) fn process_blocks(
// processing closures so outputs and inputs collection overlap each other
// and tick-tock, instead of running sequentially before the join.
let (matured, oi_result) = rayon::join(
|| vecs.utxo_cohorts.tick_tock_next_block(chain_state, timestamp),
|| {
vecs.utxo_cohorts
.tick_tock_next_block(chain_state, timestamp)
},
|| -> Result<_> {
let (outputs_result, inputs_result) = rayon::join(
|| {
let txout_index_to_tx_index = txout_to_tx_index_buf
.build(first_tx_index, tx_count, tx_index_to_output_count);
let txout_index_to_tx_index = txout_to_tx_index_buf.build(
first_tx_index,
tx_count,
tx_index_to_output_count,
);
let txout_data_vec =
txout_iters.collect_block_outputs(first_txout_index, output_count);
process_outputs(
@@ -294,14 +310,21 @@ pub(crate) fn process_blocks(
},
|| -> Result<_> {
if input_count > 1 {
let txin_index_to_tx_index = txin_to_tx_index_buf
.build(first_tx_index, tx_count, tx_index_to_input_count);
let (input_values, input_prev_heights, input_output_types, input_type_indexes) =
txin_iters.collect_block_inputs(
first_txin_index + 1,
input_count - 1,
height,
);
let txin_index_to_tx_index = txin_to_tx_index_buf.build(
first_tx_index,
tx_count,
tx_index_to_input_count,
);
let (
input_values,
input_prev_heights,
input_output_types,
input_type_indexes,
) = txin_iters.collect_block_inputs(
first_txin_index + 1,
input_count - 1,
height,
);
process_inputs(
input_count - 1,
&txin_index_to_tx_index[1..],
@@ -380,9 +403,9 @@ pub(crate) fn process_blocks(
blocks_old as u128 * u64::from(sent.spendable_supply.value) as u128
})
.sum();
vecs.coinblocks_destroyed.block.push(
StoredF64::from(total_satblocks as f64 / Sats::ONE_BTC_U128 as f64),
);
vecs.coinblocks_destroyed.block.push(StoredF64::from(
total_satblocks as f64 / Sats::ONE_BTC_U128 as f64,
));
}
// Record maturation (sats crossing age boundaries)
@@ -451,9 +474,11 @@ pub(crate) fn process_blocks(
vecs.utxo_cohorts.update_fenwick_from_pending();
// Push to height-indexed vectors
vecs.addrs.funded
vecs.addrs
.funded
.push_height(addr_counts.sum(), &addr_counts);
vecs.addrs.empty
vecs.addrs
.empty
.push_height(empty_addr_counts.sum(), &empty_addr_counts);
vecs.addrs.activity.push_height(&activity_counts);
@@ -467,11 +492,8 @@ pub(crate) fn process_blocks(
block_price,
);
vecs.utxo_cohorts.push_aggregate_percentiles(
block_price,
date_opt,
&vecs.states_path,
)?;
vecs.utxo_cohorts
.push_aggregate_percentiles(block_price, date_opt, &vecs.states_path)?;
// Periodic checkpoint flush
if height != last_height
@@ -534,20 +556,16 @@ fn push_cohort_states(
// Phase 1: push + unrealized (no reset yet — states still needed for aggregation)
rayon::join(
|| {
utxo_cohorts
.par_iter_separate_mut()
.for_each(|v| {
v.push_state(height);
v.push_unrealized_state(height_price);
})
utxo_cohorts.par_iter_separate_mut().for_each(|v| {
v.push_state(height);
v.push_unrealized_state(height_price);
})
},
|| {
addr_cohorts
.par_iter_separate_mut()
.for_each(|v| {
v.push_state(height);
v.push_unrealized_state(height_price);
})
addr_cohorts.par_iter_separate_mut().for_each(|v| {
v.push_state(height);
v.push_unrealized_state(height_price);
})
},
);
@@ -116,11 +116,10 @@ impl<'a> TxInReaders<'a> {
current_height: Height,
) -> (&[Sats], &[Height], &[OutputType], &[TypeIndex]) {
let end = first_txin_index + input_count;
self.txins.spent.value.collect_range_into_at(
first_txin_index,
end,
&mut self.values_buf,
);
self.txins
.spent
.value
.collect_range_into_at(first_txin_index, end, &mut self.values_buf);
self.indexer.vecs.inputs.outpoint.collect_range_into_at(
first_txin_index,
end,
@@ -138,8 +137,8 @@ impl<'a> TxInReaders<'a> {
);
self.prev_heights_buf.clear();
self.prev_heights_buf.extend(
self.outpoints_buf.iter().map(|outpoint| {
self.prev_heights_buf
.extend(self.outpoints_buf.iter().map(|outpoint| {
if outpoint.is_coinbase() {
current_height
} else {
@@ -147,8 +146,7 @@ impl<'a> TxInReaders<'a> {
.get(outpoint.tx_index())
.unwrap_or(current_height)
}
}),
);
}));
(
&self.values_buf,
@@ -166,10 +164,7 @@ pub struct VecsReaders {
}
impl VecsReaders {
pub(crate) fn new(
any_addr_indexes: &AnyAddrIndexesVecs,
addrs_data: &AddrsDataVecs,
) -> Self {
pub(crate) fn new(any_addr_indexes: &AnyAddrIndexesVecs, addrs_data: &AddrsDataVecs) -> Self {
Self {
addr_type_index_to_any_addr_index: ByAddrType {
p2a: any_addr_indexes.p2a.create_reader(),
@@ -185,10 +185,7 @@ fn rollback_states(
heights.insert(chain_height);
let Ok(stamps) = addr_indexes_rollbacks else {
warn!(
"addr_indexes rollback failed: {:?}",
addr_indexes_rollbacks
);
warn!("addr_indexes rollback failed: {:?}", addr_indexes_rollbacks);
return Height::ZERO;
};
for (i, s) in stamps.iter().enumerate() {
@@ -5,7 +5,10 @@ use derive_more::{Deref, DerefMut};
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
use crate::{
distribution::{metrics::ImportConfig, state::{CohortState, CostBasisOps, RealizedOps}},
distribution::{
metrics::ImportConfig,
state::{CohortState, CostBasisOps, RealizedOps},
},
internal::{AmountPerBlockCumulativeRolling, PerBlockCumulativeRolling},
prices,
};
@@ -46,19 +49,18 @@ impl ActivityCore {
}
#[inline(always)]
pub(crate) fn push_state(
&mut self,
state: &CohortState<impl RealizedOps, impl CostBasisOps>,
) {
pub(crate) fn push_state(&mut self, state: &CohortState<impl RealizedOps, impl CostBasisOps>) {
self.minimal.push_state(state);
self.coindays_destroyed.block.push(
StoredF64::from(Bitcoin::from(state.satdays_destroyed)),
);
self.coindays_destroyed
.block
.push(StoredF64::from(Bitcoin::from(state.satdays_destroyed)));
self.transfer_volume_in_profit
.block.sats
.block
.sats
.push(state.realized.sent_in_profit());
self.transfer_volume_in_loss
.block.sats
.block
.sats
.push(state.realized.sent_in_loss());
}
@@ -7,7 +7,10 @@ use vecdb::{AnyStoredVec, Exit, ReadableCloneableVec, Rw, StorageMode};
use crate::internal::{Identity, LazyPerBlock, PerBlock, Windows};
use crate::{
distribution::{metrics::ImportConfig, state::{CohortState, CostBasisOps, RealizedOps}},
distribution::{
metrics::ImportConfig,
state::{CohortState, CostBasisOps, RealizedOps},
},
prices,
};
@@ -33,7 +36,12 @@ impl ActivityFull {
let coinyears_destroyed = LazyPerBlock::from_height_source::<Identity<StoredF64>>(
&cfg.name("coinyears_destroyed"),
cfg.version + v1,
inner.coindays_destroyed.sum._1y.height.read_only_boxed_clone(),
inner
.coindays_destroyed
.sum
._1y
.height
.read_only_boxed_clone(),
cfg.indexes,
);
@@ -89,7 +97,8 @@ impl ActivityFull {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.inner.compute_rest_part1(prices, starting_indexes, exit)?;
self.inner
.compute_rest_part1(prices, starting_indexes, exit)?;
for ((dormancy, cdd_sum), tv_sum) in self
.dormancy
@@ -4,7 +4,10 @@ use brk_types::{Indexes, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
use crate::{
distribution::{metrics::ImportConfig, state::{CohortState, CostBasisOps, RealizedOps}},
distribution::{
metrics::ImportConfig,
state::{CohortState, CostBasisOps, RealizedOps},
},
internal::AmountPerBlockCumulativeRolling,
prices,
};
@@ -23,16 +26,11 @@ impl ActivityMinimal {
}
pub(crate) fn min_len(&self) -> usize {
self.transfer_volume
.block.sats
.len()
self.transfer_volume.block.sats.len()
}
#[inline(always)]
pub(crate) fn push_state(
&mut self,
state: &CohortState<impl RealizedOps, impl CostBasisOps>,
) {
pub(crate) fn push_state(&mut self, state: &CohortState<impl RealizedOps, impl CostBasisOps>) {
self.transfer_volume.block.sats.push(state.sent);
}
@@ -19,10 +19,7 @@ pub trait ActivityLike: Send + Sync {
fn as_core(&self) -> &ActivityCore;
fn as_core_mut(&mut self) -> &mut ActivityCore;
fn min_len(&self) -> usize;
fn push_state<R: RealizedOps>(
&mut self,
state: &CohortState<R, impl CostBasisOps>,
);
fn push_state<R: RealizedOps>(&mut self, state: &CohortState<R, impl CostBasisOps>);
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()>;
fn compute_from_stateful(
&mut self,
@@ -39,37 +36,69 @@ pub trait ActivityLike: Send + Sync {
}
impl ActivityLike for ActivityCore {
fn as_core(&self) -> &ActivityCore { self }
fn as_core_mut(&mut self) -> &mut ActivityCore { self }
fn min_len(&self) -> usize { self.min_len() }
fn as_core(&self) -> &ActivityCore {
self
}
fn as_core_mut(&mut self) -> &mut ActivityCore {
self
}
fn min_len(&self) -> usize {
self.min_len()
}
fn push_state<R: RealizedOps>(&mut self, state: &CohortState<R, impl CostBasisOps>) {
self.push_state(state);
}
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
self.validate_computed_versions(base_version)
}
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&ActivityCore], exit: &Exit) -> Result<()> {
fn compute_from_stateful(
&mut self,
starting_indexes: &Indexes,
others: &[&ActivityCore],
exit: &Exit,
) -> Result<()> {
self.compute_from_stateful(starting_indexes, others, exit)
}
fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
fn compute_rest_part1(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.compute_rest_part1(prices, starting_indexes, exit)
}
}
impl ActivityLike for ActivityFull {
fn as_core(&self) -> &ActivityCore { &self.inner }
fn as_core_mut(&mut self) -> &mut ActivityCore { &mut self.inner }
fn min_len(&self) -> usize { self.full_min_len() }
fn as_core(&self) -> &ActivityCore {
&self.inner
}
fn as_core_mut(&mut self) -> &mut ActivityCore {
&mut self.inner
}
fn min_len(&self) -> usize {
self.full_min_len()
}
fn push_state<R: RealizedOps>(&mut self, state: &CohortState<R, impl CostBasisOps>) {
self.full_push_state(state);
}
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
self.inner.validate_computed_versions(base_version)
}
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&ActivityCore], exit: &Exit) -> Result<()> {
fn compute_from_stateful(
&mut self,
starting_indexes: &Indexes,
others: &[&ActivityCore],
exit: &Exit,
) -> Result<()> {
self.compute_from_stateful(starting_indexes, others, exit)
}
fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
fn compute_rest_part1(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.compute_rest_part1(prices, starting_indexes, exit)
}
}
@@ -1,9 +1,7 @@
use brk_cohort::Filter;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
Cents, Dollars, Height, Indexes, Version,
};
use brk_types::{Cents, Dollars, Height, Indexes, Version};
use vecdb::AnyStoredVec;
use vecdb::{Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode};
@@ -121,7 +119,7 @@ impl AllCohortMetrics {
self.unrealized.compute(
starting_indexes.height,
&prices.spot.cents.height,
&prices.cached_spot_cents,
&self.realized.price.cents.height,
exit,
)?;
@@ -136,15 +134,12 @@ impl AllCohortMetrics {
)?;
let all_utxo_count = self.outputs.unspent_count.height.read_only_clone();
self.outputs.compute_part2(
starting_indexes.height,
&all_utxo_count,
exit,
)?;
self.outputs
.compute_part2(starting_indexes.height, &all_utxo_count, exit)?;
self.cost_basis.compute_prices(
starting_indexes,
&prices.spot.cents.height,
&prices.cached_spot_cents,
&self.unrealized.invested_capital.in_profit.cents.height,
&self.unrealized.invested_capital.in_loss.cents.height,
&self.supply.in_profit.sats.height,
@@ -154,11 +149,8 @@ impl AllCohortMetrics {
exit,
)?;
self.unrealized.compute_sentiment(
starting_indexes,
&prices.spot.cents.height,
exit,
)?;
self.unrealized
.compute_sentiment(starting_indexes, &prices.cached_spot_cents, exit)?;
self.relative.compute(
starting_indexes.height,
@@ -82,21 +82,17 @@ impl BasicCohortMetrics {
self.unrealized.compute(
starting_indexes.height,
&prices.spot.cents.height,
&prices.cached_spot_cents,
&self.realized.price.cents.height,
exit,
)?;
self.relative.compute(
starting_indexes.height,
&self.supply,
all_supply_sats,
exit,
)?;
self.relative
.compute(starting_indexes.height, &self.supply, all_supply_sats, exit)?;
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
self.outputs
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
Ok(())
}
}
@@ -92,7 +92,10 @@ impl CoreCohortMetrics {
)?;
self.unrealized.compute_from_stateful(
starting_indexes,
&others.iter().map(|v| v.unrealized_core()).collect::<Vec<_>>(),
&others
.iter()
.map(|v| v.unrealized_core())
.collect::<Vec<_>>(),
exit,
)?;
@@ -105,16 +108,14 @@ impl CoreCohortMetrics {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.supply
.compute(prices, starting_indexes.height, exit)?;
self.supply.compute(prices, starting_indexes.height, exit)?;
self.outputs.compute_rest(starting_indexes.height, exit)?;
self.activity
.compute_rest_part1(prices, starting_indexes, exit)?;
self.realized
.compute_rest_part1(starting_indexes, exit)?;
self.realized.compute_rest_part1(starting_indexes, exit)?;
self.unrealized.compute_rest(starting_indexes, exit)?;
@@ -139,19 +140,16 @@ impl CoreCohortMetrics {
self.unrealized.compute(
starting_indexes.height,
&prices.spot.cents.height,
&prices.cached_spot_cents,
&self.realized.price.cents.height,
exit,
)?;
self.relative.compute(
starting_indexes.height,
&self.supply,
all_supply_sats,
exit,
)?;
self.relative
.compute(starting_indexes.height, &self.supply, all_supply_sats, exit)?;
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
self.outputs
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
Ok(())
}
@@ -108,14 +108,14 @@ impl ExtendedCohortMetrics {
self.unrealized.compute(
starting_indexes.height,
&prices.spot.cents.height,
&prices.cached_spot_cents,
&self.realized.price.cents.height,
exit,
)?;
self.cost_basis.compute_prices(
starting_indexes,
&prices.spot.cents.height,
&prices.cached_spot_cents,
&self.unrealized.invested_capital.in_profit.cents.height,
&self.unrealized.invested_capital.in_loss.cents.height,
&self.supply.in_profit.sats.height,
@@ -126,7 +126,7 @@ impl ExtendedCohortMetrics {
)?;
self.unrealized
.compute_sentiment(starting_indexes, &prices.spot.cents.height, exit)?;
.compute_sentiment(starting_indexes, &prices.cached_spot_cents, exit)?;
self.relative.compute(
starting_indexes.height,
@@ -104,8 +104,7 @@ impl MinimalCohortMetrics {
self.outputs.compute_rest(starting_indexes.height, exit)?;
self.activity
.compute_rest_part1(prices, starting_indexes, exit)?;
self.realized
.compute_rest_part1(starting_indexes, exit)?;
self.realized.compute_rest_part1(starting_indexes, exit)?;
Ok(())
}
@@ -125,12 +124,13 @@ impl MinimalCohortMetrics {
self.unrealized.compute(
starting_indexes.height,
&prices.spot.cents.height,
&prices.cached_spot_cents,
&self.realized.price.cents.height,
exit,
)?;
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
self.outputs
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
Ok(())
}
@@ -66,8 +66,7 @@ impl TypeCohortMetrics {
self.outputs.compute_rest(starting_indexes.height, exit)?;
self.activity
.compute_rest_part1(prices, starting_indexes, exit)?;
self.realized
.compute_rest_part1(starting_indexes, exit)?;
self.realized.compute_rest_part1(starting_indexes, exit)?;
Ok(())
}
@@ -87,12 +86,13 @@ impl TypeCohortMetrics {
self.unrealized.compute(
starting_indexes.height,
&prices.spot.cents.height,
&prices.cached_spot_cents,
&self.realized.price.cents.height,
exit,
)?;
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
self.outputs
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
Ok(())
}
@@ -4,9 +4,7 @@ use brk_types::CentsSquaredSats;
use brk_types::{BasisPoints16, Cents, Height, Indexes, Sats, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
use crate::internal::{
PERCENTILES_LEN, PerBlock, PercentPerBlock, PercentilesVecs, Price,
};
use crate::internal::{PERCENTILES_LEN, PerBlock, PercentPerBlock, PercentilesVecs, Price};
use super::ImportConfig;
@@ -33,12 +31,32 @@ impl CostBasis {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
Ok(Self {
in_profit: CostBasisSide {
per_coin: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_profit_per_coin"), cfg.version + Version::ONE, cfg.indexes)?,
per_dollar: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_profit_per_dollar"), cfg.version + Version::ONE, cfg.indexes)?,
per_coin: Price::forced_import(
cfg.db,
&cfg.name("cost_basis_in_profit_per_coin"),
cfg.version + Version::ONE,
cfg.indexes,
)?,
per_dollar: Price::forced_import(
cfg.db,
&cfg.name("cost_basis_in_profit_per_dollar"),
cfg.version + Version::ONE,
cfg.indexes,
)?,
},
in_loss: CostBasisSide {
per_coin: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_loss_per_coin"), cfg.version + Version::ONE, cfg.indexes)?,
per_dollar: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_loss_per_dollar"), cfg.version + Version::ONE, cfg.indexes)?,
per_coin: Price::forced_import(
cfg.db,
&cfg.name("cost_basis_in_loss_per_coin"),
cfg.version + Version::ONE,
cfg.indexes,
)?,
per_dollar: Price::forced_import(
cfg.db,
&cfg.name("cost_basis_in_loss_per_dollar"),
cfg.version + Version::ONE,
cfg.indexes,
)?,
},
min: cfg.import("cost_basis_min", Version::ZERO)?,
max: cfg.import("cost_basis_max", Version::ZERO)?,
@@ -51,7 +51,12 @@ macro_rules! impl_cohort_accessors {
fn unrealized_mut(&mut self) -> &mut Self::UnrealizedVecs {
&mut self.unrealized
}
fn supply_and_unrealized_mut(&mut self) -> (&$crate::distribution::metrics::SupplyCore, &mut Self::UnrealizedVecs) {
fn supply_and_unrealized_mut(
&mut self,
) -> (
&$crate::distribution::metrics::SupplyCore,
&mut Self::UnrealizedVecs,
) {
(&*self.supply, &mut self.unrealized)
}
};
@@ -94,7 +99,12 @@ macro_rules! impl_cohort_accessors_inner {
fn unrealized_mut(&mut self) -> &mut Self::UnrealizedVecs {
&mut self.inner.unrealized
}
fn supply_and_unrealized_mut(&mut self) -> (&$crate::distribution::metrics::SupplyCore, &mut Self::UnrealizedVecs) {
fn supply_and_unrealized_mut(
&mut self,
) -> (
&$crate::distribution::metrics::SupplyCore,
&mut Self::UnrealizedVecs,
) {
(&*self.inner.supply, &mut self.inner.unrealized)
}
};
@@ -125,8 +135,7 @@ pub use realized::{
pub use relative::{RelativeForAll, RelativeToAll, RelativeWithExtended};
pub use supply::{SupplyBase, SupplyCore};
pub use unrealized::{
UnrealizedBasic, UnrealizedCore, UnrealizedFull, UnrealizedLike,
UnrealizedMinimal,
UnrealizedBasic, UnrealizedCore, UnrealizedFull, UnrealizedLike, UnrealizedMinimal,
};
use brk_cohort::Filter;
@@ -250,10 +259,7 @@ pub trait CohortMetricsBase:
.min(self.unrealized().min_stateful_len())
}
fn push_state(
&mut self,
state: &CohortState<RealizedState, CostBasisData<WithCapital>>,
) {
fn push_state(&mut self, state: &CohortState<RealizedState, CostBasisData<WithCapital>>) {
self.supply_mut().push_state(state);
self.outputs_mut().push_state(state);
self.activity_mut().push_state(state);
@@ -6,7 +6,9 @@ use vecdb::{AnyStoredVec, AnyVec, Database, Exit, Rw, StorageMode, WritableVec};
use crate::{
indexes,
internal::{AmountPerBlock, AmountPerBlockWithDeltas, CachedWindowStarts, PerBlock, RatioPerBlock},
internal::{
AmountPerBlock, AmountPerBlockWithDeltas, CachedWindowStarts, PerBlock, RatioPerBlock,
},
prices,
};
@@ -124,7 +126,7 @@ impl ProfitabilityBucket {
self.unrealized_pnl.all.height.compute_transform3(
max_from,
&prices.spot.cents.height,
&prices.cached_spot_cents,
&self.realized_cap.all.height,
&self.supply.all.sats.height,
|(i, spot, cap, supply, ..)| {
@@ -137,7 +139,7 @@ impl ProfitabilityBucket {
)?;
self.unrealized_pnl.sth.height.compute_transform3(
max_from,
&prices.spot.cents.height,
&prices.cached_spot_cents,
&self.realized_cap.sth.height,
&self.supply.sth.sats.height,
|(i, spot, cap, supply, ..)| {
@@ -151,7 +153,7 @@ impl ProfitabilityBucket {
self.nupl.bps.height.compute_transform3(
max_from,
&prices.spot.cents.height,
&prices.cached_spot_cents,
&self.realized_cap.all.height,
&self.supply.all.sats.height,
|(i, spot, cap_dollars, supply_sats, ..)| {
@@ -183,22 +185,34 @@ impl ProfitabilityBucket {
self.supply.all.sats.height.compute_sum_of_others(
max_from,
&sources.iter().map(|s| &s.supply.all.sats.height).collect::<Vec<_>>(),
&sources
.iter()
.map(|s| &s.supply.all.sats.height)
.collect::<Vec<_>>(),
exit,
)?;
self.supply.sth.sats.height.compute_sum_of_others(
max_from,
&sources.iter().map(|s| &s.supply.sth.sats.height).collect::<Vec<_>>(),
&sources
.iter()
.map(|s| &s.supply.sth.sats.height)
.collect::<Vec<_>>(),
exit,
)?;
self.realized_cap.all.height.compute_sum_of_others(
max_from,
&sources.iter().map(|s| &s.realized_cap.all.height).collect::<Vec<_>>(),
&sources
.iter()
.map(|s| &s.realized_cap.all.height)
.collect::<Vec<_>>(),
exit,
)?;
self.realized_cap.sth.height.compute_sum_of_others(
max_from,
&sources.iter().map(|s| &s.realized_cap.sth.height).collect::<Vec<_>>(),
&sources
.iter()
.map(|s| &s.realized_cap.sth.height)
.collect::<Vec<_>>(),
exit,
)?;
@@ -259,7 +273,7 @@ impl ProfitabilityMetrics {
ProfitabilityBucket::forced_import(db, name, version, indexes, cached_starts)
})?;
let aggregate_version = version + Version::ONE;
let aggregate_version = version + Version::TWO;
let profit = Profit::try_new(|name| {
ProfitabilityBucket::forced_import(db, name, aggregate_version, indexes, cached_starts)
@@ -1,6 +1,8 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{BasisPointsSigned32, Bitcoin, Cents, CentsSigned, Dollars, Height, Indexes, StoredF64, Version};
use brk_types::{
BasisPointsSigned32, Bitcoin, Cents, CentsSigned, Dollars, Height, Indexes, StoredF64, Version,
};
use derive_more::{Deref, DerefMut};
use vecdb::{
AnyStoredVec, Exit, LazyVecFrom1, ReadableCloneableVec, ReadableVec, Rw, StorageMode,
@@ -42,7 +44,8 @@ pub struct RealizedCore<M: StorageMode = Rw> {
#[traversable(wrap = "loss", rename = "negative")]
pub neg_loss: NegRealizedLoss,
pub net_pnl: FiatPerBlockCumulativeWithSumsAndDeltas<CentsSigned, CentsSigned, BasisPointsSigned32, M>,
pub net_pnl:
FiatPerBlockCumulativeWithSumsAndDeltas<CentsSigned, CentsSigned, BasisPointsSigned32, M>,
pub sopr: RealizedSoprCore<M>,
}
@@ -107,7 +110,10 @@ impl RealizedCore {
#[inline(always)]
pub(crate) fn push_state(&mut self, state: &CohortState<impl RealizedOps, impl CostBasisOps>) {
self.minimal.push_state(state);
self.sopr.value_destroyed.block.push(state.realized.value_destroyed());
self.sopr
.value_destroyed
.block
.push(state.realized.value_destroyed());
}
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
@@ -135,8 +141,7 @@ impl RealizedCore {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.minimal
.compute_rest_part1(starting_indexes, exit)?;
self.minimal.compute_rest_part1(starting_indexes, exit)?;
self.sopr
.value_destroyed
@@ -169,8 +174,7 @@ impl RealizedCore {
self.minimal
.compute_rest_part2(prices, starting_indexes, height_to_supply, exit)?;
self.net_pnl
.compute_rest(starting_indexes.height, exit)?;
self.net_pnl.compute_rest(starting_indexes.height, exit)?;
self.sopr
.ratio
@@ -178,7 +178,8 @@ impl RealizedFull {
.push(state.realized.investor_cap_raw());
self.peak_regret
.value
.block.cents
.block
.cents
.push(state.realized.peak_regret());
}
@@ -218,10 +219,7 @@ impl RealizedFull {
};
self.investor.price.cents.height.push(investor_price);
self.peak_regret
.value
.block.cents
.push(accum.peak_regret());
self.peak_regret.value.block.cents.push(accum.peak_regret());
}
pub(crate) fn compute_rest_part1(
@@ -1,18 +1,16 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
BasisPoints32, BasisPointsSigned32, Bitcoin, Cents, CentsSigned, Height, Indexes, Sats, StoredF32,
Version,
};
use vecdb::{
AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec,
BasisPoints32, BasisPointsSigned32, Bitcoin, Cents, CentsSigned, Height, Indexes, Sats,
StoredF32, Version,
};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
use crate::{
distribution::state::{CohortState, CostBasisOps, RealizedOps},
internal::{
FiatPerBlockCumulativeWithSums,
FiatPerBlockWithDeltas, Identity, LazyPerBlock, PriceWithRatioPerBlock,
FiatPerBlockCumulativeWithSums, FiatPerBlockWithDeltas, Identity, LazyPerBlock,
PriceWithRatioPerBlock,
},
prices,
};
@@ -111,23 +109,24 @@ impl RealizedMinimal {
exit: &Exit,
) -> Result<()> {
let cap = &self.cap.cents.height;
self.price.compute_all(prices, starting_indexes, exit, |v| {
Ok(v.compute_transform2(
starting_indexes.height,
cap,
height_to_supply,
|(i, cap_cents, supply, ..)| {
let cap = cap_cents.as_u128();
let supply_sats = Sats::from(supply).as_u128();
if supply_sats == 0 {
(i, Cents::ZERO)
} else {
(i, Cents::from(cap * Sats::ONE_BTC_U128 / supply_sats))
}
},
exit,
)?)
})?;
self.price
.compute_all(prices, starting_indexes, exit, |v| {
Ok(v.compute_transform2(
starting_indexes.height,
cap,
height_to_supply,
|(i, cap_cents, supply, ..)| {
let cap = cap_cents.as_u128();
let supply_sats = Sats::from(supply).as_u128();
if supply_sats == 0 {
(i, Cents::ZERO)
} else {
(i, Cents::from(cap * Sats::ONE_BTC_U128 / supply_sats))
}
},
exit,
)?)
})?;
Ok(())
}
@@ -3,8 +3,8 @@ mod core;
mod full;
mod minimal;
pub use adjusted::AdjustedSopr;
pub use self::core::RealizedCore;
pub use adjusted::AdjustedSopr;
pub use full::{RealizedFull, RealizedFullAccum};
pub use minimal::RealizedMinimal;
@@ -12,7 +12,7 @@ use brk_error::Result;
use brk_types::Indexes;
use vecdb::Exit;
use crate::distribution::state::{WithCapital, CohortState, CostBasisData, RealizedState};
use crate::distribution::state::{CohortState, CostBasisData, RealizedState, WithCapital};
pub trait RealizedLike: Send + Sync {
fn as_core(&self) -> &RealizedCore;
@@ -29,9 +29,15 @@ pub trait RealizedLike: Send + Sync {
}
impl RealizedLike for RealizedCore {
fn as_core(&self) -> &RealizedCore { self }
fn as_core_mut(&mut self) -> &mut RealizedCore { self }
fn min_stateful_len(&self) -> usize { self.min_stateful_len() }
fn as_core(&self) -> &RealizedCore {
self
}
fn as_core_mut(&mut self) -> &mut RealizedCore {
self
}
fn min_stateful_len(&self) -> usize {
self.min_stateful_len()
}
#[inline(always)]
fn push_state(&mut self, state: &CohortState<RealizedState, CostBasisData<WithCapital>>) {
self.push_state(state)
@@ -39,15 +45,26 @@ impl RealizedLike for RealizedCore {
fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
self.compute_rest_part1(starting_indexes, exit)
}
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedCore], exit: &Exit) -> Result<()> {
fn compute_from_stateful(
&mut self,
starting_indexes: &Indexes,
others: &[&RealizedCore],
exit: &Exit,
) -> Result<()> {
self.compute_from_stateful(starting_indexes, others, exit)
}
}
impl RealizedLike for RealizedFull {
fn as_core(&self) -> &RealizedCore { &self.core }
fn as_core_mut(&mut self) -> &mut RealizedCore { &mut self.core }
fn min_stateful_len(&self) -> usize { self.min_stateful_len() }
fn as_core(&self) -> &RealizedCore {
&self.core
}
fn as_core_mut(&mut self) -> &mut RealizedCore {
&mut self.core
}
fn min_stateful_len(&self) -> usize {
self.min_stateful_len()
}
#[inline(always)]
fn push_state(&mut self, state: &CohortState<RealizedState, CostBasisData<WithCapital>>) {
self.push_state(state)
@@ -55,7 +72,12 @@ impl RealizedLike for RealizedFull {
fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
self.compute_rest_part1(starting_indexes, exit)
}
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedCore], exit: &Exit) -> Result<()> {
fn compute_from_stateful(
&mut self,
starting_indexes: &Indexes,
others: &[&RealizedCore],
exit: &Exit,
) -> Result<()> {
self.compute_from_stateful(starting_indexes, others, exit)
}
}
@@ -23,8 +23,7 @@ impl RelativeExtendedOwnMarketCap {
let v2 = Version::new(2);
Ok(Self {
unrealized_profit_to_own_mcap: cfg
.import("unrealized_profit_to_own_mcap", v2)?,
unrealized_profit_to_own_mcap: cfg.import("unrealized_profit_to_own_mcap", v2)?,
unrealized_loss_to_own_mcap: cfg
.import("unrealized_loss_to_own_mcap", Version::new(3))?,
net_unrealized_pnl_to_own_mcap: cfg
@@ -16,7 +16,6 @@ pub struct RelativeExtendedOwnPnl<M: StorageMode = Rw> {
pub unrealized_loss_to_own_gross_pnl: PercentPerBlock<BasisPoints16, M>,
#[traversable(wrap = "unrealized/net_pnl", rename = "to_own_gross_pnl")]
pub net_unrealized_pnl_to_own_gross_pnl: PercentPerBlock<BasisPointsSigned32, M>,
}
impl RelativeExtendedOwnPnl {
@@ -26,8 +25,7 @@ impl RelativeExtendedOwnPnl {
Ok(Self {
unrealized_profit_to_own_gross_pnl: cfg
.import("unrealized_profit_to_own_gross_pnl", v1)?,
unrealized_loss_to_own_gross_pnl: cfg
.import("unrealized_loss_to_own_gross_pnl", v1)?,
unrealized_loss_to_own_gross_pnl: cfg.import("unrealized_loss_to_own_gross_pnl", v1)?,
net_unrealized_pnl_to_own_gross_pnl: cfg
.import("net_unrealized_pnl_to_own_gross_pnl", Version::new(3))?,
})
@@ -6,7 +6,7 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use crate::distribution::metrics::{ImportConfig, SupplyCore, UnrealizedFull};
use super::{RelativeFull, RelativeExtendedOwnPnl};
use super::{RelativeExtendedOwnPnl, RelativeFull};
/// Relative metrics for the "all" cohort (base + own_pnl, NO rel_to_all).
#[derive(Deref, DerefMut, Traversable)]
@@ -35,15 +35,14 @@ impl RelativeForAll {
market_cap: &impl ReadableVec<Height, Dollars>,
exit: &Exit,
) -> Result<()> {
self.base.compute(
self.base
.compute(max_from, supply, &unrealized.inner.basic, market_cap, exit)?;
self.extended_own_pnl.compute(
max_from,
supply,
&unrealized.inner.basic,
market_cap,
&unrealized.inner,
&unrealized.gross_pnl.usd.height,
exit,
)?;
self.extended_own_pnl
.compute(max_from, &unrealized.inner, &unrealized.gross_pnl.usd.height, exit)?;
Ok(())
}
}
@@ -28,13 +28,10 @@ impl RelativeFull {
let v2 = Version::new(2);
Ok(Self {
supply_in_profit_to_own: cfg
.import("supply_in_profit_to_own", v1)?,
supply_in_profit_to_own: cfg.import("supply_in_profit_to_own", v1)?,
supply_in_loss_to_own: cfg.import("supply_in_loss_to_own", v1)?,
unrealized_profit_to_mcap: cfg
.import("unrealized_profit_to_mcap", v2)?,
unrealized_loss_to_mcap: cfg
.import("unrealized_loss_to_mcap", v2)?,
unrealized_profit_to_mcap: cfg.import("unrealized_profit_to_mcap", v2)?,
unrealized_loss_to_mcap: cfg.import("unrealized_loss_to_mcap", v2)?,
})
}

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