Compare commits

...

19 Commits

Author SHA1 Message Date
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
826 changed files with 12806 additions and 7458 deletions
Generated
+73 -73
View File
@@ -338,7 +338,7 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
name = "brk"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"brk_bencher",
"brk_bindgen",
@@ -399,7 +399,7 @@ dependencies = [
[[package]]
name = "brk_alloc"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"libmimalloc-sys",
"mimalloc",
@@ -407,7 +407,7 @@ dependencies = [
[[package]]
name = "brk_bencher"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"brk_error",
"brk_logger",
@@ -417,14 +417,14 @@ dependencies = [
[[package]]
name = "brk_bencher_visualizer"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"plotters",
]
[[package]]
name = "brk_bindgen"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"brk_cohort",
"brk_query",
@@ -437,7 +437,7 @@ dependencies = [
[[package]]
name = "brk_cli"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"anyhow",
"brk_alloc",
@@ -463,7 +463,7 @@ dependencies = [
[[package]]
name = "brk_client"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"brk_cohort",
"brk_types",
@@ -474,7 +474,7 @@ dependencies = [
[[package]]
name = "brk_cohort"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"brk_error",
"brk_traversable",
@@ -486,7 +486,7 @@ dependencies = [
[[package]]
name = "brk_computer"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"bitcoin",
"brk_alloc",
@@ -517,7 +517,7 @@ dependencies = [
[[package]]
name = "brk_error"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -534,7 +534,7 @@ dependencies = [
[[package]]
name = "brk_fetcher"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"brk_error",
"brk_logger",
@@ -546,7 +546,7 @@ dependencies = [
[[package]]
name = "brk_indexer"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"bitcoin",
"brk_alloc",
@@ -573,7 +573,7 @@ dependencies = [
[[package]]
name = "brk_iterator"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"brk_error",
"brk_reader",
@@ -583,7 +583,7 @@ dependencies = [
[[package]]
name = "brk_logger"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"jiff",
"owo-colors",
@@ -594,7 +594,7 @@ dependencies = [
[[package]]
name = "brk_mempool"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"brk_error",
"brk_logger",
@@ -609,7 +609,7 @@ dependencies = [
[[package]]
name = "brk_oracle"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"brk_indexer",
"brk_types",
@@ -619,7 +619,7 @@ dependencies = [
[[package]]
name = "brk_query"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"bitcoin",
"brk_computer",
@@ -641,7 +641,7 @@ dependencies = [
[[package]]
name = "brk_reader"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"bitcoin",
"brk_error",
@@ -656,7 +656,7 @@ dependencies = [
[[package]]
name = "brk_rpc"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -673,7 +673,7 @@ dependencies = [
[[package]]
name = "brk_server"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"aide",
"axum",
@@ -708,7 +708,7 @@ dependencies = [
[[package]]
name = "brk_store"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"brk_error",
"brk_types",
@@ -719,7 +719,7 @@ dependencies = [
[[package]]
name = "brk_traversable"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"brk_traversable_derive",
"brk_types",
@@ -732,7 +732,7 @@ dependencies = [
[[package]]
name = "brk_traversable_derive"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"proc-macro2",
"quote",
@@ -741,7 +741,7 @@ dependencies = [
[[package]]
name = "brk_types"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"bitcoin",
"brk_error",
@@ -764,7 +764,7 @@ dependencies = [
[[package]]
name = "brk_website"
version = "0.2.2"
version = "0.2.5"
dependencies = [
"axum",
"brk_logger",
@@ -836,9 +836,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",
@@ -1967,9 +1967,9 @@ checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07"
[[package]]
name = "js-sys"
version = "0.3.91"
version = "0.3.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -2045,9 +2045,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",
]
@@ -2180,9 +2180,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 +2201,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 +2216,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",
@@ -2545,9 +2545,9 @@ dependencies = [
[[package]]
name = "rawdb"
version = "0.7.2"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "912a9c6f76a5f141057139d510b969b082ff74f39a72a1c27178d8e1eeec95dc"
checksum = "fddb06a11fcc5f7f44d9b5bee4ab61b5a1135232b2fd239253428abd192ba504"
dependencies = [
"libc",
"log",
@@ -2678,9 +2678,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 +2916,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "1.0.4"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98"
dependencies = [
"serde_core",
]
@@ -2963,9 +2963,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"
@@ -3182,9 +3182,9 @@ dependencies = [
[[package]]
name = "toml"
version = "1.0.7+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd28d57d8a6f6e458bc0b8784f8fdcc4b99a437936056fa122cb234f18656a96"
checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc"
dependencies = [
"indexmap",
"serde_core",
@@ -3197,27 +3197,27 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "1.0.1+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9"
checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.0.10+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.0.7+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d"
checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed"
[[package]]
name = "tower"
@@ -3352,9 +3352,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,9 +3439,9 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23"
[[package]]
name = "vecdb"
version = "0.7.2"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f89be182f86511ee28832cc04039d818564b88c63b14093fcf1871c732c0dd"
checksum = "a33f1cbef9bf38048ee1b51328366f0a734e06bcc0b9739d68fef9ecce43d0b8"
dependencies = [
"itoa",
"libc",
@@ -3462,9 +3462,9 @@ dependencies = [
[[package]]
name = "vecdb_derive"
version = "0.7.2"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42915a5ca404d941e3e6d024f794403c481e5aeb96b1867d12d1f1e972d1433f"
checksum = "33d31f03d1c7269d65195fb4d54c1d510b124807871bd11af7d10a08700d7590"
dependencies = [
"quote",
"syn",
@@ -3512,9 +3512,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
version = "0.2.114"
version = "0.2.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a"
dependencies = [
"cfg-if",
"once_cell",
@@ -3525,9 +3525,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.114"
version = "0.2.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3535,9 +3535,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.114"
version = "0.2.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -3548,9 +3548,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.114"
version = "0.2.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93"
dependencies = [
"unicode-ident",
]
@@ -3591,9 +3591,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.91"
version = "0.3.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9"
checksum = "84cde8507f4d7cfcb1185b8cb5890c494ffea65edbe1ba82cfd63661c805ed94"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -3937,18 +3937,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",
+26 -26
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.2"
package.version = "0.2.5"
package.homepage = "https://bitcoinresearchkit.org"
package.repository = "https://github.com/bitcoinresearchkit/brk"
package.readme = "README.md"
@@ -40,29 +40,29 @@ 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.2", path = "crates/brk_alloc" }
brk_bencher = { version = "0.2.2", path = "crates/brk_bencher" }
brk_bindgen = { version = "0.2.2", path = "crates/brk_bindgen" }
brk_cli = { version = "0.2.2", path = "crates/brk_cli" }
brk_client = { version = "0.2.2", path = "crates/brk_client" }
brk_cohort = { version = "0.2.2", path = "crates/brk_cohort" }
brk_computer = { version = "0.2.2", path = "crates/brk_computer" }
brk_error = { version = "0.2.2", path = "crates/brk_error" }
brk_fetcher = { version = "0.2.2", path = "crates/brk_fetcher" }
brk_indexer = { version = "0.2.2", path = "crates/brk_indexer" }
brk_iterator = { version = "0.2.2", path = "crates/brk_iterator" }
brk_logger = { version = "0.2.2", path = "crates/brk_logger" }
brk_mempool = { version = "0.2.2", path = "crates/brk_mempool" }
brk_oracle = { version = "0.2.2", path = "crates/brk_oracle" }
brk_query = { version = "0.2.2", path = "crates/brk_query", features = ["tokio"] }
brk_reader = { version = "0.2.2", path = "crates/brk_reader" }
brk_rpc = { version = "0.2.2", path = "crates/brk_rpc" }
brk_server = { version = "0.2.2", path = "crates/brk_server" }
brk_store = { version = "0.2.2", path = "crates/brk_store" }
brk_traversable = { version = "0.2.2", path = "crates/brk_traversable", features = ["pco", "derive"] }
brk_traversable_derive = { version = "0.2.2", path = "crates/brk_traversable_derive" }
brk_types = { version = "0.2.2", path = "crates/brk_types" }
brk_website = { version = "0.2.2", path = "crates/brk_website" }
brk_alloc = { version = "0.2.5", path = "crates/brk_alloc" }
brk_bencher = { version = "0.2.5", path = "crates/brk_bencher" }
brk_bindgen = { version = "0.2.5", path = "crates/brk_bindgen" }
brk_cli = { version = "0.2.5", path = "crates/brk_cli" }
brk_client = { version = "0.2.5", path = "crates/brk_client" }
brk_cohort = { version = "0.2.5", path = "crates/brk_cohort" }
brk_computer = { version = "0.2.5", path = "crates/brk_computer" }
brk_error = { version = "0.2.5", path = "crates/brk_error" }
brk_fetcher = { version = "0.2.5", path = "crates/brk_fetcher" }
brk_indexer = { version = "0.2.5", path = "crates/brk_indexer" }
brk_iterator = { version = "0.2.5", path = "crates/brk_iterator" }
brk_logger = { version = "0.2.5", path = "crates/brk_logger" }
brk_mempool = { version = "0.2.5", path = "crates/brk_mempool" }
brk_oracle = { version = "0.2.5", path = "crates/brk_oracle" }
brk_query = { version = "0.2.5", path = "crates/brk_query", features = ["tokio"] }
brk_reader = { version = "0.2.5", path = "crates/brk_reader" }
brk_rpc = { version = "0.2.5", path = "crates/brk_rpc" }
brk_server = { version = "0.2.5", path = "crates/brk_server" }
brk_store = { version = "0.2.5", path = "crates/brk_store" }
brk_traversable = { version = "0.2.5", path = "crates/brk_traversable", features = ["pco", "derive"] }
brk_traversable_derive = { version = "0.2.5", path = "crates/brk_traversable_derive" }
brk_types = { version = "0.2.5", path = "crates/brk_types" }
brk_website = { version = "0.2.5", 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"] }
@@ -75,7 +75,7 @@ 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.0", features = ["derive", "serde_json", "pco", "schemars"] }
# vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] }
[workspace.metadata.release]
+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
@@ -726,7 +726,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();
}
@@ -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
@@ -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();
+1 -1
View File
@@ -26,7 +26,7 @@ owo-colors = { workspace = true }
tracing = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
toml = "1.0.7"
toml = "1.1.0"
vecdb = { workspace = true }
[[bin]]
+5011 -1265
View File
File diff suppressed because it is too large Load Diff
+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::*;
+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)]
+20 -4
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.
+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)]
+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(
@@ -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)?,
})
}
@@ -31,52 +31,49 @@ impl Vecs {
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.spot.usd.height,
&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.spot.usd.height,
&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.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(())
})?;
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);
}
}
@@ -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 {
(
@@ -274,12 +280,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 +306,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 +399,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 +470,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 +488,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 +552,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};
@@ -136,11 +134,8 @@ 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,
@@ -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.spot.cents.height, exit)?;
self.relative.compute(
starting_indexes.height,
@@ -87,16 +87,12 @@ impl BasicCohortMetrics {
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)?;
@@ -144,14 +145,11 @@ impl CoreCohortMetrics {
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(())
}
@@ -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(())
}
@@ -130,7 +129,8 @@ impl MinimalCohortMetrics {
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(())
}
@@ -92,7 +91,8 @@ impl TypeCohortMetrics {
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,
};
@@ -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,
)?;
@@ -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)?,
})
}
@@ -21,8 +21,7 @@ pub struct RelativeToAll<M: StorageMode = Rw> {
impl RelativeToAll {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
Ok(Self {
supply_to_circulating: cfg
.import("supply_to_circulating", Version::ONE)?,
supply_to_circulating: cfg.import("supply_to_circulating", Version::ONE)?,
supply_in_profit_to_circulating: cfg
.import("supply_in_profit_to_circulating", Version::ONE)?,
supply_in_loss_to_circulating: cfg
@@ -6,7 +6,7 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use crate::distribution::metrics::{ImportConfig, SupplyCore, UnrealizedFull};
use super::{RelativeFull, RelativeExtendedOwnMarketCap, RelativeExtendedOwnPnl, RelativeToAll};
use super::{RelativeExtendedOwnMarketCap, RelativeExtendedOwnPnl, RelativeFull, RelativeToAll};
/// Full extended relative metrics (base + rel_to_all + own_market_cap + own_pnl).
/// Used by: sth, lth cohorts.
@@ -45,23 +45,18 @@ impl RelativeWithExtended {
own_market_cap: &impl ReadableVec<Height, Dollars>,
exit: &Exit,
) -> Result<()> {
self.base.compute(
max_from,
supply,
&unrealized.inner.basic,
market_cap,
exit,
)?;
self.rel_to_all.compute(
max_from,
supply,
all_supply_sats,
exit,
)?;
self.base
.compute(max_from, supply, &unrealized.inner.basic, market_cap, exit)?;
self.rel_to_all
.compute(max_from, supply, all_supply_sats, exit)?;
self.extended_own_market_cap
.compute(max_from, &unrealized.inner, own_market_cap, exit)?;
self.extended_own_pnl
.compute(max_from, &unrealized.inner, &unrealized.gross_pnl.usd.height, exit)?;
self.extended_own_pnl.compute(
max_from,
&unrealized.inner,
&unrealized.gross_pnl.usd.height,
exit,
)?;
Ok(())
}
}
@@ -3,12 +3,13 @@ use brk_traversable::Traversable;
use brk_types::{BasisPointsSigned32, Height, Indexes, Sats, SatsSigned, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
use crate::{distribution::state::{CohortState, CostBasisOps, RealizedOps}, prices};
use crate::internal::{
AmountPerBlock, LazyRollingDeltasFromHeight,
use crate::{
distribution::state::{CohortState, CostBasisOps, RealizedOps},
prices,
};
use crate::internal::{AmountPerBlock, LazyRollingDeltasFromHeight};
use crate::distribution::metrics::ImportConfig;
/// Base supply metrics: total supply only (2 stored vecs).
@@ -1,5 +1,5 @@
mod base;
mod core;
pub use base::SupplyBase;
pub use self::core::SupplyCore;
pub use base::SupplyBase;
@@ -54,14 +54,8 @@ impl UnrealizedBasic {
#[inline(always)]
pub(crate) fn push_state(&mut self, state: &UnrealizedState) {
self.profit
.cents
.height
.push(state.unrealized_profit);
self.loss
.cents
.height
.push(state.unrealized_loss);
self.profit.cents.height.push(state.unrealized_profit);
self.loss.cents.height.push(state.unrealized_loss);
}
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
@@ -5,10 +5,7 @@ use derive_more::{Deref, DerefMut};
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
use crate::{
distribution::{
metrics::ImportConfig,
state::UnrealizedState,
},
distribution::{metrics::ImportConfig, state::UnrealizedState},
internal::{CentsSubtractToCentsSigned, FiatPerBlock},
};
@@ -60,11 +57,7 @@ impl UnrealizedCore {
Ok(())
}
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<()> {
self.net_pnl
.cents
.height
@@ -77,6 +77,12 @@ impl UnrealizedLike for UnrealizedFull {
supply_in_loss_sats: &(impl ReadableVec<Height, Sats> + Sync),
exit: &Exit,
) -> Result<()> {
self.compute_rest_all(prices, starting_indexes, supply_in_profit_sats, supply_in_loss_sats, exit)
self.compute_rest_all(
prices,
starting_indexes,
supply_in_profit_sats,
supply_in_loss_sats,
exit,
)
}
}
@@ -1,9 +1,15 @@
use std::path::Path;
use brk_error::Result;
use brk_types::{Age, Cents, CentsCompact, CentsSats, CentsSquaredSats, CostBasisSnapshot, Height, Sats, SupplyState};
use brk_types::{
Age, Cents, CentsCompact, CentsSats, CentsSquaredSats, CostBasisSnapshot, Height, Sats,
SupplyState,
};
use super::super::{cost_basis::{Accumulate, CostBasisData, CostBasisOps, RealizedOps, UnrealizedState}, pending::PendingDelta};
use super::super::{
cost_basis::{Accumulate, CostBasisData, CostBasisOps, RealizedOps, UnrealizedState},
pending::PendingDelta,
};
pub struct SendPrecomputed {
pub sats: Sats,
@@ -193,11 +199,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
}
}
pub(crate) fn send_utxo_precomputed(
&mut self,
supply: &SupplyState,
pre: &SendPrecomputed,
) {
pub(crate) fn send_utxo_precomputed(&mut self, supply: &SupplyState, pre: &SendPrecomputed) {
self.supply -= supply;
self.sent += pre.sats;
self.spent_utxo_count += supply.utxo_count;
@@ -205,8 +207,13 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
self.satdays_destroyed += pre.age.satdays_destroyed(pre.sats);
}
self.realized
.send(pre.sats, pre.current_ps, pre.prev_ps, pre.ath_ps, pre.prev_investor_cap);
self.realized.send(
pre.sats,
pre.current_ps,
pre.prev_ps,
pre.ath_ps,
pre.prev_investor_cap,
);
self.cost_basis
.decrement(pre.prev_price, pre.sats, pre.prev_ps, pre.prev_investor_cap);
@@ -370,7 +370,11 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
let (base, rest) = CostBasisDistribution::deserialize_with_rest(&data)?;
self.map = Some(base);
self.raw.state = Some(RawState::deserialize(rest)?);
debug_assert!(rest.len() >= 32, "CostBasisData state too short: {} bytes", rest.len());
debug_assert!(
rest.len() >= 32,
"CostBasisData state too short: {} bytes",
rest.len()
);
self.investor_cap_raw = CentsSquaredSats::from_bytes(&rest[16..32])?;
self.pending.clear();
self.raw.pending_cap = PendingCapDelta::default();
@@ -435,7 +439,9 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
Path: {:?}\n\
Current (after increments): {:?}\n\
Trying to decrement by: {:?}",
self.raw.pathbuf, self.investor_cap_raw, self.pending_investor_cap.dec
self.raw.pathbuf,
self.investor_cap_raw,
self.pending_investor_cap.dec
);
self.investor_cap_raw -= self.pending_investor_cap.dec;
self.pending_investor_cap = PendingInvestorCapDelta::default();
@@ -6,7 +6,7 @@ pub use data::*;
pub use realized::*;
pub use unrealized::UnrealizedState;
pub(crate) use unrealized::{Accumulate, WithoutCapital, WithCapital};
pub(crate) use unrealized::{Accumulate, WithCapital, WithoutCapital};
// Internal use only
pub(super) use unrealized::CachedUnrealizedState;
@@ -58,10 +58,18 @@ pub trait Accumulate: Default + Clone + Send + Sync + 'static {
fn core(&self) -> &WithoutCapital;
fn core_mut(&mut self) -> &mut WithoutCapital;
fn supply_in_profit(&self) -> Sats { self.core().supply_in_profit }
fn supply_in_loss(&self) -> Sats { self.core().supply_in_loss }
fn unrealized_profit(&mut self) -> &mut u128 { &mut self.core_mut().unrealized_profit }
fn unrealized_loss(&mut self) -> &mut u128 { &mut self.core_mut().unrealized_loss }
fn supply_in_profit(&self) -> Sats {
self.core().supply_in_profit
}
fn supply_in_loss(&self) -> Sats {
self.core().supply_in_loss
}
fn unrealized_profit(&mut self) -> &mut u128 {
&mut self.core_mut().unrealized_profit
}
fn unrealized_loss(&mut self) -> &mut u128 {
&mut self.core_mut().unrealized_loss
}
fn accumulate_profit(&mut self, price_u128: u128, sats: Sats);
fn accumulate_loss(&mut self, price_u128: u128, sats: Sats);
@@ -80,8 +88,12 @@ impl Accumulate for WithoutCapital {
}
}
fn core(&self) -> &WithoutCapital { self }
fn core_mut(&mut self) -> &mut WithoutCapital { self }
fn core(&self) -> &WithoutCapital {
self
}
fn core_mut(&mut self) -> &mut WithoutCapital {
self
}
#[inline(always)]
fn accumulate_profit(&mut self, _price_u128: u128, sats: Sats) {
@@ -110,8 +122,12 @@ impl Accumulate for WithCapital {
}
}
fn core(&self) -> &WithoutCapital { &self.core }
fn core_mut(&mut self) -> &mut WithoutCapital { &mut self.core }
fn core(&self) -> &WithoutCapital {
&self.core
}
fn core_mut(&mut self) -> &mut WithoutCapital {
&mut self.core
}
#[inline(always)]
fn accumulate_profit(&mut self, price_u128: u128, sats: Sats) {
+11 -6
View File
@@ -227,6 +227,8 @@ impl Vecs {
starting_indexes: &mut Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
// 1. Find minimum height we have data for across stateful vecs
let current_height = Height::from(self.supply_state.len());
let min_stateful = self.min_stateful_len();
@@ -300,7 +302,7 @@ impl Vecs {
.cents
.height
.len()
.min(blocks.time.timestamp_monotonic.len());
.min(indexes.timestamp.monotonic.len());
let cache_current_len = self.caches.prices.len();
if cache_target_len < cache_current_len {
self.caches.prices.truncate(cache_target_len);
@@ -312,9 +314,9 @@ impl Vecs {
.cents
.height
.collect_range_at(cache_current_len, cache_target_len);
let new_timestamps = blocks
.time
.timestamp_monotonic
let new_timestamps = indexes
.timestamp
.monotonic
.collect_range_at(cache_current_len, cache_target_len);
self.caches.prices.extend(new_prices);
self.caches.timestamps.extend(new_timestamps);
@@ -499,8 +501,11 @@ impl Vecs {
self.addr_cohorts
.compute_rest_part2(prices, starting_indexes, &all_utxo_count, exit)?;
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(())
}
+19 -15
View File
@@ -1,11 +1,10 @@
use brk_indexer::Indexer;
use brk_traversable::Traversable;
use brk_types::{
Addr, AddrBytes, EmptyOutputIndex, OpReturnIndex, P2AAddrIndex, P2ABytes,
P2MSOutputIndex, P2PK33AddrIndex, P2PK33Bytes, P2PK65AddrIndex, P2PK65Bytes,
P2PKHAddrIndex, P2PKHBytes, P2SHAddrIndex, P2SHBytes, P2TRAddrIndex, P2TRBytes,
P2WPKHAddrIndex, P2WPKHBytes, P2WSHAddrIndex, P2WSHBytes, TxIndex, UnknownOutputIndex,
Version,
Addr, AddrBytes, EmptyOutputIndex, OpReturnIndex, P2AAddrIndex, P2ABytes, P2MSOutputIndex,
P2PK33AddrIndex, P2PK33Bytes, P2PK65AddrIndex, P2PK65Bytes, P2PKHAddrIndex, P2PKHBytes,
P2SHAddrIndex, P2SHBytes, P2TRAddrIndex, P2TRBytes, P2WPKHAddrIndex, P2WPKHBytes,
P2WSHAddrIndex, P2WSHBytes, TxIndex, UnknownOutputIndex, Version,
};
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
@@ -27,15 +26,13 @@ pub struct Vecs {
#[derive(Clone, Traversable)]
pub struct P2PK33Vecs {
pub identity:
LazyVecFrom1<P2PK33AddrIndex, P2PK33AddrIndex, P2PK33AddrIndex, P2PK33Bytes>,
pub identity: LazyVecFrom1<P2PK33AddrIndex, P2PK33AddrIndex, P2PK33AddrIndex, P2PK33Bytes>,
pub addr: LazyVecFrom1<P2PK33AddrIndex, Addr, P2PK33AddrIndex, P2PK33Bytes>,
}
#[derive(Clone, Traversable)]
pub struct P2PK65Vecs {
pub identity:
LazyVecFrom1<P2PK65AddrIndex, P2PK65AddrIndex, P2PK65AddrIndex, P2PK65Bytes>,
pub identity: LazyVecFrom1<P2PK65AddrIndex, P2PK65AddrIndex, P2PK65AddrIndex, P2PK65Bytes>,
pub addr: LazyVecFrom1<P2PK65AddrIndex, Addr, P2PK65AddrIndex, P2PK65Bytes>,
}
@@ -59,8 +56,7 @@ pub struct P2TRVecs {
#[derive(Clone, Traversable)]
pub struct P2WPKHVecs {
pub identity:
LazyVecFrom1<P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHBytes>,
pub identity: LazyVecFrom1<P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHBytes>,
pub addr: LazyVecFrom1<P2WPKHAddrIndex, Addr, P2WPKHAddrIndex, P2WPKHBytes>,
}
@@ -215,7 +211,12 @@ impl Vecs {
identity: LazyVecFrom1::init(
"p2ms_output_index",
version,
indexer.vecs.scripts.p2ms.to_tx_index.read_only_boxed_clone(),
indexer
.vecs
.scripts
.p2ms
.to_tx_index
.read_only_boxed_clone(),
|index, _| index,
),
},
@@ -226,7 +227,8 @@ impl Vecs {
indexer
.vecs
.scripts
.empty.to_tx_index
.empty
.to_tx_index
.read_only_boxed_clone(),
|index, _| index,
),
@@ -238,7 +240,8 @@ impl Vecs {
indexer
.vecs
.scripts
.unknown.to_tx_index
.unknown
.to_tx_index
.read_only_boxed_clone(),
|index, _| index,
),
@@ -250,7 +253,8 @@ impl Vecs {
indexer
.vecs
.scripts
.op_return.to_tx_index
.op_return
.to_tx_index
.read_only_boxed_clone(),
|index, _| index,
),
+2 -2
View File
@@ -1,7 +1,7 @@
use brk_traversable::Traversable;
use brk_types::{
Day1, Day3, Epoch, Halving, Height, Hour1, Hour4, Hour12, Minute10, Minute30,
Month1, Month3, Month6, StoredU64, Version, Week1, Year1, Year10,
Day1, Day3, Epoch, Halving, Height, Hour1, Hour4, Hour12, Minute10, Minute30, Month1, Month3,
Month6, StoredU64, Version, Week1, Year1, Year10,
};
use vecdb::{Database, EagerVec, ImportableVec, PcoVec, Rw, StorageMode};

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