mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: MASSIVE snapshot
This commit is contained in:
232
Cargo.lock
generated
232
Cargo.lock
generated
@@ -86,9 +86,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.101"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
@@ -98,9 +98,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.4.39"
|
||||
version = "0.4.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68650b7df54f0293fd061972a0fb05aaf4fc0879d3b3d21a638a182c5c543b9f"
|
||||
checksum = "7d67d43201f4d20c78bcda740c142ca52482d81da80681533d33bf3f0596c8e2"
|
||||
dependencies = [
|
||||
"compression-codecs",
|
||||
"compression-core",
|
||||
@@ -203,6 +203,18 @@ version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bech32"
|
||||
version = "0.11.1"
|
||||
@@ -221,7 +233,7 @@ version = "0.72.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools",
|
||||
@@ -240,6 +252,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e499f9fc0407f50fe98af744ab44fa67d409f76b6772e1689ec8485eb0c0f66"
|
||||
dependencies = [
|
||||
"base58ck",
|
||||
"base64 0.21.7",
|
||||
"bech32",
|
||||
"bitcoin-internals",
|
||||
"bitcoin-io",
|
||||
@@ -294,7 +307,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee"
|
||||
dependencies = [
|
||||
"bitcoincore-rpc-json",
|
||||
"jsonrpc",
|
||||
"jsonrpc 0.18.0",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -319,9 +332,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
@@ -403,7 +416,6 @@ dependencies = [
|
||||
"brk_alloc",
|
||||
"brk_computer",
|
||||
"brk_error",
|
||||
"brk_fetcher",
|
||||
"brk_indexer",
|
||||
"brk_iterator",
|
||||
"brk_logger",
|
||||
@@ -454,7 +466,6 @@ dependencies = [
|
||||
"brk_bencher",
|
||||
"brk_cohort",
|
||||
"brk_error",
|
||||
"brk_fetcher",
|
||||
"brk_indexer",
|
||||
"brk_iterator",
|
||||
"brk_logger",
|
||||
@@ -471,6 +482,7 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"tracing",
|
||||
"vecdb",
|
||||
@@ -482,6 +494,7 @@ version = "0.1.9"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"bitcoincore-rpc",
|
||||
"corepc-client",
|
||||
"fjall",
|
||||
"jiff",
|
||||
"minreq",
|
||||
@@ -619,7 +632,11 @@ dependencies = [
|
||||
"brk_error",
|
||||
"brk_logger",
|
||||
"brk_types",
|
||||
"corepc-client",
|
||||
"jsonrpc 0.19.0",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@@ -632,7 +649,6 @@ dependencies = [
|
||||
"brk_bindgen",
|
||||
"brk_computer",
|
||||
"brk_error",
|
||||
"brk_fetcher",
|
||||
"brk_indexer",
|
||||
"brk_logger",
|
||||
"brk_mempool",
|
||||
@@ -749,9 +765,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.1"
|
||||
version = "3.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
@@ -881,9 +897,9 @@ checksum = "ea0095f6103c2a8b44acd6fd15960c801dafebf02e21940360833e0673f48ba7"
|
||||
|
||||
[[package]]
|
||||
name = "compression-codecs"
|
||||
version = "0.4.36"
|
||||
version = "0.4.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a"
|
||||
checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"compression-core",
|
||||
@@ -960,6 +976,27 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "corepc-client"
|
||||
version = "0.11.0"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"corepc-types",
|
||||
"jsonrpc 0.19.0",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "corepc-types"
|
||||
version = "0.11.0"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
@@ -1122,7 +1159,7 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.0",
|
||||
"block2",
|
||||
"libc",
|
||||
"objc2",
|
||||
@@ -1239,9 +1276,9 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "fjall"
|
||||
version = "3.0.1"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f69637c02d38ad1b0f003101d0195a60368130aa17d9ef78b1557d265a22093"
|
||||
checksum = "5a2799b4198427a08c774838e44d0b77f677208f19a1927671cd2cd36bb30d69"
|
||||
dependencies = [
|
||||
"byteorder-lite",
|
||||
"byteview",
|
||||
@@ -1291,7 +1328,7 @@ version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c7e611d49285d4c4b2e1727b72cf05353558885cc5252f93707b845dfcaf3d3"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.0",
|
||||
"byteorder",
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
@@ -1359,9 +1396,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1374,9 +1411,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -1384,15 +1421,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -1401,15 +1438,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1418,21 +1455,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1442,7 +1479,6 @@ dependencies = [
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
@@ -1786,9 +1822,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "importmap"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "689654b51e7a463f6d1009c18e784a43d287d82296b67f27411f01c38ab5adb7"
|
||||
checksum = "136c4b5b2e42ffb57d7b99c2104350ae3ee5a2c555fe48ca04534ded7e599060"
|
||||
dependencies = [
|
||||
"include_dir",
|
||||
"rapidhash",
|
||||
@@ -1858,9 +1894,9 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.20"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c867c356cc096b33f4981825ab281ecba3db0acefe60329f044c1789d94c6543"
|
||||
checksum = "b3e3d65f018c6ae946ab16e80944b97096ed73c35b221d1c478a6c81d8f57940"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
@@ -1872,9 +1908,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.20"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7946b4325269738f270bb55b3c19ab5c5040525f83fd625259422a9d25d9be5"
|
||||
checksum = "a17c2b211d863c7fde02cbea8a3c1a439b98e109286554f2860bdded7ff83818"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1899,9 +1935,9 @@ checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.85"
|
||||
version = "0.3.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
|
||||
checksum = "c7e709f3e3d22866f9c25b3aff01af289b18422cc8b4262fb19103ee80fe513d"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
@@ -1913,12 +1949,21 @@ version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.13.1",
|
||||
"minreq",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@@ -1981,7 +2026,7 @@ version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -2014,9 +2059,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "lsm-tree"
|
||||
version = "3.0.1"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b875f1dfe14f557f805b167fb9b0fc54c5560c7a4bd6ae02535b2846f276a8cb"
|
||||
checksum = "86e8d0b8e0cf2531a437788ce94d95570dbaabfe9888db20022c2d5ccec9b221"
|
||||
dependencies = [
|
||||
"byteorder-lite",
|
||||
"byteview",
|
||||
@@ -2049,15 +2094,6 @@ version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab6473172471198271ff72e9379150e9dfd70d8e533e0752a27e515b48dd375e"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.8.4"
|
||||
@@ -2072,9 +2108,9 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.9"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490"
|
||||
checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -2140,7 +2176,7 @@ version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225e7cfe711e0ba79a68baeddb2982723e4235247aefce1482f2f16c27865b66"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
@@ -2220,9 +2256,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "4.2.3"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52"
|
||||
checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
@@ -2490,9 +2526,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rapidhash"
|
||||
version = "4.3.0"
|
||||
version = "4.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84816e4c99c467e92cf984ee6328caa976dfecd33a673544489d79ca2caaefe5"
|
||||
checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
@@ -2500,8 +2536,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rawdb"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a66c17743b9a7e6a3bb8edb10fef25c62516e281b723ea38d7c1feea2035c75d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
@@ -2538,7 +2572,7 @@ version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2651,7 +2685,7 @@ version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
@@ -2957,18 +2991,18 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.27.2"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
|
||||
checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.27.2"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
|
||||
checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -2978,9 +3012,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.115"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3096,9 +3130,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.0.1+spec-1.1.0"
|
||||
version = "1.0.3+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe30f93627849fa362d4a602212d41bb237dc2bd0f8ba0b2ce785012e124220"
|
||||
checksum = "c7614eaf19ad818347db24addfa201729cf2a9b6fdfd9eb0ab870fcacc606c0c"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
@@ -3120,9 +3154,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.8+spec-1.1.0"
|
||||
version = "1.0.9+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0742ff5ff03ea7e67c8ae6c93cac239e0d9784833362da3f9a9c1da8dfefcbdc"
|
||||
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
@@ -3155,7 +3189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.0",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
@@ -3241,12 +3275,8 @@ version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"once_cell",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
@@ -3264,9 +3294,9 @@ checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.23"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
@@ -3320,8 +3350,6 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23"
|
||||
[[package]]
|
||||
name = "vecdb"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16459a73939ec1c7ddb5c2f4264916f7bb96c88287b15dcce29cd95c16d2f6c0"
|
||||
dependencies = [
|
||||
"ctrlc",
|
||||
"log",
|
||||
@@ -3341,8 +3369,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vecdb_derive"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1845265e89f36a22175ebef07dc1340050ef3ec54aa9f9c84859d4dda0a3a03"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -3390,9 +3416,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.108"
|
||||
version = "0.2.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
|
||||
checksum = "ec1adf1535672f5b7824f817792b1afd731d7e843d2d04ec8f27e8cb51edd8ac"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -3403,9 +3429,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.108"
|
||||
version = "0.2.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
|
||||
checksum = "19e638317c08b21663aed4d2b9a2091450548954695ff4efa75bff5fa546b3b1"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -3413,9 +3439,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.108"
|
||||
version = "0.2.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
|
||||
checksum = "2c64760850114d03d5f65457e96fc988f11f01d38fbaa51b254e4ab5809102af"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
@@ -3426,9 +3452,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.108"
|
||||
version = "0.2.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
|
||||
checksum = "60eecd4fe26177cfa3339eb00b4a36445889ba3ad37080c2429879718e20ca41"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -3461,7 +3487,7 @@ version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.0",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
@@ -3469,9 +3495,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.85"
|
||||
version = "0.3.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
|
||||
checksum = "9d6bb20ed2d9572df8584f6dc81d68a41a625cadc6f15999d649a70ce7e3597a"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -3808,7 +3834,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.0",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
|
||||
12
Cargo.toml
12
Cargo.toml
@@ -40,6 +40,8 @@ aide = { version = "0.16.0-alpha.2", 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"
|
||||
corepc-client = { path = "/Users/k/Developer/corepc/client", features = ["client-sync"] }
|
||||
corepc-jsonrpc = { package = "jsonrpc", path = "/Users/k/Developer/corepc/jsonrpc", features = ["simple_http"], default-features = false }
|
||||
brk_alloc = { version = "0.1.9", path = "crates/brk_alloc" }
|
||||
brk_bencher = { version = "0.1.9", path = "crates/brk_bencher" }
|
||||
brk_bindgen = { version = "0.1.9", path = "crates/brk_bindgen" }
|
||||
@@ -66,11 +68,11 @@ brk_website = { version = "0.1.9", path = "crates/brk_website" }
|
||||
byteview = "0.10.1"
|
||||
color-eyre = "0.6.5"
|
||||
derive_more = { version = "2.1.1", features = ["deref", "deref_mut"] }
|
||||
fjall = "3.0.1"
|
||||
fjall = "3.0.2"
|
||||
indexmap = { version = "2.13.0", features = ["serde"] }
|
||||
jiff = { version = "0.2.20", features = ["perf-inline", "tz-system"], default-features = false }
|
||||
jiff = { version = "0.2.21", features = ["perf-inline", "tz-system"], default-features = false }
|
||||
minreq = { version = "2.14.1", features = ["https", "json-using-serde"] }
|
||||
owo-colors = "4.2.3"
|
||||
owo-colors = "4.3.0"
|
||||
parking_lot = "0.12.5"
|
||||
pco = "1.0.1"
|
||||
rayon = "1.11.0"
|
||||
@@ -85,8 +87,8 @@ tokio = { version = "1.49.0", features = ["rt-multi-thread"] }
|
||||
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||
tower-http = { version = "0.6.8", features = ["catch-panic", "compression-br", "compression-gzip", "compression-zstd", "cors", "normalize-path", "timeout", "trace"] }
|
||||
tower-layer = "0.3"
|
||||
vecdb = { version = "0.6.8", features = ["derive", "serde_json", "pco", "schemars"] }
|
||||
# vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] }
|
||||
# vecdb = { version = "0.6.8", features = ["derive", "serde_json", "pco", "schemars"] }
|
||||
vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] }
|
||||
|
||||
[workspace.metadata.release]
|
||||
shared-version = true
|
||||
|
||||
@@ -64,7 +64,7 @@ brk_mempool = { workspace = true, optional = true }
|
||||
brk_oracle = { workspace = true, optional = true }
|
||||
brk_query = { workspace = true, optional = true }
|
||||
brk_reader = { workspace = true, optional = true }
|
||||
brk_rpc = { workspace = true, optional = true }
|
||||
brk_rpc = { workspace = true, optional = true, features = ["corepc"] }
|
||||
brk_server = { workspace = true, optional = true }
|
||||
brk_store = { workspace = true, optional = true }
|
||||
brk_traversable = { workspace = true, optional = true }
|
||||
|
||||
@@ -18,7 +18,12 @@ use brk::query::Query;
|
||||
use brk::types::Height;
|
||||
```
|
||||
|
||||
Feature flags match crate names without the `brk_` prefix. Use `full` to enable all.
|
||||
Feature flags match crate names without the `brk_` prefix. Use `full` to enable all:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
brk = { version = "0.1", features = ["full"] }
|
||||
```
|
||||
|
||||
## Crates
|
||||
|
||||
|
||||
@@ -371,15 +371,15 @@ mod tests {
|
||||
let tree = make_branch(vec![
|
||||
(
|
||||
"base",
|
||||
make_branch(vec![("dateindex", make_leaf("block_vbytes"))]),
|
||||
make_branch(vec![("day1", make_leaf("block_vbytes"))]),
|
||||
),
|
||||
(
|
||||
"average",
|
||||
make_branch(vec![("dateindex", make_leaf("block_vbytes_average"))]),
|
||||
make_branch(vec![("day1", make_leaf("block_vbytes_average"))]),
|
||||
),
|
||||
(
|
||||
"sum",
|
||||
make_branch(vec![("dateindex", make_leaf("block_vbytes_sum"))]),
|
||||
make_branch(vec![("day1", make_leaf("block_vbytes_sum"))]),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -394,23 +394,23 @@ mod tests {
|
||||
let tree = make_branch(vec![
|
||||
(
|
||||
"average",
|
||||
make_branch(vec![("dateindex", make_leaf("block_weight_average"))]),
|
||||
make_branch(vec![("day1", make_leaf("block_weight_average"))]),
|
||||
),
|
||||
(
|
||||
"sum",
|
||||
make_branch(vec![("dateindex", make_leaf("block_weight_sum"))]),
|
||||
make_branch(vec![("day1", make_leaf("block_weight_sum"))]),
|
||||
),
|
||||
(
|
||||
"cumulative",
|
||||
make_branch(vec![("dateindex", make_leaf("block_weight_cumulative"))]),
|
||||
make_branch(vec![("day1", make_leaf("block_weight_cumulative"))]),
|
||||
),
|
||||
(
|
||||
"max",
|
||||
make_branch(vec![("dateindex", make_leaf("block_weight_max"))]),
|
||||
make_branch(vec![("day1", make_leaf("block_weight_max"))]),
|
||||
),
|
||||
(
|
||||
"min",
|
||||
make_branch(vec![("dateindex", make_leaf("block_weight_min"))]),
|
||||
make_branch(vec![("day1", make_leaf("block_weight_min"))]),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -426,15 +426,15 @@ mod tests {
|
||||
let tree = make_branch(vec![
|
||||
(
|
||||
"base",
|
||||
make_branch(vec![("dateindex", make_leaf("block_weight_average"))]),
|
||||
make_branch(vec![("day1", make_leaf("block_weight_average"))]),
|
||||
),
|
||||
(
|
||||
"average",
|
||||
make_branch(vec![("dateindex", make_leaf("block_weight_average"))]),
|
||||
make_branch(vec![("day1", make_leaf("block_weight_average"))]),
|
||||
),
|
||||
(
|
||||
"sum",
|
||||
make_branch(vec![("dateindex", make_leaf("block_weight_sum"))]),
|
||||
make_branch(vec![("day1", make_leaf("block_weight_sum"))]),
|
||||
),
|
||||
]);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ impl ClientConstants {
|
||||
/// Collect all constant data.
|
||||
pub fn collect() -> Self {
|
||||
let indexes = Index::all();
|
||||
let indexes: Vec<&'static str> = indexes.iter().map(|i| i.serialize_long()).collect();
|
||||
let indexes: Vec<&'static str> = indexes.iter().map(|i| i.name()).collect();
|
||||
|
||||
let pools = pools();
|
||||
let mut sorted_pools: Vec<_> = pools.iter().collect();
|
||||
|
||||
@@ -49,11 +49,18 @@ class BrkError extends Error {{
|
||||
}}
|
||||
|
||||
// Date conversion constants and helpers
|
||||
const _GENESIS = new Date(2009, 0, 3); // dateindex 0, weekindex 0
|
||||
const _DAY_ONE = new Date(2009, 0, 9); // dateindex 1 (6 day gap after genesis)
|
||||
const _MS_PER_DAY = 24 * 60 * 60 * 1000;
|
||||
const _GENESIS = new Date(2009, 0, 3); // day1 0, week1 0
|
||||
const _DAY_ONE = new Date(2009, 0, 9); // day1 1 (6 day gap after genesis)
|
||||
const _MS_PER_DAY = 86400000;
|
||||
const _MS_PER_WEEK = 7 * _MS_PER_DAY;
|
||||
const _DATE_INDEXES = new Set(['dateindex', 'weekindex', 'monthindex', 'yearindex', 'quarterindex', 'semesterindex', 'decadeindex']);
|
||||
const _EPOCH_MS = 1230768000000;
|
||||
const _DATE_INDEXES = new Set([
|
||||
'minute1', 'minute5', 'minute10', 'minute30',
|
||||
'hour1', 'hour4', 'hour12',
|
||||
'day1', 'day3', 'week1',
|
||||
'month1', 'month3', 'month6',
|
||||
'year1', 'year10',
|
||||
]);
|
||||
|
||||
/** @param {{number}} months @returns {{globalThis.Date}} */
|
||||
const _addMonths = (months) => new Date(2009, months, 1);
|
||||
@@ -66,24 +73,55 @@ const _addMonths = (months) => new Date(2009, months, 1);
|
||||
*/
|
||||
function indexToDate(index, i) {{
|
||||
switch (index) {{
|
||||
case 'dateindex': return i === 0 ? _GENESIS : new Date(_DAY_ONE.getTime() + (i - 1) * _MS_PER_DAY);
|
||||
case 'weekindex': return new Date(_GENESIS.getTime() + i * _MS_PER_WEEK);
|
||||
case 'monthindex': return _addMonths(i);
|
||||
case 'yearindex': return new Date(2009 + i, 0, 1);
|
||||
case 'quarterindex': return _addMonths(i * 3);
|
||||
case 'semesterindex': return _addMonths(i * 6);
|
||||
case 'decadeindex': return new Date(2009 + i * 10, 0, 1);
|
||||
case 'minute1': return new Date(_EPOCH_MS + i * 60000);
|
||||
case 'minute5': return new Date(_EPOCH_MS + i * 300000);
|
||||
case 'minute10': return new Date(_EPOCH_MS + i * 600000);
|
||||
case 'minute30': return new Date(_EPOCH_MS + i * 1800000);
|
||||
case 'hour1': return new Date(_EPOCH_MS + i * 3600000);
|
||||
case 'hour4': return new Date(_EPOCH_MS + i * 14400000);
|
||||
case 'hour12': return new Date(_EPOCH_MS + i * 43200000);
|
||||
case 'day1': return i === 0 ? _GENESIS : new Date(_DAY_ONE.getTime() + (i - 1) * _MS_PER_DAY);
|
||||
case 'day3': return new Date(_EPOCH_MS + i * 259200000);
|
||||
case 'week1': return new Date(_GENESIS.getTime() + i * _MS_PER_WEEK);
|
||||
case 'month1': return _addMonths(i);
|
||||
case 'month3': return _addMonths(i * 3);
|
||||
case 'month6': return _addMonths(i * 6);
|
||||
case 'year1': return new Date(2009 + i, 0, 1);
|
||||
case 'year10': return new Date(2009 + i * 10, 0, 1);
|
||||
default: throw new Error(`${{index}} is not a date-based index`);
|
||||
}}
|
||||
}}
|
||||
|
||||
/**
|
||||
* Check if an index type is date-based.
|
||||
* @param {{Index}} index
|
||||
* @returns {{boolean}}
|
||||
* Convert a Date to an index value for date-based indexes.
|
||||
* Returns the floor index (latest index whose date is <= the given date).
|
||||
* @param {{Index}} index - The index type
|
||||
* @param {{globalThis.Date}} d - The date to convert
|
||||
* @returns {{number}}
|
||||
*/
|
||||
function isDateIndex(index) {{
|
||||
return _DATE_INDEXES.has(index);
|
||||
function dateToIndex(index, d) {{
|
||||
const ms = d.getTime();
|
||||
switch (index) {{
|
||||
case 'minute1': return Math.floor((ms - _EPOCH_MS) / 60000);
|
||||
case 'minute5': return Math.floor((ms - _EPOCH_MS) / 300000);
|
||||
case 'minute10': return Math.floor((ms - _EPOCH_MS) / 600000);
|
||||
case 'minute30': return Math.floor((ms - _EPOCH_MS) / 1800000);
|
||||
case 'hour1': return Math.floor((ms - _EPOCH_MS) / 3600000);
|
||||
case 'hour4': return Math.floor((ms - _EPOCH_MS) / 14400000);
|
||||
case 'hour12': return Math.floor((ms - _EPOCH_MS) / 43200000);
|
||||
case 'day1': {{
|
||||
if (ms < _DAY_ONE.getTime()) return 0;
|
||||
return 1 + Math.floor((ms - _DAY_ONE.getTime()) / _MS_PER_DAY);
|
||||
}}
|
||||
case 'day3': return Math.floor((ms - _EPOCH_MS) / 259200000);
|
||||
case 'week1': return Math.floor((ms - _GENESIS.getTime()) / _MS_PER_WEEK);
|
||||
case 'month1': return (d.getFullYear() - 2009) * 12 + d.getMonth();
|
||||
case 'month3': return (d.getFullYear() - 2009) * 4 + Math.floor(d.getMonth() / 3);
|
||||
case 'month6': return (d.getFullYear() - 2009) * 2 + Math.floor(d.getMonth() / 6);
|
||||
case 'year1': return d.getFullYear() - 2009;
|
||||
case 'year10': return Math.floor((d.getFullYear() - 2009) / 10);
|
||||
default: throw new Error(`${{index}} is not a date-based index`);
|
||||
}}
|
||||
}}
|
||||
|
||||
/**
|
||||
@@ -94,8 +132,10 @@ function isDateIndex(index) {{
|
||||
*/
|
||||
function _wrapMetricData(raw) {{
|
||||
const {{ index, start, end, data }} = raw;
|
||||
const _dateBased = _DATE_INDEXES.has(index);
|
||||
return /** @type {{MetricData<T>}} */ ({{
|
||||
...raw,
|
||||
isDateBased: _dateBased,
|
||||
dates() {{
|
||||
/** @type {{globalThis.Date[]}} */
|
||||
const result = [];
|
||||
@@ -108,38 +148,35 @@ function _wrapMetricData(raw) {{
|
||||
for (let i = start; i < end; i++) result.push(i);
|
||||
return result;
|
||||
}},
|
||||
toDateMap() {{
|
||||
/** @type {{Map<globalThis.Date, T>}} */
|
||||
const map = new Map();
|
||||
for (let i = 0; i < data.length; i++) map.set(indexToDate(index, start + i), data[i]);
|
||||
return map;
|
||||
keys() {{
|
||||
return _dateBased ? this.dates() : this.indexes();
|
||||
}},
|
||||
toIndexMap() {{
|
||||
/** @type {{Map<number, T>}} */
|
||||
const map = new Map();
|
||||
for (let i = 0; i < data.length; i++) map.set(start + i, data[i]);
|
||||
return map;
|
||||
}},
|
||||
dateEntries() {{
|
||||
/** @type {{Array<[globalThis.Date, T]>}} */
|
||||
entries() {{
|
||||
/** @type {{Array<[globalThis.Date | number, T]>}} */
|
||||
const result = [];
|
||||
for (let i = 0; i < data.length; i++) result.push([indexToDate(index, start + i), data[i]]);
|
||||
if (_dateBased) {{
|
||||
for (let i = 0; i < data.length; i++) result.push([indexToDate(index, start + i), data[i]]);
|
||||
}} else {{
|
||||
for (let i = 0; i < data.length; i++) result.push([start + i, data[i]]);
|
||||
}}
|
||||
return result;
|
||||
}},
|
||||
indexEntries() {{
|
||||
/** @type {{Array<[number, T]>}} */
|
||||
const result = [];
|
||||
for (let i = 0; i < data.length; i++) result.push([start + i, data[i]]);
|
||||
return result;
|
||||
toMap() {{
|
||||
/** @type {{Map<globalThis.Date | number, T>}} */
|
||||
const map = new Map();
|
||||
if (_dateBased) {{
|
||||
for (let i = 0; i < data.length; i++) map.set(indexToDate(index, start + i), data[i]);
|
||||
}} else {{
|
||||
for (let i = 0; i < data.length; i++) map.set(start + i, data[i]);
|
||||
}}
|
||||
return map;
|
||||
}},
|
||||
*iter() {{
|
||||
for (let i = 0; i < data.length; i++) yield [start + i, data[i]];
|
||||
}},
|
||||
*iterDates() {{
|
||||
for (let i = 0; i < data.length; i++) yield [indexToDate(index, start + i), data[i]];
|
||||
}},
|
||||
[Symbol.iterator]() {{
|
||||
return this.iter();
|
||||
*[Symbol.iterator]() {{
|
||||
if (_dateBased) {{
|
||||
for (let i = 0; i < data.length; i++) yield [indexToDate(index, start + i), data[i]];
|
||||
}} else {{
|
||||
for (let i = 0; i < data.length; i++) yield [start + i, data[i]];
|
||||
}}
|
||||
}},
|
||||
}});
|
||||
}}
|
||||
@@ -154,14 +191,12 @@ function _wrapMetricData(raw) {{
|
||||
* @property {{number}} end - End index (exclusive)
|
||||
* @property {{string}} stamp - ISO 8601 timestamp of when the response was generated
|
||||
* @property {{T[]}} data - The metric data
|
||||
* @property {{() => globalThis.Date[]}} dates - Convert index range to dates (date-based indexes only)
|
||||
* @property {{() => number[]}} indexes - Get index range as array
|
||||
* @property {{() => Map<globalThis.Date, T>}} toDateMap - Return data as Map keyed by date (date-based only)
|
||||
* @property {{() => Map<number, T>}} toIndexMap - Return data as Map keyed by index
|
||||
* @property {{() => Array<[globalThis.Date, T]>}} dateEntries - Return data as [date, value] pairs (date-based only)
|
||||
* @property {{() => Array<[number, T]>}} indexEntries - Return data as [index, value] pairs
|
||||
* @property {{() => IterableIterator<[number, T]>}} iter - Iterate over [index, value] pairs
|
||||
* @property {{() => IterableIterator<[globalThis.Date, T]>}} iterDates - Iterate over [date, value] pairs (date-based only)
|
||||
* @property {{boolean}} isDateBased - Whether this metric uses a date-based index
|
||||
* @property {{() => (globalThis.Date[] | number[])}} keys - Get keys (dates for date-based, index numbers otherwise)
|
||||
* @property {{() => Array<[globalThis.Date | number, T]>}} entries - Get [key, value] pairs (dates for date-based, index numbers otherwise)
|
||||
* @property {{() => Map<globalThis.Date | number, T>}} toMap - Return data as Map (dates for date-based, index numbers otherwise)
|
||||
* @property {{() => globalThis.Date[]}} dates - Get dates (date-based indexes only, throws otherwise)
|
||||
* @property {{() => number[]}} indexes - Get index numbers
|
||||
*/
|
||||
/** @typedef {{MetricData<any>}} AnyMetricData */
|
||||
|
||||
@@ -172,11 +207,11 @@ function _wrapMetricData(raw) {{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Metric endpoint builder. Callable (returns itself) so both .by.dateindex and .by.dateindex() work.
|
||||
* Metric endpoint builder. Callable (returns itself) so both .by.day1 and .by.day1() work.
|
||||
* @template T
|
||||
* @typedef {{Object}} MetricEndpointBuilder
|
||||
* @property {{(index: number) => SingleItemBuilder<T>}} get - Get single item at index
|
||||
* @property {{(start?: number, end?: number) => RangeBuilder<T>}} slice - Slice like Array.slice
|
||||
* @property {{(start?: number | globalThis.Date, end?: number | globalThis.Date) => RangeBuilder<T>}} slice - Slice by index or Date
|
||||
* @property {{(n: number) => RangeBuilder<T>}} first - Get first n items
|
||||
* @property {{(n: number) => RangeBuilder<T>}} last - Get last n items
|
||||
* @property {{(n: number) => SkippedBuilder<T>}} skip - Skip first n items, chain with take()
|
||||
@@ -216,7 +251,7 @@ function _wrapMetricData(raw) {{
|
||||
* @template T
|
||||
* @typedef {{Object}} MetricPattern
|
||||
* @property {{string}} name - The metric name
|
||||
* @property {{Readonly<Partial<Record<Index, MetricEndpointBuilder<T>>>>}} by - Index endpoints as lazy getters. Access via .by.dateindex or .by['dateindex']
|
||||
* @property {{Readonly<Partial<Record<Index, MetricEndpointBuilder<T>>>>}} by - Index endpoints as lazy getters. Access via .by.day1 or .by['day1']
|
||||
* @property {{() => readonly Index[]}} indexes - Get the list of available indexes
|
||||
* @property {{(index: Index) => MetricEndpointBuilder<T>|undefined}} get - Get an endpoint for a specific index
|
||||
*/
|
||||
@@ -284,7 +319,11 @@ function _endpoint(client, name, index) {{
|
||||
/** @type {{MetricEndpointBuilder<T>}} */
|
||||
const endpoint = {{
|
||||
get(idx) {{ return singleItemBuilder(idx); }},
|
||||
slice(start, end) {{ return rangeBuilder(start, end); }},
|
||||
slice(start, end) {{
|
||||
if (start instanceof Date) start = dateToIndex(index, start);
|
||||
if (end instanceof Date) end = dateToIndex(index, end);
|
||||
return rangeBuilder(start, end);
|
||||
}},
|
||||
first(n) {{ return rangeBuilder(undefined, n); }},
|
||||
last(n) {{ return n === 0 ? rangeBuilder(undefined, 0) : rangeBuilder(-n, undefined); }},
|
||||
skip(n) {{ return skippedBuilder(n); }},
|
||||
@@ -457,13 +496,15 @@ pub fn generate_static_constants(output: &mut String) {
|
||||
}}
|
||||
|
||||
/**
|
||||
* Check if an index type is date-based.
|
||||
* @param {{Index}} index
|
||||
* @returns {{boolean}}
|
||||
* Convert a Date to an index value for date-based indexes.
|
||||
* @param {{Index}} index - The index type
|
||||
* @param {{globalThis.Date}} d - The date to convert
|
||||
* @returns {{number}}
|
||||
*/
|
||||
isDateIndex(index) {{
|
||||
return isDateIndex(index);
|
||||
dateToIndex(index, d) {{
|
||||
return dateToIndex(index, d);
|
||||
}}
|
||||
|
||||
"#
|
||||
)
|
||||
.unwrap();
|
||||
@@ -501,14 +542,14 @@ pub fn generate_index_accessors(output: &mut String, patterns: &[IndexSetPattern
|
||||
|
||||
writeln!(output, "// Index group constants and factory\n").unwrap();
|
||||
|
||||
// Generate index array constants (e.g., _i1 = ["dateindex", "height"])
|
||||
// Generate index array constants (e.g., _i1 = ["day1", "height"])
|
||||
for (i, pattern) in patterns.iter().enumerate() {
|
||||
write!(output, "const _i{} = /** @type {{const}} */ ([", i + 1).unwrap();
|
||||
for (j, index) in pattern.indexes.iter().enumerate() {
|
||||
if j > 0 {
|
||||
write!(output, ", ").unwrap();
|
||||
}
|
||||
write!(output, "\"{}\"", index.serialize_long()).unwrap();
|
||||
write!(output, "\"{}\"", index.name()).unwrap();
|
||||
}
|
||||
writeln!(output, "]);").unwrap();
|
||||
}
|
||||
@@ -554,7 +595,7 @@ function _mp(client, name, indexes) {{
|
||||
.map(|idx| {
|
||||
format!(
|
||||
"readonly {}: MetricEndpointBuilder<T>",
|
||||
idx.serialize_long()
|
||||
idx.name()
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -40,15 +40,14 @@ pub fn generate_main_client(output: &mut String, endpoints: &[Endpoint]) {
|
||||
writeln!(output).unwrap();
|
||||
|
||||
// Generate helper methods
|
||||
writeln!(output, " def index_to_date(self, index: Index, i: int) -> date:").unwrap();
|
||||
writeln!(output, " \"\"\"Convert an index value to a date for date-based indexes.\"\"\"").unwrap();
|
||||
writeln!(output, " return index_to_date(index, i)").unwrap();
|
||||
writeln!(output, " def index_to_date(self, index: Index, i: int) -> Union[date, datetime]:").unwrap();
|
||||
writeln!(output, " \"\"\"Convert an index value to a date/datetime for date-based indexes.\"\"\"").unwrap();
|
||||
writeln!(output, " return _index_to_date(index, i)").unwrap();
|
||||
writeln!(output).unwrap();
|
||||
writeln!(output, " def is_date_index(self, index: Index) -> bool:").unwrap();
|
||||
writeln!(output, " \"\"\"Check if an index type is date-based.\"\"\"").unwrap();
|
||||
writeln!(output, " return is_date_index(index)").unwrap();
|
||||
writeln!(output, " def date_to_index(self, index: Index, d: Union[date, datetime]) -> int:").unwrap();
|
||||
writeln!(output, " \"\"\"Convert a date/datetime to an index value for date-based indexes.\"\"\"").unwrap();
|
||||
writeln!(output, " return _date_to_index(index, d)").unwrap();
|
||||
writeln!(output).unwrap();
|
||||
|
||||
// Generate API methods
|
||||
generate_api_methods(output, endpoints);
|
||||
}
|
||||
|
||||
@@ -132,36 +132,93 @@ pub fn generate_endpoint_class(output: &mut String) {
|
||||
writeln!(
|
||||
output,
|
||||
r#"# Date conversion constants
|
||||
_GENESIS = date(2009, 1, 3) # dateindex 0, weekindex 0
|
||||
_DAY_ONE = date(2009, 1, 9) # dateindex 1 (6 day gap after genesis)
|
||||
_DATE_INDEXES = frozenset(['dateindex', 'weekindex', 'monthindex', 'yearindex', 'quarterindex', 'semesterindex', 'decadeindex'])
|
||||
_GENESIS = date(2009, 1, 3) # day1 0, week1 0
|
||||
_DAY_ONE = date(2009, 1, 9) # day1 1 (6 day gap after genesis)
|
||||
_EPOCH = datetime(2009, 1, 1, tzinfo=timezone.utc)
|
||||
_DATE_INDEXES = frozenset([
|
||||
'minute1', 'minute5', 'minute10', 'minute30',
|
||||
'hour1', 'hour4', 'hour12',
|
||||
'day1', 'day3', 'week1',
|
||||
'month1', 'month3', 'month6',
|
||||
'year1', 'year10',
|
||||
])
|
||||
|
||||
def is_date_index(index: str) -> bool:
|
||||
"""Check if an index type is date-based."""
|
||||
return index in _DATE_INDEXES
|
||||
|
||||
def index_to_date(index: str, i: int) -> date:
|
||||
"""Convert an index value to a date for date-based indexes."""
|
||||
if index == 'dateindex':
|
||||
def _index_to_date(index: str, i: int) -> Union[date, datetime]:
|
||||
"""Convert an index value to a date/datetime for date-based indexes."""
|
||||
if index == 'minute1':
|
||||
return _EPOCH + timedelta(minutes=i)
|
||||
elif index == 'minute5':
|
||||
return _EPOCH + timedelta(minutes=i * 5)
|
||||
elif index == 'minute10':
|
||||
return _EPOCH + timedelta(minutes=i * 10)
|
||||
elif index == 'minute30':
|
||||
return _EPOCH + timedelta(minutes=i * 30)
|
||||
elif index == 'hour1':
|
||||
return _EPOCH + timedelta(hours=i)
|
||||
elif index == 'hour4':
|
||||
return _EPOCH + timedelta(hours=i * 4)
|
||||
elif index == 'hour12':
|
||||
return _EPOCH + timedelta(hours=i * 12)
|
||||
elif index == 'day1':
|
||||
return _GENESIS if i == 0 else _DAY_ONE + timedelta(days=i - 1)
|
||||
elif index == 'weekindex':
|
||||
elif index == 'day3':
|
||||
return _EPOCH.date() + timedelta(days=i * 3)
|
||||
elif index == 'week1':
|
||||
return _GENESIS + timedelta(weeks=i)
|
||||
elif index == 'monthindex':
|
||||
elif index == 'month1':
|
||||
return date(2009 + i // 12, i % 12 + 1, 1)
|
||||
elif index == 'yearindex':
|
||||
return date(2009 + i, 1, 1)
|
||||
elif index == 'quarterindex':
|
||||
elif index == 'month3':
|
||||
m = i * 3
|
||||
return date(2009 + m // 12, m % 12 + 1, 1)
|
||||
elif index == 'semesterindex':
|
||||
elif index == 'month6':
|
||||
m = i * 6
|
||||
return date(2009 + m // 12, m % 12 + 1, 1)
|
||||
elif index == 'decadeindex':
|
||||
elif index == 'year1':
|
||||
return date(2009 + i, 1, 1)
|
||||
elif index == 'year10':
|
||||
return date(2009 + i * 10, 1, 1)
|
||||
else:
|
||||
raise ValueError(f"{{index}} is not a date-based index")
|
||||
|
||||
|
||||
def _date_to_index(index: str, d: Union[date, datetime]) -> int:
|
||||
"""Convert a date/datetime to an index value for date-based indexes.
|
||||
|
||||
Returns the floor index (latest index whose date is <= the given date).
|
||||
For sub-day indexes (minute*, hour*), a plain date is treated as midnight UTC.
|
||||
"""
|
||||
if index in ('minute1', 'minute5', 'minute10', 'minute30', 'hour1', 'hour4', 'hour12'):
|
||||
if isinstance(d, datetime):
|
||||
dt = d if d.tzinfo else d.replace(tzinfo=timezone.utc)
|
||||
else:
|
||||
dt = datetime(d.year, d.month, d.day, tzinfo=timezone.utc)
|
||||
secs = int((dt - _EPOCH).total_seconds())
|
||||
div = {{'minute1': 60, 'minute5': 300, 'minute10': 600, 'minute30': 1800,
|
||||
'hour1': 3600, 'hour4': 14400, 'hour12': 43200}}
|
||||
return secs // div[index]
|
||||
dd = d.date() if isinstance(d, datetime) else d
|
||||
if index == 'day1':
|
||||
if dd < _DAY_ONE:
|
||||
return 0
|
||||
return 1 + (dd - _DAY_ONE).days
|
||||
elif index == 'day3':
|
||||
return (dd - date(2009, 1, 1)).days // 3
|
||||
elif index == 'week1':
|
||||
return (dd - _GENESIS).days // 7
|
||||
elif index == 'month1':
|
||||
return (dd.year - 2009) * 12 + (dd.month - 1)
|
||||
elif index == 'month3':
|
||||
return (dd.year - 2009) * 4 + (dd.month - 1) // 3
|
||||
elif index == 'month6':
|
||||
return (dd.year - 2009) * 2 + (dd.month - 1) // 6
|
||||
elif index == 'year1':
|
||||
return dd.year - 2009
|
||||
elif index == 'year10':
|
||||
return (dd.year - 2009) // 10
|
||||
else:
|
||||
raise ValueError(f"{{index}} is not a date-based index")
|
||||
|
||||
|
||||
@dataclass
|
||||
class MetricData(Generic[T]):
|
||||
"""Metric data with range information."""
|
||||
@@ -173,71 +230,64 @@ class MetricData(Generic[T]):
|
||||
stamp: str
|
||||
data: List[T]
|
||||
|
||||
def dates(self) -> List[date]:
|
||||
"""Convert index range to dates. Only works for date-based indexes."""
|
||||
return [index_to_date(self.index, i) for i in range(self.start, self.end)]
|
||||
@property
|
||||
def is_date_based(self) -> bool:
|
||||
"""Whether this metric uses a date-based index."""
|
||||
return self.index in _DATE_INDEXES
|
||||
|
||||
def dates(self) -> list:
|
||||
"""Get dates for the index range. Date-based indexes only, throws otherwise."""
|
||||
return [_index_to_date(self.index, i) for i in range(self.start, self.end)]
|
||||
|
||||
def indexes(self) -> List[int]:
|
||||
"""Get index range as list."""
|
||||
"""Get raw index numbers."""
|
||||
return list(range(self.start, self.end))
|
||||
|
||||
def to_date_dict(self) -> dict[date, T]:
|
||||
"""Return data as {{date: value}} dict. Only works for date-based indexes."""
|
||||
return dict(zip(self.dates(), self.data))
|
||||
def keys(self) -> list:
|
||||
"""Get keys: dates for date-based indexes, index numbers otherwise."""
|
||||
return self.dates() if self.is_date_based else self.indexes()
|
||||
|
||||
def to_index_dict(self) -> dict[int, T]:
|
||||
"""Return data as {{index: value}} dict."""
|
||||
return dict(zip(range(self.start, self.end), self.data))
|
||||
def items(self) -> list:
|
||||
"""Get (key, value) pairs: keys are dates for date-based, numbers otherwise."""
|
||||
return list(zip(self.keys(), self.data))
|
||||
|
||||
def date_items(self) -> List[Tuple[date, T]]:
|
||||
"""Return data as [(date, value), ...] pairs. Only works for date-based indexes."""
|
||||
return list(zip(self.dates(), self.data))
|
||||
def to_dict(self) -> dict:
|
||||
"""Return {{key: value}} dict: keys are dates for date-based, numbers otherwise."""
|
||||
return dict(zip(self.keys(), self.data))
|
||||
|
||||
def index_items(self) -> List[Tuple[int, T]]:
|
||||
"""Return data as [(index, value), ...] pairs."""
|
||||
return list(zip(range(self.start, self.end), self.data))
|
||||
|
||||
def iter(self) -> Iterator[Tuple[int, T]]:
|
||||
"""Iterate over (index, value) pairs."""
|
||||
return iter(zip(range(self.start, self.end), self.data))
|
||||
|
||||
def iter_dates(self) -> Iterator[Tuple[date, T]]:
|
||||
"""Iterate over (date, value) pairs. Date-based indexes only."""
|
||||
return iter(zip(self.dates(), self.data))
|
||||
|
||||
def __iter__(self) -> Iterator[Tuple[int, T]]:
|
||||
"""Default iteration over (index, value) pairs."""
|
||||
return self.iter()
|
||||
def __iter__(self):
|
||||
"""Iterate over (key, value) pairs. Keys are dates for date-based, numbers otherwise."""
|
||||
return iter(zip(self.keys(), self.data))
|
||||
|
||||
def to_polars(self, with_dates: bool = True) -> pl.DataFrame:
|
||||
"""Convert to Polars DataFrame. Requires polars to be installed.
|
||||
|
||||
Returns a DataFrame with columns:
|
||||
- 'date' (date) and 'value' (T) if with_dates=True and index is date-based
|
||||
- 'index' (int) and 'value' (T) otherwise
|
||||
- 'date' and 'value' if with_dates=True and index is date-based
|
||||
- 'index' and 'value' otherwise
|
||||
"""
|
||||
try:
|
||||
import polars as pl # type: ignore[import-not-found]
|
||||
except ImportError:
|
||||
raise ImportError("polars is required: pip install polars")
|
||||
if with_dates and self.index in _DATE_INDEXES:
|
||||
if with_dates and self.is_date_based:
|
||||
return pl.DataFrame({{"date": self.dates(), "value": self.data}})
|
||||
return pl.DataFrame({{"index": list(range(self.start, self.end)), "value": self.data}})
|
||||
return pl.DataFrame({{"index": self.indexes(), "value": self.data}})
|
||||
|
||||
def to_pandas(self, with_dates: bool = True) -> pd.DataFrame:
|
||||
"""Convert to Pandas DataFrame. Requires pandas to be installed.
|
||||
|
||||
Returns a DataFrame with columns:
|
||||
- 'date' (date) and 'value' (T) if with_dates=True and index is date-based
|
||||
- 'index' (int) and 'value' (T) otherwise
|
||||
- 'date' and 'value' if with_dates=True and index is date-based
|
||||
- 'index' and 'value' otherwise
|
||||
"""
|
||||
try:
|
||||
import pandas as pd # type: ignore[import-not-found]
|
||||
except ImportError:
|
||||
raise ImportError("pandas is required: pip install pandas")
|
||||
if with_dates and self.index in _DATE_INDEXES:
|
||||
if with_dates and self.is_date_based:
|
||||
return pd.DataFrame({{"date": self.dates(), "value": self.data}})
|
||||
return pd.DataFrame({{"index": list(range(self.start, self.end)), "value": self.data}})
|
||||
return pd.DataFrame({{"index": self.indexes(), "value": self.data}})
|
||||
|
||||
|
||||
# Type alias for non-generic usage
|
||||
@@ -369,23 +419,36 @@ class MetricEndpointBuilder(Generic[T]):
|
||||
@overload
|
||||
def __getitem__(self, key: slice) -> RangeBuilder[T]: ...
|
||||
|
||||
def __getitem__(self, key: Union[int, slice]) -> Union[SingleItemBuilder[T], RangeBuilder[T]]:
|
||||
"""Access single item or slice.
|
||||
def __getitem__(self, key: Union[int, slice, date, datetime]) -> Union[SingleItemBuilder[T], RangeBuilder[T]]:
|
||||
"""Access single item or slice. Accepts dates for date-based indexes.
|
||||
|
||||
Examples:
|
||||
endpoint[5] # Single item at index 5
|
||||
endpoint[:10] # First 10
|
||||
endpoint[-5:] # Last 5
|
||||
endpoint[100:110] # Range 100-109
|
||||
endpoint[5] # Single item at index 5
|
||||
endpoint[:10] # First 10
|
||||
endpoint[-5:] # Last 5
|
||||
endpoint[100:110] # Range 100-109
|
||||
endpoint[date(2020, 1, 1):date(2023, 1, 1)] # Date range
|
||||
endpoint[date(2020, 1, 1):] # Since date
|
||||
"""
|
||||
if isinstance(key, (date, datetime)):
|
||||
idx = _date_to_index(self._config.index, key)
|
||||
return SingleItemBuilder(_EndpointConfig(
|
||||
self._config.client, self._config.name, self._config.index,
|
||||
idx, idx + 1
|
||||
))
|
||||
if isinstance(key, int):
|
||||
return SingleItemBuilder(_EndpointConfig(
|
||||
self._config.client, self._config.name, self._config.index,
|
||||
key, key + 1
|
||||
))
|
||||
start, stop = key.start, key.stop
|
||||
if isinstance(start, (date, datetime)):
|
||||
start = _date_to_index(self._config.index, start)
|
||||
if isinstance(stop, (date, datetime)):
|
||||
stop = _date_to_index(self._config.index, stop)
|
||||
return RangeBuilder(_EndpointConfig(
|
||||
self._config.client, self._config.name, self._config.index,
|
||||
key.start, key.stop
|
||||
start, stop
|
||||
))
|
||||
|
||||
def head(self, n: int = 10) -> RangeBuilder[T]:
|
||||
@@ -462,7 +525,7 @@ pub fn generate_index_accessors(output: &mut String, patterns: &[IndexSetPattern
|
||||
if j > 0 {
|
||||
write!(output, ", ").unwrap();
|
||||
}
|
||||
write!(output, "'{}'", index.serialize_long()).unwrap();
|
||||
write!(output, "'{}'", index.name()).unwrap();
|
||||
}
|
||||
// Single-element tuple needs trailing comma
|
||||
if pattern.indexes.len() == 1 {
|
||||
@@ -496,7 +559,7 @@ pub fn generate_index_accessors(output: &mut String, patterns: &[IndexSetPattern
|
||||
.unwrap();
|
||||
for index in &pattern.indexes {
|
||||
let method_name = index_to_field_name(index);
|
||||
let index_name = index.serialize_long();
|
||||
let index_name = index.name();
|
||||
writeln!(
|
||||
output,
|
||||
" def {}(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, '{}')",
|
||||
|
||||
@@ -38,7 +38,7 @@ pub fn generate_python_client(
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(output, "from urllib.parse import urlparse").unwrap();
|
||||
writeln!(output, "from datetime import date, timedelta").unwrap();
|
||||
writeln!(output, "from datetime import date, datetime, timedelta, timezone").unwrap();
|
||||
writeln!(output, "import json\n").unwrap();
|
||||
writeln!(output, "if TYPE_CHECKING:").unwrap();
|
||||
writeln!(output, " import pandas as pd # type: ignore[import-not-found]").unwrap();
|
||||
|
||||
@@ -187,7 +187,7 @@ fn param_type_to_rust(param_type: &str) -> String {
|
||||
fn build_path_template(endpoint: &Endpoint) -> (String, &'static str) {
|
||||
let has_index_param = endpoint.path_params.iter().any(|p| p.name == "index" && p.param_type == "Index");
|
||||
if has_index_param {
|
||||
(endpoint.path.replace("{index}", "{}"), ", index.serialize_long()")
|
||||
(endpoint.path.replace("{index}", "{}"), ", index.name()")
|
||||
} else {
|
||||
(endpoint.path.clone(), "")
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ impl EndpointConfig {{
|
||||
}}
|
||||
|
||||
fn path(&self) -> String {{
|
||||
format!("/api/metric/{{}}/{{}}", self.name, self.index.serialize_long())
|
||||
format!("/api/metric/{{}}/{{}}", self.name, self.index.name())
|
||||
}}
|
||||
|
||||
fn build_path(&self, format: Option<&str>) -> String {{
|
||||
|
||||
@@ -54,9 +54,9 @@ pub fn to_camel_case(s: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an Index to a snake_case field name (e.g., DateIndex -> dateindex).
|
||||
/// Convert an Index to a snake_case field name (e.g., Day1 -> day1).
|
||||
pub fn index_to_field_name(index: &Index) -> String {
|
||||
to_snake_case(index.serialize_long())
|
||||
to_snake_case(index.name())
|
||||
}
|
||||
|
||||
/// Generate a child type/struct/class name (e.g., ParentName + child_name -> ParentName_ChildName).
|
||||
|
||||
@@ -12,14 +12,13 @@ anyhow = "1.0"
|
||||
brk_alloc = { workspace = true }
|
||||
brk_computer = { workspace = true }
|
||||
brk_error = { workspace = true, features = ["tokio", "vecdb"] }
|
||||
brk_fetcher = { workspace = true }
|
||||
brk_indexer = { workspace = true }
|
||||
brk_iterator = { workspace = true }
|
||||
brk_logger = { workspace = true }
|
||||
brk_mempool = { workspace = true }
|
||||
brk_query = { workspace = true }
|
||||
brk_reader = { workspace = true }
|
||||
brk_rpc = { workspace = true }
|
||||
brk_rpc = { workspace = true, features = ["corepc"] }
|
||||
brk_server = { workspace = true }
|
||||
brk_types = { workspace = true }
|
||||
lexopt = "0.3"
|
||||
@@ -27,7 +26,7 @@ owo-colors = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
toml = "1.0.1"
|
||||
toml = "1.0.3"
|
||||
vecdb = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
# brk_cli
|
||||
# BRK CLI
|
||||
|
||||
Command-line interface for running a Bitcoin Research Kit instance.
|
||||
|
||||
## Preview
|
||||
## Demo
|
||||
|
||||
- https://bitview.space - web interface
|
||||
- https://bitview.space/api - API docs
|
||||
- [bitview.space](https://bitview.space) - web interface
|
||||
- [bitview.space/api](https://bitview.space/api) - API docs
|
||||
|
||||
## Requirements
|
||||
|
||||
- Bitcoin Core running with RPC enabled
|
||||
- Linux or macOS
|
||||
- Bitcoin Core with `server=1` in `bitcoin.conf`
|
||||
- Access to `blk*.dat` files
|
||||
- [~400 GB disk space](https://bitview.space/api/server/disk)
|
||||
- [12+ GB RAM](https://github.com/bitcoinresearchkit/benches#benchmarks)
|
||||
@@ -35,7 +36,9 @@ brk
|
||||
|
||||
Indexes the blockchain, computes datasets, starts the server on `localhost:3110`, and waits for new blocks.
|
||||
|
||||
**Note:** When more than 10,000 blocks behind, indexing completes before the server starts to free up memory from fragmentation that occurs during large syncs. The web interface at `localhost:3110` won't be available until sync finishes.
|
||||
## First sync
|
||||
|
||||
The initial sync processes the entire blockchain and can take several hours. During this time (more than 10,000 blocks behind), indexing completes before the server starts to free up memory. The web interface at `localhost:3110` won't be available until sync finishes.
|
||||
|
||||
## Options
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::{
|
||||
};
|
||||
|
||||
use brk_error::{Error, Result};
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_rpc::{Auth, Client};
|
||||
use brk_server::Website;
|
||||
use brk_types::Port;
|
||||
@@ -24,9 +23,6 @@ pub struct Config {
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
website: Option<Website>,
|
||||
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
fetch: Option<bool>,
|
||||
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
bitcoindir: Option<String>,
|
||||
|
||||
@@ -70,9 +66,6 @@ impl Config {
|
||||
if let Some(v) = config_args.website {
|
||||
config.website = Some(v);
|
||||
}
|
||||
if let Some(v) = config_args.fetch {
|
||||
config.fetch = Some(v);
|
||||
}
|
||||
if let Some(v) = config_args.bitcoindir {
|
||||
config.bitcoindir = Some(v);
|
||||
}
|
||||
@@ -119,7 +112,6 @@ impl Config {
|
||||
Long("brkdir") => config.brkdir = Some(parser.value().unwrap().parse().unwrap()),
|
||||
Long("brkport") => config.brkport = Some(parser.value().unwrap().parse().unwrap()),
|
||||
Long("website") => config.website = Some(parser.value().unwrap().parse().unwrap()),
|
||||
Long("fetch") => config.fetch = Some(parser.value().unwrap().parse().unwrap()),
|
||||
Long("bitcoindir") => {
|
||||
config.bitcoindir = Some(parser.value().unwrap().parse().unwrap())
|
||||
}
|
||||
@@ -179,11 +171,6 @@ impl Config {
|
||||
"<BOOL|PATH>".bright_black(),
|
||||
"[true]".bright_black()
|
||||
);
|
||||
println!(
|
||||
" --fetch {} Fetch prices {}",
|
||||
"<BOOL>".bright_black(),
|
||||
"[true]".bright_black()
|
||||
);
|
||||
println!();
|
||||
println!(
|
||||
" --bitcoindir {} Bitcoin directory {}",
|
||||
@@ -236,10 +223,7 @@ impl Config {
|
||||
" Edit {} to persist settings:",
|
||||
"~/.brk/config.toml".bright_black()
|
||||
);
|
||||
println!(
|
||||
" {}",
|
||||
"brkdir = \"/path/to/data\"".bright_black()
|
||||
);
|
||||
println!(" {}", "brkdir = \"/path/to/data\"".bright_black());
|
||||
println!(
|
||||
" {}",
|
||||
"bitcoindir = \"/path/to/.bitcoin\"".bright_black()
|
||||
@@ -338,10 +322,6 @@ Finally, you can run the program with '-h' for help."
|
||||
.map_or_else(default_brk_path, |s| fix_user_path(s.as_ref()))
|
||||
}
|
||||
|
||||
pub fn harsdir(&self) -> PathBuf {
|
||||
self.brkdir().join("hars")
|
||||
}
|
||||
|
||||
fn path_cookiefile(&self) -> PathBuf {
|
||||
self.rpccookiefile.as_ref().map_or_else(
|
||||
|| self.bitcoindir().join(".cookie"),
|
||||
@@ -356,15 +336,6 @@ Finally, you can run the program with '-h' for help."
|
||||
pub fn brkport(&self) -> Option<Port> {
|
||||
self.brkport
|
||||
}
|
||||
|
||||
pub fn fetch(&self) -> bool {
|
||||
self.fetch.is_none_or(|b| b)
|
||||
}
|
||||
|
||||
pub fn fetcher(&self) -> Option<Fetcher> {
|
||||
self.fetch()
|
||||
.then(|| Fetcher::import(Some(self.harsdir().as_path())).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
fn default_on_error<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
||||
|
||||
@@ -24,15 +24,6 @@ mod paths;
|
||||
use crate::{config::Config, paths::*};
|
||||
|
||||
pub fn main() -> anyhow::Result<()> {
|
||||
// Can't increase main thread's stack size, thus we need to use another thread
|
||||
thread::Builder::new()
|
||||
.stack_size(512 * 1024 * 1024)
|
||||
.spawn(run)?
|
||||
.join()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn run() -> anyhow::Result<()> {
|
||||
fs::create_dir_all(dot_brk_path())?;
|
||||
|
||||
brk_logger::init(Some(&dot_brk_log_path()))?;
|
||||
@@ -68,7 +59,7 @@ pub fn run() -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut computer = Computer::forced_import(&config.brkdir(), &indexer, config.fetcher())?;
|
||||
let mut computer = Computer::forced_import(&config.brkdir(), &indexer)?;
|
||||
|
||||
let mempool = Mempool::new(&client);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ homepage.workspace = true
|
||||
repository.workspace = true
|
||||
keywords = ["bitcoin", "blockchain", "analytics", "on-chain"]
|
||||
categories = ["api-bindings", "cryptography::cryptocurrencies"]
|
||||
exclude = ["examples/"]
|
||||
|
||||
[dependencies]
|
||||
brk_cohort = { workspace = true }
|
||||
|
||||
@@ -21,7 +21,7 @@ fn main() -> brk_client::Result<()> {
|
||||
|
||||
// Blockchain data (mempool.space compatible)
|
||||
let block = client.get_block_by_height(800000)?;
|
||||
let tx = client.get_tx("abc123...")?;
|
||||
let tx = client.get_tx("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d")?;
|
||||
let address = client.get_address("bc1q...")?;
|
||||
|
||||
// Metrics API - typed, chainable
|
||||
|
||||
@@ -17,12 +17,12 @@ fn main() -> brk_client::Result<()> {
|
||||
// Using new idiomatic API: last(3).fetch()
|
||||
let price_close = client
|
||||
.metrics()
|
||||
.price
|
||||
.prices
|
||||
.usd
|
||||
.split
|
||||
.close
|
||||
.by
|
||||
.dateindex()
|
||||
.day1()
|
||||
.last(3)
|
||||
.fetch()?;
|
||||
println!("Last 3 price close values: {:?}", price_close);
|
||||
@@ -35,29 +35,20 @@ fn main() -> brk_client::Result<()> {
|
||||
.block_count
|
||||
.sum
|
||||
.by
|
||||
.dateindex()
|
||||
.day1()
|
||||
.last(3)
|
||||
.fetch()?;
|
||||
println!("Last 3 block count values: {:?}", block_count);
|
||||
|
||||
// Fetch supply data
|
||||
dbg!(
|
||||
client
|
||||
.metrics()
|
||||
.supply
|
||||
.circulating
|
||||
.bitcoin
|
||||
.by
|
||||
.dateindex()
|
||||
.path()
|
||||
);
|
||||
dbg!(client.metrics().supply.circulating.btc.by.day1().path());
|
||||
let circulating = client
|
||||
.metrics()
|
||||
.supply
|
||||
.circulating
|
||||
.bitcoin
|
||||
.btc
|
||||
.by
|
||||
.dateindex()
|
||||
.day1()
|
||||
.last(3)
|
||||
.fetch_csv()?;
|
||||
println!("Last 3 circulating supply values: {:?}", circulating);
|
||||
@@ -65,7 +56,7 @@ fn main() -> brk_client::Result<()> {
|
||||
// Using generic metric fetching
|
||||
let metricdata = client.get_metric(
|
||||
Metric::from("price_close"),
|
||||
Index::DateIndex,
|
||||
Index::Day1,
|
||||
Some(-3),
|
||||
None,
|
||||
None,
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, Write};
|
||||
|
||||
use brk_client::{BrkClient, BrkClientOptions, Result};
|
||||
use brk_types::Dollars;
|
||||
|
||||
const CHUNK_SIZE: usize = 10_000;
|
||||
const END_HEIGHT: usize = 630_000;
|
||||
const OUTPUT_FILE: &str = "prices_avg.txt";
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let client = BrkClient::with_options(BrkClientOptions {
|
||||
base_url: "https://next.bitview.space".to_string(),
|
||||
timeout_secs: 60,
|
||||
});
|
||||
|
||||
let file = File::create(OUTPUT_FILE).map_err(|e| brk_client::BrkError {
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
let mut writer = BufWriter::new(file);
|
||||
|
||||
for start in (0..END_HEIGHT).step_by(CHUNK_SIZE) {
|
||||
let end = (start + CHUNK_SIZE).min(END_HEIGHT);
|
||||
eprintln!("Fetching {start} to {end}...");
|
||||
|
||||
let ohlcs = client
|
||||
.metrics()
|
||||
.price
|
||||
.cents
|
||||
.ohlc
|
||||
.by
|
||||
.height()
|
||||
.range(start..end)
|
||||
.fetch()?;
|
||||
|
||||
for ohlc in ohlcs.data {
|
||||
let avg = (u64::from(*ohlc.open) + u64::from(*ohlc.close)) / 2;
|
||||
let avg = Dollars::from(avg);
|
||||
writeln!(writer, "{avg}").map_err(|e| brk_client::BrkError {
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
writer.flush().map_err(|e| brk_client::BrkError {
|
||||
message: e.to_string(),
|
||||
})?;
|
||||
eprintln!("Done. Output in {OUTPUT_FILE}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -55,7 +55,7 @@ fn main() -> brk_client::Result<()> {
|
||||
|
||||
for metric in &metrics {
|
||||
for index in &metric.indexes {
|
||||
let index_str = index.serialize_long();
|
||||
let index_str = index.name();
|
||||
let full_path = format!("{}.by.{}", metric.path, index_str);
|
||||
|
||||
match client.get_metric(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
use brk_traversable::Traversable;
|
||||
use rayon::prelude::*;
|
||||
use vecdb::AnyExportableVec;
|
||||
use vecdb::{AnyExportableVec, ReadOnlyClone};
|
||||
|
||||
use crate::Filter;
|
||||
|
||||
@@ -80,6 +80,18 @@ impl<T> AddressGroups<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ReadOnlyClone> ReadOnlyClone for AddressGroups<T> {
|
||||
type ReadOnly = AddressGroups<T::ReadOnly>;
|
||||
|
||||
fn read_only_clone(&self) -> Self::ReadOnly {
|
||||
AddressGroups {
|
||||
ge_amount: self.ge_amount.read_only_clone(),
|
||||
amount_range: self.amount_range.read_only_clone(),
|
||||
lt_amount: self.lt_amount.read_only_clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Traversable for AddressGroups<T>
|
||||
where
|
||||
ByGreatEqualAmount<T>: brk_traversable::Traversable,
|
||||
|
||||
@@ -6,18 +6,18 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
exclude = ["examples/"]
|
||||
|
||||
[dependencies]
|
||||
bitcoin = { workspace = true }
|
||||
brk_error = { workspace = true, features = ["vecdb"] }
|
||||
brk_fetcher = { workspace = true }
|
||||
brk_cohort = { workspace = true }
|
||||
brk_indexer = { workspace = true }
|
||||
brk_oracle = { workspace = true }
|
||||
brk_iterator = { workspace = true }
|
||||
brk_logger = { workspace = true }
|
||||
brk_reader = { workspace = true }
|
||||
brk_rpc = { workspace = true }
|
||||
brk_rpc = { workspace = true, features = ["corepc"] }
|
||||
brk_store = { workspace = true }
|
||||
brk_traversable = { workspace = true }
|
||||
brk_types = { workspace = true }
|
||||
@@ -28,6 +28,7 @@ rayon = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
smallvec = { workspace = true }
|
||||
vecdb = { workspace = true }
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ Compute 1000+ on-chain metrics from indexed blockchain data: supply breakdowns,
|
||||
## Core API
|
||||
|
||||
```rust,ignore
|
||||
let mut computer = Computer::forced_import(&outputs_path, &indexer, Some(fetcher))?;
|
||||
let mut computer = Computer::forced_import(&outputs_path, &indexer)?;
|
||||
|
||||
// Compute all metrics for new blocks
|
||||
computer.compute(&indexer, starting_indexes, &reader, &exit)?;
|
||||
@@ -40,7 +40,7 @@ let realized_cap = computer.distribution.utxo_cohorts.all.metrics.realized.unwra
|
||||
| `cointime` | Liveliness, vaultedness, true market mean |
|
||||
| `pools` | Per-pool block counts, rewards, fees |
|
||||
| `market` | Market cap, NVT, Puell multiple |
|
||||
| `price` | Height-to-price mapping from fetched data |
|
||||
| `price` | Height-to-price mapping from on-chain oracle |
|
||||
|
||||
## Cohort System
|
||||
|
||||
@@ -50,6 +50,17 @@ UTXO and address cohorts support filtering by:
|
||||
- **Type**: P2PK, P2PKH, P2MS, P2SH, P2WPKH, P2WSH, P2TR, P2A
|
||||
- **Epoch**: By halving epoch
|
||||
|
||||
```rust,ignore
|
||||
// Access metrics for a specific UTXO cohort (e.g. long-term holders)
|
||||
let lth_supply = computer.distribution.utxo_cohorts.lth.metrics.supply.total.sats.height;
|
||||
|
||||
// Access metrics for an address cohort (e.g. 1-10 BTC holders)
|
||||
let whale_count = computer.distribution.address_cohorts.from_1_to_10.metrics.address_count.height;
|
||||
|
||||
// Access metrics for all UTXOs combined
|
||||
let sopr = computer.distribution.utxo_cohorts.all.metrics.realized.unwrap().sopr.height;
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### End-to-End
|
||||
@@ -71,6 +82,6 @@ Use [mimalloc v3](https://crates.io/crates/mimalloc) as the global allocator to
|
||||
|
||||
- `brk_indexer` for indexed blockchain data
|
||||
- `brk_cohort` for cohort filtering
|
||||
- `brk_fetcher` for price data
|
||||
- `brk_oracle` for on-chain price data
|
||||
- `brk_reader` for raw block access
|
||||
- `brk_traversable` for data export
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use std::{
|
||||
env,
|
||||
path::Path,
|
||||
thread::{self, sleep},
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use brk_alloc::Mimalloc;
|
||||
use brk_computer::Computer;
|
||||
use brk_error::Result;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_iterator::Blocks;
|
||||
use brk_reader::Reader;
|
||||
@@ -18,17 +16,6 @@ use vecdb::Exit;
|
||||
pub fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
|
||||
// Can't increase main thread's stack size, thus we need to use another thread
|
||||
thread::Builder::new()
|
||||
.stack_size(512 * 1024 * 1024)
|
||||
.spawn(run)?
|
||||
.join()
|
||||
.unwrap()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run() -> Result<()> {
|
||||
brk_logger::init(Some(Path::new(".log")))?;
|
||||
|
||||
let bitcoin_dir = Client::default_bitcoin_path();
|
||||
@@ -48,8 +35,6 @@ fn run() -> Result<()> {
|
||||
|
||||
let mut indexer = Indexer::forced_import(&outputs_dir)?;
|
||||
|
||||
let fetcher = Fetcher::import(None)?;
|
||||
|
||||
let exit = Exit::new();
|
||||
exit.set_ctrlc_handler();
|
||||
|
||||
@@ -63,7 +48,7 @@ fn run() -> Result<()> {
|
||||
indexer = Indexer::forced_import(&outputs_dir)?;
|
||||
}
|
||||
|
||||
let mut computer = Computer::forced_import(&outputs_dir, &indexer, Some(fetcher))?;
|
||||
let mut computer = Computer::forced_import(&outputs_dir, &indexer)?;
|
||||
|
||||
loop {
|
||||
let i = Instant::now();
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use std::{env, path::Path, thread, time::Instant};
|
||||
use std::{env, path::Path, time::Instant};
|
||||
|
||||
use brk_alloc::Mimalloc;
|
||||
use brk_bencher::Bencher;
|
||||
use brk_computer::Computer;
|
||||
use brk_error::Result;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_iterator::Blocks;
|
||||
use brk_reader::Reader;
|
||||
@@ -13,15 +12,6 @@ use tracing::{debug, info};
|
||||
use vecdb::Exit;
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
// Can't increase main thread's stack size, thus we need to use another thread
|
||||
thread::Builder::new()
|
||||
.stack_size(512 * 1024 * 1024)
|
||||
.spawn(run)?
|
||||
.join()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn run() -> Result<()> {
|
||||
brk_logger::init(None)?;
|
||||
|
||||
let bitcoin_dir = Client::default_bitcoin_path();
|
||||
@@ -42,9 +32,7 @@ fn run() -> Result<()> {
|
||||
|
||||
let mut indexer = Indexer::forced_import(&outputs_dir)?;
|
||||
|
||||
let fetcher = Fetcher::import(None)?;
|
||||
|
||||
let mut computer = Computer::forced_import(&outputs_benches_dir, &indexer, Some(fetcher))?;
|
||||
let mut computer = Computer::forced_import(&outputs_benches_dir, &indexer)?;
|
||||
|
||||
let mut bencher =
|
||||
Bencher::from_cargo_env(env!("CARGO_PKG_NAME"), &outputs_dir.join("computed"))?;
|
||||
|
||||
@@ -1,33 +1,18 @@
|
||||
use std::{env, path::Path, thread, time::Instant};
|
||||
use std::{env, path::Path, time::Instant};
|
||||
|
||||
use brk_computer::Computer;
|
||||
use brk_error::Result;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
use vecdb::{AnySerializableVec, AnyVec, Exit};
|
||||
use vecdb::{AnySerializableVec, AnyVec};
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
// Can't increase main thread's stack size, thus we need to use another thread
|
||||
thread::Builder::new()
|
||||
.stack_size(512 * 1024 * 1024)
|
||||
.spawn(run)?
|
||||
.join()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn run() -> Result<()> {
|
||||
brk_logger::init(None)?;
|
||||
|
||||
let outputs_dir = Path::new(&env::var("HOME").unwrap()).join(".brk");
|
||||
|
||||
let indexer = Indexer::forced_import(&outputs_dir)?;
|
||||
|
||||
let fetcher = Fetcher::import(None)?;
|
||||
|
||||
let exit = Exit::new();
|
||||
exit.set_ctrlc_handler();
|
||||
|
||||
let computer = Computer::forced_import(&outputs_dir, &indexer, Some(fetcher))?;
|
||||
let computer = Computer::forced_import(&outputs_dir, &indexer)?;
|
||||
|
||||
// Test emptyaddressdata (underlying BytesVec) - direct access
|
||||
let empty_data = &computer.distribution.addresses_data.empty;
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
use std::{
|
||||
env, fs,
|
||||
path::Path,
|
||||
thread::{self, sleep},
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use brk_alloc::Mimalloc;
|
||||
use brk_bencher::Bencher;
|
||||
use brk_computer::Computer;
|
||||
use brk_error::Result;
|
||||
use brk_fetcher::{Fetcher, PriceSource};
|
||||
use brk_indexer::Indexer;
|
||||
use brk_iterator::Blocks;
|
||||
use brk_reader::Reader;
|
||||
@@ -20,17 +18,6 @@ use vecdb::Exit;
|
||||
pub fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
|
||||
// Can't increase main thread's stack size, thus we need to use another thread
|
||||
thread::Builder::new()
|
||||
.stack_size(512 * 1024 * 1024)
|
||||
.spawn(run)?
|
||||
.join()
|
||||
.unwrap()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run() -> Result<()> {
|
||||
let bitcoin_dir = Client::default_bitcoin_path();
|
||||
// let bitcoin_dir = Path::new("/Volumes/WD_BLACK1/bitcoin");
|
||||
|
||||
@@ -60,10 +47,6 @@ fn run() -> Result<()> {
|
||||
|
||||
let blocks = Blocks::new(&client, &reader);
|
||||
|
||||
let fetcher = Fetcher::import(None)?;
|
||||
|
||||
info!("Ping: {:?}", fetcher.brk.ping()?);
|
||||
|
||||
let mut indexer = Indexer::forced_import(&outputs_dir)?;
|
||||
|
||||
// Pre-run indexer if too far behind, then drop and reimport to reduce memory
|
||||
@@ -76,7 +59,7 @@ fn run() -> Result<()> {
|
||||
indexer = Indexer::forced_import(&outputs_dir)?;
|
||||
}
|
||||
|
||||
let mut computer = Computer::forced_import(&outputs_dir, &indexer, Some(fetcher))?;
|
||||
let mut computer = Computer::forced_import(&outputs_dir, &indexer)?;
|
||||
|
||||
loop {
|
||||
let i = Instant::now();
|
||||
|
||||
@@ -2,56 +2,27 @@ use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use vecdb::Exit;
|
||||
|
||||
use crate::{ComputeIndexes, indexes, transactions};
|
||||
use crate::{ComputeIndexes, indexes};
|
||||
|
||||
use super::Vecs;
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
transactions: &transactions::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// Core block metrics
|
||||
self.interval.compute(indexer, starting_indexes, exit)?;
|
||||
self.count
|
||||
.compute(indexer, indexes, &self.time, starting_indexes, exit)?;
|
||||
self.interval.compute(indexes, starting_indexes, exit)?;
|
||||
self.size
|
||||
.compute(indexer, indexes, starting_indexes, exit)?;
|
||||
self.weight
|
||||
.compute(indexer, indexes, starting_indexes, exit)?;
|
||||
|
||||
// Time metrics (timestamps)
|
||||
self.time.compute(indexes, starting_indexes, exit)?;
|
||||
|
||||
// Epoch metrics
|
||||
.compute(indexer, &self.time, starting_indexes, exit)?;
|
||||
self.size.compute(indexer, starting_indexes, exit)?;
|
||||
self.weight.compute(indexer, starting_indexes, exit)?;
|
||||
self.difficulty
|
||||
.compute(indexer, indexes, starting_indexes, exit)?;
|
||||
self.halving.compute(indexes, starting_indexes, exit)?;
|
||||
|
||||
// Rewards depends on count and transactions fees
|
||||
self.rewards.compute(
|
||||
indexer,
|
||||
indexes,
|
||||
&self.count,
|
||||
&transactions.fees,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Mining depends on count, difficulty, and rewards
|
||||
self.mining.compute(
|
||||
indexes,
|
||||
&self.count,
|
||||
&self.difficulty,
|
||||
&self.rewards,
|
||||
starting_indexes,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let _lock = exit.lock();
|
||||
self.db.compact()?;
|
||||
Ok(())
|
||||
|
||||
@@ -1,66 +1,186 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{Height, StoredU32};
|
||||
use vecdb::{EagerVec, Exit, PcoVec, TypedVecIterator};
|
||||
use brk_types::{Height, StoredU32, Timestamp};
|
||||
use vecdb::{EagerVec, Exit, PcoVec, ReadableVec, VecIndex};
|
||||
|
||||
use super::super::time;
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, indexes, internal::ComputedFromHeightLast};
|
||||
use crate::ComputeIndexes;
|
||||
|
||||
use super::{super::time, Vecs};
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
time: &time::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.block_count.height.compute_range(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.weight,
|
||||
|h| (h, StoredU32::from(1_u32)),
|
||||
exit,
|
||||
)?;
|
||||
self.block_count
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_range(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.weight,
|
||||
|h| (h, StoredU32::from(1_u32)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
.compute_cumulative(starting_indexes, exit)?;
|
||||
|
||||
// Compute rolling window starts
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 1, |s| &mut s._24h_start)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 7, |s| &mut s._1w_start)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 30, |s| &mut s._1m_start)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 365, |s| &mut s._1y_start)?;
|
||||
// Compute rolling window starts (collect monotonic data once for all windows)
|
||||
let monotonic_data: Vec<Timestamp> = time.timestamp_monotonic.collect();
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 1, |s| {
|
||||
&mut s.height_24h_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 3, |s| {
|
||||
&mut s.height_3d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 7, |s| {
|
||||
&mut s.height_1w_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 8, |s| {
|
||||
&mut s.height_8d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 9, |s| {
|
||||
&mut s.height_9d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 12, |s| {
|
||||
&mut s.height_12d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 13, |s| {
|
||||
&mut s.height_13d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 14, |s| {
|
||||
&mut s.height_2w_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 21, |s| {
|
||||
&mut s.height_21d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 26, |s| {
|
||||
&mut s.height_26d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 30, |s| {
|
||||
&mut s.height_1m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 34, |s| {
|
||||
&mut s.height_34d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 55, |s| {
|
||||
&mut s.height_55d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 2 * 30, |s| {
|
||||
&mut s.height_2m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 89, |s| {
|
||||
&mut s.height_89d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 3 * 30, |s| {
|
||||
&mut s.height_3m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 111, |s| {
|
||||
&mut s.height_111d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 144, |s| {
|
||||
&mut s.height_144d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 6 * 30, |s| {
|
||||
&mut s.height_6m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 200, |s| {
|
||||
&mut s.height_200d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 350, |s| {
|
||||
&mut s.height_350d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 365, |s| {
|
||||
&mut s.height_1y_ago
|
||||
})?;
|
||||
self.compute_rolling_start(
|
||||
&monotonic_data,
|
||||
time,
|
||||
starting_indexes,
|
||||
exit,
|
||||
2 * 365,
|
||||
|s| &mut s.height_2y_ago,
|
||||
)?;
|
||||
self.compute_rolling_start(
|
||||
&monotonic_data,
|
||||
time,
|
||||
starting_indexes,
|
||||
exit,
|
||||
200 * 7,
|
||||
|s| &mut s.height_200w_ago,
|
||||
)?;
|
||||
self.compute_rolling_start(
|
||||
&monotonic_data,
|
||||
time,
|
||||
starting_indexes,
|
||||
exit,
|
||||
3 * 365,
|
||||
|s| &mut s.height_3y_ago,
|
||||
)?;
|
||||
self.compute_rolling_start(
|
||||
&monotonic_data,
|
||||
time,
|
||||
starting_indexes,
|
||||
exit,
|
||||
4 * 365,
|
||||
|s| &mut s.height_4y_ago,
|
||||
)?;
|
||||
self.compute_rolling_start(
|
||||
&monotonic_data,
|
||||
time,
|
||||
starting_indexes,
|
||||
exit,
|
||||
5 * 365,
|
||||
|s| &mut s.height_5y_ago,
|
||||
)?;
|
||||
self.compute_rolling_start(
|
||||
&monotonic_data,
|
||||
time,
|
||||
starting_indexes,
|
||||
exit,
|
||||
6 * 365,
|
||||
|s| &mut s.height_6y_ago,
|
||||
)?;
|
||||
self.compute_rolling_start(
|
||||
&monotonic_data,
|
||||
time,
|
||||
starting_indexes,
|
||||
exit,
|
||||
8 * 365,
|
||||
|s| &mut s.height_8y_ago,
|
||||
)?;
|
||||
self.compute_rolling_start(
|
||||
&monotonic_data,
|
||||
time,
|
||||
starting_indexes,
|
||||
exit,
|
||||
10 * 365,
|
||||
|s| &mut s.height_10y_ago,
|
||||
)?;
|
||||
|
||||
// Compute rolling window block counts
|
||||
self.compute_rolling_block_count(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
self.block_count_24h_sum.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.height_24h_ago,
|
||||
|(h, start, ..)| (h, StoredU32::from(*h + 1 - *start)),
|
||||
exit,
|
||||
&self._24h_start.clone(),
|
||||
|s| &mut s._24h_block_count,
|
||||
)?;
|
||||
self.compute_rolling_block_count(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
self.block_count_1w_sum.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.height_1w_ago,
|
||||
|(h, start, ..)| (h, StoredU32::from(*h + 1 - *start)),
|
||||
exit,
|
||||
&self._1w_start.clone(),
|
||||
|s| &mut s._1w_block_count,
|
||||
)?;
|
||||
self.compute_rolling_block_count(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
self.block_count_1m_sum.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.height_1m_ago,
|
||||
|(h, start, ..)| (h, StoredU32::from(*h + 1 - *start)),
|
||||
exit,
|
||||
&self._1m_start.clone(),
|
||||
|s| &mut s._1m_block_count,
|
||||
)?;
|
||||
self.compute_rolling_block_count(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
self.block_count_1y_sum.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.height_1y_ago,
|
||||
|(h, start, ..)| (h, StoredU32::from(*h + 1 - *start)),
|
||||
exit,
|
||||
&self._1y_start.clone(),
|
||||
|s| &mut s._1y_block_count,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -68,6 +188,7 @@ impl Vecs {
|
||||
|
||||
fn compute_rolling_start<F>(
|
||||
&mut self,
|
||||
monotonic_data: &[Timestamp],
|
||||
time: &time::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
@@ -77,13 +198,12 @@ impl Vecs {
|
||||
where
|
||||
F: FnOnce(&mut Self) -> &mut EagerVec<PcoVec<Height, Height>>,
|
||||
{
|
||||
let mut iter = time.timestamp_monotonic.into_iter();
|
||||
let mut prev = Height::ZERO;
|
||||
Ok(get_field(self).compute_transform(
|
||||
starting_indexes.height,
|
||||
&time.timestamp_monotonic,
|
||||
|(h, t, ..)| {
|
||||
while t.difference_in_days_between(iter.get_unwrap(prev)) >= days {
|
||||
while t.difference_in_days_between(monotonic_data[prev.to_usize()]) >= days {
|
||||
prev.increment();
|
||||
if prev > h {
|
||||
unreachable!()
|
||||
@@ -94,26 +214,4 @@ impl Vecs {
|
||||
exit,
|
||||
)?)
|
||||
}
|
||||
|
||||
fn compute_rolling_block_count<F>(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
start_height: &EagerVec<PcoVec<Height, Height>>,
|
||||
get_field: F,
|
||||
) -> Result<()>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> &mut ComputedFromHeightLast<StoredU32>,
|
||||
{
|
||||
get_field(self).compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
start_height,
|
||||
|(h, start, ..)| (h, StoredU32::from(*h + 1 - *start)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,78 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{StoredU64, Version};
|
||||
use brk_types::Version;
|
||||
use vecdb::{Database, ImportableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{
|
||||
blocks::{
|
||||
TARGET_BLOCKS_PER_DAY, TARGET_BLOCKS_PER_DECADE, TARGET_BLOCKS_PER_MONTH,
|
||||
TARGET_BLOCKS_PER_QUARTER, TARGET_BLOCKS_PER_SEMESTER, TARGET_BLOCKS_PER_WEEK,
|
||||
TARGET_BLOCKS_PER_YEAR,
|
||||
},
|
||||
indexes,
|
||||
internal::{ComputedFromHeightLast, ComputedFromHeightSumCum, LazyFromDate},
|
||||
internal::{BlockCountTarget, ComputedFromHeightLast, ComputedFromHeightSumCum, ConstantVecs},
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
Ok(Self {
|
||||
block_count_target: LazyFromDate::new(
|
||||
block_count_target: ConstantVecs::new::<BlockCountTarget>(
|
||||
"block_count_target",
|
||||
version,
|
||||
indexes,
|
||||
|_, _| Some(StoredU64::from(TARGET_BLOCKS_PER_DAY)),
|
||||
|_, _| Some(StoredU64::from(TARGET_BLOCKS_PER_WEEK)),
|
||||
|_, _| Some(StoredU64::from(TARGET_BLOCKS_PER_MONTH)),
|
||||
|_, _| Some(StoredU64::from(TARGET_BLOCKS_PER_QUARTER)),
|
||||
|_, _| Some(StoredU64::from(TARGET_BLOCKS_PER_SEMESTER)),
|
||||
|_, _| Some(StoredU64::from(TARGET_BLOCKS_PER_YEAR)),
|
||||
|_, _| Some(StoredU64::from(TARGET_BLOCKS_PER_DECADE)),
|
||||
),
|
||||
block_count: ComputedFromHeightSumCum::forced_import(db, "block_count", version, indexes)?,
|
||||
_24h_start: ImportableVec::forced_import(db, "24h_start", version)?,
|
||||
_1w_start: ImportableVec::forced_import(db, "1w_start", version)?,
|
||||
_1m_start: ImportableVec::forced_import(db, "1m_start", version)?,
|
||||
_1y_start: ImportableVec::forced_import(db, "1y_start", version)?,
|
||||
_24h_block_count: ComputedFromHeightLast::forced_import(
|
||||
block_count: ComputedFromHeightSumCum::forced_import(
|
||||
db,
|
||||
"24h_block_count",
|
||||
"block_count",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
_1w_block_count: ComputedFromHeightLast::forced_import(
|
||||
height_24h_ago: ImportableVec::forced_import(db, "height_24h_ago", version)?,
|
||||
height_3d_ago: ImportableVec::forced_import(db, "height_3d_ago", version)?,
|
||||
height_1w_ago: ImportableVec::forced_import(db, "height_1w_ago", version)?,
|
||||
height_8d_ago: ImportableVec::forced_import(db, "height_8d_ago", version)?,
|
||||
height_9d_ago: ImportableVec::forced_import(db, "height_9d_ago", version)?,
|
||||
height_12d_ago: ImportableVec::forced_import(db, "height_12d_ago", version)?,
|
||||
height_13d_ago: ImportableVec::forced_import(db, "height_13d_ago", version)?,
|
||||
height_2w_ago: ImportableVec::forced_import(db, "height_2w_ago", version)?,
|
||||
height_21d_ago: ImportableVec::forced_import(db, "height_21d_ago", version)?,
|
||||
height_26d_ago: ImportableVec::forced_import(db, "height_26d_ago", version)?,
|
||||
height_1m_ago: ImportableVec::forced_import(db, "height_1m_ago", version)?,
|
||||
height_34d_ago: ImportableVec::forced_import(db, "height_34d_ago", version)?,
|
||||
height_55d_ago: ImportableVec::forced_import(db, "height_55d_ago", version)?,
|
||||
height_2m_ago: ImportableVec::forced_import(db, "height_2m_ago", version)?,
|
||||
height_89d_ago: ImportableVec::forced_import(db, "height_89d_ago", version)?,
|
||||
height_111d_ago: ImportableVec::forced_import(db, "height_111d_ago", version)?,
|
||||
height_144d_ago: ImportableVec::forced_import(db, "height_144d_ago", version)?,
|
||||
height_3m_ago: ImportableVec::forced_import(db, "height_3m_ago", version)?,
|
||||
height_6m_ago: ImportableVec::forced_import(db, "height_6m_ago", version)?,
|
||||
height_200d_ago: ImportableVec::forced_import(db, "height_200d_ago", version)?,
|
||||
height_350d_ago: ImportableVec::forced_import(db, "height_350d_ago", version)?,
|
||||
height_1y_ago: ImportableVec::forced_import(db, "height_1y_ago", version)?,
|
||||
height_2y_ago: ImportableVec::forced_import(db, "height_2y_ago", version)?,
|
||||
height_200w_ago: ImportableVec::forced_import(db, "height_200w_ago", version)?,
|
||||
height_3y_ago: ImportableVec::forced_import(db, "height_3y_ago", version)?,
|
||||
height_4y_ago: ImportableVec::forced_import(db, "height_4y_ago", version)?,
|
||||
height_5y_ago: ImportableVec::forced_import(db, "height_5y_ago", version)?,
|
||||
height_6y_ago: ImportableVec::forced_import(db, "height_6y_ago", version)?,
|
||||
height_8y_ago: ImportableVec::forced_import(db, "height_8y_ago", version)?,
|
||||
height_10y_ago: ImportableVec::forced_import(db, "height_10y_ago", version)?,
|
||||
block_count_24h_sum: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"1w_block_count",
|
||||
"block_count_24h_sum",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
_1m_block_count: ComputedFromHeightLast::forced_import(
|
||||
block_count_1w_sum: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"1m_block_count",
|
||||
"block_count_1w_sum",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
_1y_block_count: ComputedFromHeightLast::forced_import(
|
||||
block_count_1m_sum: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"1y_block_count",
|
||||
"block_count_1m_sum",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
block_count_1y_sum: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"block_count_1y_sum",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
|
||||
@@ -1,21 +1,85 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredU32, StoredU64};
|
||||
use vecdb::{EagerVec, PcoVec};
|
||||
use vecdb::{EagerVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedFromHeightLast, ComputedFromHeightSumCum, LazyFromDate};
|
||||
use crate::internal::{ComputedFromHeightLast, ComputedFromHeightSumCum, ConstantVecs};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub block_count_target: LazyFromDate<StoredU64>,
|
||||
pub block_count: ComputedFromHeightSumCum<StoredU32>,
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub block_count_target: ConstantVecs<StoredU64>,
|
||||
pub block_count: ComputedFromHeightSumCum<StoredU32, M>,
|
||||
// Rolling window starts (height-indexed only, no date aggregation needed)
|
||||
pub _24h_start: EagerVec<PcoVec<Height, Height>>,
|
||||
pub _1w_start: EagerVec<PcoVec<Height, Height>>,
|
||||
pub _1m_start: EagerVec<PcoVec<Height, Height>>,
|
||||
pub _1y_start: EagerVec<PcoVec<Height, Height>>,
|
||||
pub height_24h_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_3d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_1w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_8d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_9d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_12d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_13d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_2w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_21d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_26d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_1m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_34d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_55d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_2m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_89d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_111d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_144d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_3m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_6m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_200d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_350d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_1y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_2y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_200w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_3y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_4y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_5y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_6y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_8y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_10y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
// Rolling window block counts
|
||||
pub _24h_block_count: ComputedFromHeightLast<StoredU32>,
|
||||
pub _1w_block_count: ComputedFromHeightLast<StoredU32>,
|
||||
pub _1m_block_count: ComputedFromHeightLast<StoredU32>,
|
||||
pub _1y_block_count: ComputedFromHeightLast<StoredU32>,
|
||||
pub block_count_24h_sum: ComputedFromHeightLast<StoredU32, M>,
|
||||
pub block_count_1w_sum: ComputedFromHeightLast<StoredU32, M>,
|
||||
pub block_count_1m_sum: ComputedFromHeightLast<StoredU32, M>,
|
||||
pub block_count_1y_sum: ComputedFromHeightLast<StoredU32, M>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn start_vec(&self, days: usize) -> &EagerVec<PcoVec<Height, Height>> {
|
||||
match days {
|
||||
1 => &self.height_24h_ago,
|
||||
3 => &self.height_3d_ago,
|
||||
7 => &self.height_1w_ago,
|
||||
8 => &self.height_8d_ago,
|
||||
9 => &self.height_9d_ago,
|
||||
12 => &self.height_12d_ago,
|
||||
13 => &self.height_13d_ago,
|
||||
14 => &self.height_2w_ago,
|
||||
21 => &self.height_21d_ago,
|
||||
26 => &self.height_26d_ago,
|
||||
30 => &self.height_1m_ago,
|
||||
34 => &self.height_34d_ago,
|
||||
55 => &self.height_55d_ago,
|
||||
60 => &self.height_2m_ago,
|
||||
89 => &self.height_89d_ago,
|
||||
90 => &self.height_3m_ago,
|
||||
111 => &self.height_111d_ago,
|
||||
144 => &self.height_144d_ago,
|
||||
180 => &self.height_6m_ago,
|
||||
200 => &self.height_200d_ago,
|
||||
350 => &self.height_350d_ago,
|
||||
365 => &self.height_1y_ago,
|
||||
730 => &self.height_2y_ago,
|
||||
1095 => &self.height_3y_ago,
|
||||
1400 => &self.height_200w_ago,
|
||||
1460 => &self.height_4y_ago,
|
||||
1825 => &self.height_5y_ago,
|
||||
2190 => &self.height_6y_ago,
|
||||
2920 => &self.height_8y_ago,
|
||||
3650 => &self.height_10y_ago,
|
||||
_ => panic!("No start vec for {days} days"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,102 +1,61 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{StoredF32, StoredU32};
|
||||
use vecdb::{Exit, TypedVecIterator};
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::super::TARGET_BLOCKS_PER_DAY_F32;
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, indexes};
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// Derive dateindex/period stats from raw difficulty
|
||||
self.raw.derive_from(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
// raw is fully lazy from indexer height source — no compute needed
|
||||
|
||||
// Compute difficulty as hash rate equivalent
|
||||
let multiplier = 2.0_f64.powi(32) / 600.0;
|
||||
self.as_hash.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.difficulty,
|
||||
|(i, v, ..)| (i, StoredF32::from(*v * multiplier)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Compute difficulty as hash rate equivalent
|
||||
self.as_hash
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
let multiplier = 2.0_f64.powi(32) / 600.0;
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.difficulty,
|
||||
|(i, v, ..)| (i, StoredF32::from(*v * multiplier)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Compute difficulty adjustment percentage
|
||||
self.adjustment
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_percentage_change(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.difficulty,
|
||||
1,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.adjustment.height.compute_percentage_change(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.difficulty,
|
||||
1,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Compute epoch by dateindex
|
||||
let mut height_to_difficultyepoch_iter = indexes.height.difficultyepoch.into_iter();
|
||||
self.epoch.compute_all(starting_indexes, exit, |vec| {
|
||||
let mut height_count_iter = indexes.dateindex.height_count.into_iter();
|
||||
vec.compute_transform(
|
||||
starting_indexes.dateindex,
|
||||
&indexes.dateindex.first_height,
|
||||
|(di, height, ..)| {
|
||||
(
|
||||
di,
|
||||
height_to_difficultyepoch_iter
|
||||
.get_unwrap(height + (*height_count_iter.get_unwrap(di) - 1)),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
// Compute epoch by height
|
||||
self.epoch.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexes.height.difficultyepoch,
|
||||
|(h, epoch, ..)| (h, epoch),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Compute blocks before next adjustment
|
||||
self.blocks_before_next_adjustment.compute_all(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
self.blocks_before_next_adjustment.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexes.height.identity,
|
||||
|(h, ..)| (h, StoredU32::from(h.left_before_next_diff_adj())),
|
||||
exit,
|
||||
|v| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexes.height.identity,
|
||||
|(h, ..)| (h, StoredU32::from(h.left_before_next_diff_adj())),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
// Compute days before next adjustment
|
||||
self.days_before_next_adjustment.compute_all(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
self.days_before_next_adjustment.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.blocks_before_next_adjustment.height,
|
||||
|(h, blocks, ..)| (h, (*blocks as f32 / TARGET_BLOCKS_PER_DAY_F32).into()),
|
||||
exit,
|
||||
|v| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.blocks_before_next_adjustment.height,
|
||||
|(h, blocks, ..)| (h, (*blocks as f32 / TARGET_BLOCKS_PER_DAY_F32).into()),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::Version;
|
||||
use vecdb::{Database, IterableCloneableVec};
|
||||
use vecdb::{Database, ReadableCloneableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedFromHeightLast, ComputedFromHeightSum, ComputedFromDateLast,
|
||||
ComputedHeightDerivedLast,
|
||||
},
|
||||
internal::{ComputedFromHeightLast, ComputedFromHeightSum, ComputedHeightDerivedLast},
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexer: &Indexer,
|
||||
@@ -23,15 +20,14 @@ impl Vecs {
|
||||
|
||||
Ok(Self {
|
||||
raw: ComputedHeightDerivedLast::forced_import(
|
||||
db,
|
||||
"difficulty",
|
||||
indexer.vecs.blocks.difficulty.boxed_clone(),
|
||||
indexer.vecs.blocks.difficulty.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
),
|
||||
as_hash: ComputedFromHeightLast::forced_import(db, "difficulty_as_hash", version, indexes)?,
|
||||
adjustment: ComputedFromHeightSum::forced_import(db, "difficulty_adjustment", version, indexes)?,
|
||||
epoch: ComputedFromDateLast::forced_import(db, "difficultyepoch", version, indexes)?,
|
||||
epoch: ComputedFromHeightLast::forced_import(db, "difficulty_epoch", version, indexes)?,
|
||||
blocks_before_next_adjustment: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"blocks_before_next_difficulty_adjustment",
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{DifficultyEpoch, StoredF32, StoredF64, StoredU32};
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{
|
||||
ComputedFromHeightLast, ComputedFromHeightSum, ComputedFromDateLast, ComputedHeightDerivedLast,
|
||||
};
|
||||
use crate::internal::{ComputedFromHeightLast, ComputedFromHeightSum, ComputedHeightDerivedLast};
|
||||
|
||||
/// Difficulty metrics: raw difficulty, derived stats, adjustment, and countdown
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
/// Raw difficulty with dateindex/period stats - merges with indexer's raw
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
/// Raw difficulty with day1/period stats - merges with indexer's raw
|
||||
pub raw: ComputedHeightDerivedLast<StoredF64>,
|
||||
pub as_hash: ComputedFromHeightLast<StoredF32>,
|
||||
pub adjustment: ComputedFromHeightSum<StoredF32>,
|
||||
pub epoch: ComputedFromDateLast<DifficultyEpoch>,
|
||||
pub blocks_before_next_adjustment: ComputedFromHeightLast<StoredU32>,
|
||||
pub days_before_next_adjustment: ComputedFromHeightLast<StoredF32>,
|
||||
pub as_hash: ComputedFromHeightLast<StoredF32, M>,
|
||||
pub adjustment: ComputedFromHeightSum<StoredF32, M>,
|
||||
pub epoch: ComputedFromHeightLast<DifficultyEpoch, M>,
|
||||
pub blocks_before_next_adjustment: ComputedFromHeightLast<StoredU32, M>,
|
||||
pub days_before_next_adjustment: ComputedFromHeightLast<StoredF32, M>,
|
||||
}
|
||||
|
||||
@@ -1,57 +1,38 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::StoredU32;
|
||||
use vecdb::{Exit, TypedVecIterator};
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::super::TARGET_BLOCKS_PER_DAY_F32;
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, indexes};
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let mut height_to_halvingepoch_iter = indexes.height.halvingepoch.into_iter();
|
||||
self.epoch.compute_all(starting_indexes, exit, |vec| {
|
||||
let mut height_count_iter = indexes.dateindex.height_count.into_iter();
|
||||
vec.compute_transform(
|
||||
starting_indexes.dateindex,
|
||||
&indexes.dateindex.first_height,
|
||||
|(di, height, ..)| {
|
||||
(
|
||||
di,
|
||||
height_to_halvingepoch_iter
|
||||
.get_unwrap(height + (*height_count_iter.get_unwrap(di) - 1)),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.epoch.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexes.height.halvingepoch,
|
||||
|(h, epoch, ..)| (h, epoch),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.blocks_before_next_halving
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexes.height.identity,
|
||||
|(h, ..)| (h, StoredU32::from(h.left_before_next_halving())),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.blocks_before_next_halving.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexes.height.identity,
|
||||
|(h, ..)| (h, StoredU32::from(h.left_before_next_halving())),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.days_before_next_halving
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.blocks_before_next_halving.height,
|
||||
|(h, blocks, ..)| (h, (*blocks as f32 / TARGET_BLOCKS_PER_DAY_F32).into()),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.days_before_next_halving.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.blocks_before_next_halving.height,
|
||||
|(h, blocks, ..)| (h, (*blocks as f32 / TARGET_BLOCKS_PER_DAY_F32).into()),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,17 +3,18 @@ use brk_types::Version;
|
||||
use vecdb::Database;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightLast, ComputedFromDateLast},
|
||||
};
|
||||
use crate::{indexes, internal::ComputedFromHeightLast};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v2 = Version::TWO;
|
||||
|
||||
Ok(Self {
|
||||
epoch: ComputedFromDateLast::forced_import(db, "halvingepoch", version, indexes)?,
|
||||
epoch: ComputedFromHeightLast::forced_import(db, "halving_epoch", version, indexes)?,
|
||||
blocks_before_next_halving: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"blocks_before_next_halving",
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{HalvingEpoch, StoredF32, StoredU32};
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedFromHeightLast, ComputedFromDateLast};
|
||||
use crate::internal::ComputedFromHeightLast;
|
||||
|
||||
/// Halving epoch metrics and countdown
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub epoch: ComputedFromDateLast<HalvingEpoch>,
|
||||
pub blocks_before_next_halving: ComputedFromHeightLast<StoredU32>,
|
||||
pub days_before_next_halving: ComputedFromHeightLast<StoredF32>,
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub epoch: ComputedFromHeightLast<HalvingEpoch, M>,
|
||||
pub blocks_before_next_halving: ComputedFromHeightLast<StoredU32, M>,
|
||||
pub days_before_next_halving: ComputedFromHeightLast<StoredF32, M>,
|
||||
}
|
||||
|
||||
@@ -6,20 +6,19 @@ use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use vecdb::{Database, PAGE_SIZE};
|
||||
|
||||
use crate::{indexes, price};
|
||||
use crate::indexes;
|
||||
|
||||
use super::{
|
||||
CountVecs, DifficultyVecs, HalvingVecs, IntervalVecs, MiningVecs, RewardsVecs, SizeVecs,
|
||||
CountVecs, DifficultyVecs, HalvingVecs, IntervalVecs, SizeVecs,
|
||||
TimeVecs, Vecs, WeightVecs,
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
parent_path: &Path,
|
||||
parent_version: Version,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
) -> Result<Self> {
|
||||
let db = Database::open(&parent_path.join(super::DB_NAME))?;
|
||||
db.set_min_len(PAGE_SIZE * 50_000_000)?;
|
||||
@@ -27,12 +26,10 @@ impl Vecs {
|
||||
let version = parent_version;
|
||||
|
||||
let count = CountVecs::forced_import(&db, version, indexes)?;
|
||||
let interval = IntervalVecs::forced_import(&db, version, indexer, indexes)?;
|
||||
let interval = IntervalVecs::forced_import(&db, version, indexes)?;
|
||||
let size = SizeVecs::forced_import(&db, version, indexer, indexes)?;
|
||||
let weight = WeightVecs::forced_import(&db, version, indexer, indexes)?;
|
||||
let time = TimeVecs::forced_import(&db, version, indexer, indexes)?;
|
||||
let mining = MiningVecs::forced_import(&db, version, indexes)?;
|
||||
let rewards = RewardsVecs::forced_import(&db, version, indexes, price)?;
|
||||
let difficulty = DifficultyVecs::forced_import(&db, version, indexer, indexes)?;
|
||||
let halving = HalvingVecs::forced_import(&db, version, indexes)?;
|
||||
|
||||
@@ -43,8 +40,6 @@ impl Vecs {
|
||||
size,
|
||||
weight,
|
||||
time,
|
||||
mining,
|
||||
rewards,
|
||||
difficulty,
|
||||
halving,
|
||||
};
|
||||
|
||||
@@ -1,18 +1,36 @@
|
||||
use brk_error::Result;
|
||||
use vecdb::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{CheckedSub, Timestamp};
|
||||
use vecdb::{Exit, ReadableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, indexes};
|
||||
use crate::ComputeIndexes;
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
indexer: &Indexer,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.interval.derive_from(indexes, starting_indexes, exit)?;
|
||||
|
||||
let mut prev_timestamp = None;
|
||||
self.interval.height.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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,21 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{CheckedSub, Height, Timestamp, Version};
|
||||
use vecdb::{Database, VecIndex};
|
||||
use brk_types::Version;
|
||||
use vecdb::Database;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{indexes, internal::LazyFromHeightDistribution};
|
||||
use crate::{indexes, internal::ComputedFromHeightDistribution};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let interval = LazyFromHeightDistribution::forced_import_with_init(
|
||||
let interval = ComputedFromHeightDistribution::forced_import(
|
||||
db,
|
||||
"block_interval",
|
||||
version,
|
||||
indexer.vecs.blocks.timestamp.clone(),
|
||||
indexes,
|
||||
|height: Height, timestamp_iter| {
|
||||
let timestamp = timestamp_iter.get_at(height.to_usize())?;
|
||||
let interval = height.decremented().map_or(Timestamp::ZERO, |prev_h| {
|
||||
timestamp_iter
|
||||
.get_at(prev_h.to_usize())
|
||||
.map_or(Timestamp::ZERO, |prev_t| {
|
||||
timestamp.checked_sub(prev_t).unwrap_or(Timestamp::ZERO)
|
||||
})
|
||||
});
|
||||
Some(interval)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(Self { interval })
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Timestamp;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::LazyFromHeightDistribution;
|
||||
use crate::internal::ComputedFromHeightDistribution;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub interval: LazyFromHeightDistribution<Timestamp>,
|
||||
pub interval: ComputedFromHeightDistribution<Timestamp, M>,
|
||||
}
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{StoredF32, StoredF64};
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::super::{ONE_TERA_HASH, TARGET_BLOCKS_PER_DAY_F64, count, difficulty, rewards};
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, indexes, traits::ComputeDrawdown};
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
count_vecs: &count::Vecs,
|
||||
difficulty_vecs: &difficulty::Vecs,
|
||||
rewards_vecs: &rewards::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.hash_rate
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&count_vecs._24h_block_count.height,
|
||||
&difficulty_vecs.as_hash.height,
|
||||
|(i, block_count_sum, difficulty_as_hash, ..)| {
|
||||
(
|
||||
i,
|
||||
StoredF64::from(
|
||||
(f64::from(block_count_sum) / TARGET_BLOCKS_PER_DAY_F64)
|
||||
* f64::from(difficulty_as_hash),
|
||||
),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_rate_1w_sma
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_sma(
|
||||
starting_indexes.dateindex,
|
||||
self.hash_rate.dateindex.inner(),
|
||||
7,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_rate_1m_sma
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_sma(
|
||||
starting_indexes.dateindex,
|
||||
self.hash_rate.dateindex.inner(),
|
||||
30,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_rate_2m_sma
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_sma(
|
||||
starting_indexes.dateindex,
|
||||
self.hash_rate.dateindex.inner(),
|
||||
2 * 30,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_rate_1y_sma
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_sma(
|
||||
starting_indexes.dateindex,
|
||||
self.hash_rate.dateindex.inner(),
|
||||
365,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_rate_ath
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_all_time_high(
|
||||
starting_indexes.height,
|
||||
&self.hash_rate.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_rate_drawdown
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_drawdown(
|
||||
starting_indexes.height,
|
||||
&self.hash_rate.height,
|
||||
&self.hash_rate_ath.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_price_ths
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_transform2(
|
||||
starting_indexes.height,
|
||||
rewards_vecs._24h_coinbase_sum.dollars.as_ref().unwrap(),
|
||||
&self.hash_rate.height,
|
||||
|(i, coinbase_sum, hashrate, ..)| {
|
||||
let hashrate_ths = *hashrate / ONE_TERA_HASH;
|
||||
let price = if hashrate_ths == 0.0 {
|
||||
StoredF32::NAN
|
||||
} else {
|
||||
(*coinbase_sum / hashrate_ths).into()
|
||||
};
|
||||
(i, price)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_price_phs
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.hash_price_ths.height,
|
||||
|(i, price, ..)| (i, (*price * 1000.0).into()),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_value_ths
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&rewards_vecs._24h_coinbase_sum.sats,
|
||||
&self.hash_rate.height,
|
||||
|(i, coinbase_sum, hashrate, ..)| {
|
||||
let hashrate_ths = *hashrate / ONE_TERA_HASH;
|
||||
let value = if hashrate_ths == 0.0 {
|
||||
StoredF32::NAN
|
||||
} else {
|
||||
StoredF32::from(*coinbase_sum as f64 / hashrate_ths)
|
||||
};
|
||||
(i, value)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_value_phs
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.hash_value_ths.height,
|
||||
|(i, value, ..)| (i, (*value * 1000.0).into()),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_price_ths_min
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_all_time_low_(
|
||||
starting_indexes.height,
|
||||
&self.hash_price_ths.height,
|
||||
exit,
|
||||
true,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_price_phs_min
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_all_time_low_(
|
||||
starting_indexes.height,
|
||||
&self.hash_price_phs.height,
|
||||
exit,
|
||||
true,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_value_ths_min
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_all_time_low_(
|
||||
starting_indexes.height,
|
||||
&self.hash_value_ths.height,
|
||||
exit,
|
||||
true,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_value_phs_min
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_all_time_low_(
|
||||
starting_indexes.height,
|
||||
&self.hash_value_phs.height,
|
||||
exit,
|
||||
true,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_price_rebound
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_percentage_difference(
|
||||
starting_indexes.height,
|
||||
&self.hash_price_phs.height,
|
||||
&self.hash_price_phs_min.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_value_rebound
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_percentage_difference(
|
||||
starting_indexes.height,
|
||||
&self.hash_value_phs.height,
|
||||
&self.hash_value_phs_min.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{StoredF32, StoredF64};
|
||||
|
||||
use crate::internal::{ComputedFromHeightLast, ComputedFromDateLast};
|
||||
|
||||
/// Mining-related metrics: hash rate, hash price, hash value
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub hash_rate: ComputedFromHeightLast<StoredF64>,
|
||||
pub hash_rate_1w_sma: ComputedFromDateLast<StoredF64>,
|
||||
pub hash_rate_1m_sma: ComputedFromDateLast<StoredF32>,
|
||||
pub hash_rate_2m_sma: ComputedFromDateLast<StoredF32>,
|
||||
pub hash_rate_1y_sma: ComputedFromDateLast<StoredF32>,
|
||||
pub hash_rate_ath: ComputedFromHeightLast<StoredF64>,
|
||||
pub hash_rate_drawdown: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_price_ths: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_price_ths_min: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_price_phs: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_price_phs_min: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_price_rebound: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_value_ths: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_value_ths_min: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_value_phs: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_value_phs_min: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_value_rebound: ComputedFromHeightLast<StoredF32>,
|
||||
}
|
||||
@@ -2,8 +2,6 @@ pub mod count;
|
||||
pub mod difficulty;
|
||||
pub mod halving;
|
||||
pub mod interval;
|
||||
pub mod mining;
|
||||
pub mod rewards;
|
||||
pub mod size;
|
||||
pub mod time;
|
||||
pub mod weight;
|
||||
@@ -12,14 +10,12 @@ mod compute;
|
||||
mod import;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use vecdb::Database;
|
||||
use vecdb::{Database, Rw, StorageMode};
|
||||
|
||||
pub use count::Vecs as CountVecs;
|
||||
pub use difficulty::Vecs as DifficultyVecs;
|
||||
pub use halving::Vecs as HalvingVecs;
|
||||
pub use interval::Vecs as IntervalVecs;
|
||||
pub use mining::Vecs as MiningVecs;
|
||||
pub use rewards::Vecs as RewardsVecs;
|
||||
pub use size::Vecs as SizeVecs;
|
||||
pub use time::Vecs as TimeVecs;
|
||||
pub use weight::Vecs as WeightVecs;
|
||||
@@ -28,29 +24,36 @@ pub const DB_NAME: &str = "blocks";
|
||||
|
||||
pub(crate) const TARGET_BLOCKS_PER_DAY_F64: f64 = 144.0;
|
||||
pub(crate) const TARGET_BLOCKS_PER_DAY_F32: f32 = 144.0;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MINUTE1: u64 = 0;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MINUTE5: u64 = 0;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MINUTE10: u64 = 1;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MINUTE30: u64 = 3;
|
||||
pub(crate) const TARGET_BLOCKS_PER_HOUR1: u64 = 6;
|
||||
pub(crate) const TARGET_BLOCKS_PER_HOUR4: u64 = 24;
|
||||
pub(crate) const TARGET_BLOCKS_PER_HOUR12: u64 = 72;
|
||||
pub(crate) const TARGET_BLOCKS_PER_DAY: u64 = 144;
|
||||
pub(crate) const TARGET_BLOCKS_PER_DAY3: u64 = 3 * TARGET_BLOCKS_PER_DAY;
|
||||
pub(crate) const TARGET_BLOCKS_PER_WEEK: u64 = 7 * TARGET_BLOCKS_PER_DAY;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MONTH: u64 = 30 * TARGET_BLOCKS_PER_DAY;
|
||||
pub(crate) const TARGET_BLOCKS_PER_QUARTER: u64 = 3 * TARGET_BLOCKS_PER_MONTH;
|
||||
pub(crate) const TARGET_BLOCKS_PER_SEMESTER: u64 = 2 * TARGET_BLOCKS_PER_QUARTER;
|
||||
pub(crate) const TARGET_BLOCKS_PER_YEAR: u64 = 2 * TARGET_BLOCKS_PER_SEMESTER;
|
||||
pub(crate) const TARGET_BLOCKS_PER_DECADE: u64 = 10 * TARGET_BLOCKS_PER_YEAR;
|
||||
pub(crate) const TARGET_BLOCKS_PER_HALVING: u64 = 210_000;
|
||||
pub(crate) const ONE_TERA_HASH: f64 = 1_000_000_000_000.0;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
#[traversable(skip)]
|
||||
pub(crate) db: Database,
|
||||
|
||||
pub count: CountVecs,
|
||||
pub interval: IntervalVecs,
|
||||
pub count: CountVecs<M>,
|
||||
pub interval: IntervalVecs<M>,
|
||||
#[traversable(flatten)]
|
||||
pub size: SizeVecs,
|
||||
pub size: SizeVecs<M>,
|
||||
#[traversable(flatten)]
|
||||
pub weight: WeightVecs,
|
||||
pub time: TimeVecs,
|
||||
pub mining: MiningVecs,
|
||||
pub rewards: RewardsVecs,
|
||||
pub difficulty: DifficultyVecs,
|
||||
pub halving: HalvingVecs,
|
||||
pub weight: WeightVecs<M>,
|
||||
pub time: TimeVecs<M>,
|
||||
pub difficulty: DifficultyVecs<M>,
|
||||
pub halving: HalvingVecs<M>,
|
||||
}
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{CheckedSub, Dollars, HalvingEpoch, Height, Sats, StoredF32, TxOutIndex};
|
||||
use vecdb::{Exit, IterableVec, TypedVecIterator, VecIndex};
|
||||
|
||||
use super::super::count;
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, indexes, transactions};
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
count_vecs: &count::Vecs,
|
||||
transactions_fees: &transactions::FeesVecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.coinbase
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
let mut txindex_to_first_txoutindex_iter =
|
||||
indexer.vecs.transactions.first_txoutindex.iter()?;
|
||||
let mut txindex_to_output_count_iter =
|
||||
indexes.txindex.output_count.iter();
|
||||
let mut txoutindex_to_value_iter = indexer.vecs.outputs.value.iter()?;
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
|(height, txindex, ..)| {
|
||||
let first_txoutindex = txindex_to_first_txoutindex_iter
|
||||
.get_unwrap(txindex)
|
||||
.to_usize();
|
||||
let output_count = txindex_to_output_count_iter.get_unwrap(txindex);
|
||||
let mut sats = Sats::ZERO;
|
||||
(first_txoutindex..first_txoutindex + usize::from(output_count)).for_each(
|
||||
|txoutindex| {
|
||||
sats += txoutindex_to_value_iter
|
||||
.get_unwrap(TxOutIndex::from(txoutindex));
|
||||
},
|
||||
);
|
||||
(height, sats)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let mut height_to_coinbase_iter = self.coinbase.sats.height.into_iter();
|
||||
self._24h_coinbase_sum.sats.compute_transform(
|
||||
starting_indexes.height,
|
||||
&count_vecs._24h_block_count.height,
|
||||
|(h, count, ..)| {
|
||||
let range = *h - (*count - 1)..=*h;
|
||||
let sum = range
|
||||
.map(Height::from)
|
||||
.map(|h| height_to_coinbase_iter.get_unwrap(h))
|
||||
.sum::<Sats>();
|
||||
(h, sum)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
drop(height_to_coinbase_iter);
|
||||
|
||||
if let (Some(dollars_out), Some(dollars_in)) =
|
||||
(&mut self._24h_coinbase_sum.dollars, &self.coinbase.dollars)
|
||||
{
|
||||
let mut height_to_coinbase_iter = dollars_in.height.into_iter();
|
||||
dollars_out.compute_transform(
|
||||
starting_indexes.height,
|
||||
&count_vecs._24h_block_count.height,
|
||||
|(h, count, ..)| {
|
||||
let range = *h - (*count - 1)..=*h;
|
||||
let sum = range
|
||||
.map(Height::from)
|
||||
.map(|h| height_to_coinbase_iter.get_unwrap(h))
|
||||
.sum::<Dollars>();
|
||||
(h, sum)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
self.subsidy
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.coinbase.sats.height,
|
||||
&transactions_fees.fee.sats.height.sum_cum.sum.0,
|
||||
|(height, coinbase, fees, ..)| {
|
||||
(
|
||||
height,
|
||||
coinbase.checked_sub(fees).unwrap_or_else(|| {
|
||||
dbg!(height, coinbase, fees);
|
||||
panic!()
|
||||
}),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.unclaimed_rewards
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.subsidy.sats.height,
|
||||
|(height, subsidy, ..)| {
|
||||
let halving = HalvingEpoch::from(height);
|
||||
let expected = Sats::FIFTY_BTC / 2_usize.pow(halving.to_usize() as u32);
|
||||
(height, expected.checked_sub(subsidy).unwrap())
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.fee_dominance.compute_transform2(
|
||||
starting_indexes.dateindex,
|
||||
&transactions_fees.fee.sats.dateindex.sum_cum.sum.0,
|
||||
&self.coinbase.sats.dateindex.sum_cum.sum.0,
|
||||
|(i, fee, coinbase, ..)| {
|
||||
let coinbase_f64 = u64::from(coinbase) as f64;
|
||||
let dominance = if coinbase_f64 == 0.0 {
|
||||
StoredF32::NAN
|
||||
} else {
|
||||
StoredF32::from(u64::from(fee) as f64 / coinbase_f64 * 100.0)
|
||||
};
|
||||
(i, dominance)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.subsidy_dominance.compute_transform2(
|
||||
starting_indexes.dateindex,
|
||||
&self.subsidy.sats.dateindex.sum_cum.sum.0,
|
||||
&self.coinbase.sats.dateindex.sum_cum.sum.0,
|
||||
|(i, subsidy, coinbase, ..)| {
|
||||
let coinbase_f64 = u64::from(coinbase) as f64;
|
||||
let dominance = if coinbase_f64 == 0.0 {
|
||||
StoredF32::NAN
|
||||
} else {
|
||||
StoredF32::from(u64::from(subsidy) as f64 / coinbase_f64 * 100.0)
|
||||
};
|
||||
(i, dominance)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
if let Some(sma) = self.subsidy_usd_1y_sma.as_mut() {
|
||||
let date_to_coinbase_usd_sum = &self
|
||||
.coinbase
|
||||
.dollars
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.dateindex
|
||||
.sum_cum
|
||||
.sum
|
||||
.0;
|
||||
|
||||
sma.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_sma(
|
||||
starting_indexes.dateindex,
|
||||
date_to_coinbase_usd_sum,
|
||||
365,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::Version;
|
||||
use vecdb::{Database, EagerVec, ImportableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromDateLast, ValueFromHeightFull, ValueHeight, ValueFromHeightSumCum},
|
||||
price,
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
) -> Result<Self> {
|
||||
let compute_dollars = price.is_some();
|
||||
|
||||
Ok(Self {
|
||||
_24h_coinbase_sum: ValueHeight::forced_import(
|
||||
db,
|
||||
"24h_coinbase_sum",
|
||||
version,
|
||||
compute_dollars,
|
||||
)?,
|
||||
coinbase: ValueFromHeightFull::forced_import(db, "coinbase", version, indexes, price)?,
|
||||
subsidy: ValueFromHeightFull::forced_import(db, "subsidy", version, indexes, price)?,
|
||||
unclaimed_rewards: ValueFromHeightSumCum::forced_import(
|
||||
db,
|
||||
"unclaimed_rewards",
|
||||
version,
|
||||
indexes,
|
||||
price,
|
||||
)?,
|
||||
fee_dominance: EagerVec::forced_import(db, "fee_dominance", version)?,
|
||||
subsidy_dominance: EagerVec::forced_import(db, "subsidy_dominance", version)?,
|
||||
subsidy_usd_1y_sma: compute_dollars
|
||||
.then(|| {
|
||||
ComputedFromDateLast::forced_import(db, "subsidy_usd_1y_sma", version, indexes)
|
||||
})
|
||||
.transpose()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{DateIndex, Dollars, StoredF32};
|
||||
use vecdb::{EagerVec, PcoVec};
|
||||
|
||||
use crate::internal::{ComputedFromDateLast, ValueFromHeightFull, ValueHeight, ValueFromHeightSumCum};
|
||||
|
||||
/// Coinbase/subsidy/rewards metrics
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub _24h_coinbase_sum: ValueHeight,
|
||||
pub coinbase: ValueFromHeightFull,
|
||||
pub subsidy: ValueFromHeightFull,
|
||||
pub unclaimed_rewards: ValueFromHeightSumCum,
|
||||
pub fee_dominance: EagerVec<PcoVec<DateIndex, StoredF32>>,
|
||||
pub subsidy_dominance: EagerVec<PcoVec<DateIndex, StoredF32>>,
|
||||
pub subsidy_usd_1y_sma: Option<ComputedFromDateLast<Dollars>>,
|
||||
}
|
||||
@@ -3,24 +3,19 @@ use brk_indexer::Indexer;
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, indexes};
|
||||
use crate::ComputeIndexes;
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.size.derive_from(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
&indexer.vecs.blocks.total_size,
|
||||
exit,
|
||||
)?;
|
||||
self.size
|
||||
.compute_cumulative(starting_indexes, &indexer.vecs.blocks.total_size, exit)?;
|
||||
|
||||
self.vbytes.derive_from(indexes, starting_indexes, exit)?;
|
||||
self.vbytes.compute_cumulative(starting_indexes, exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,35 +1,30 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{Height, StoredU64, Version};
|
||||
use vecdb::{Database, IterableCloneableVec, VecIndex};
|
||||
use brk_types::Version;
|
||||
use vecdb::{Database, ReadableCloneableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{indexes, internal::{ComputedHeightDerivedFull, LazyComputedFromHeightFull}};
|
||||
use crate::{indexes, internal::{ComputedHeightDerivedFull, LazyComputedFromHeightFull, WeightToVbytes}};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
vbytes: LazyComputedFromHeightFull::forced_import_with_init(
|
||||
vbytes: LazyComputedFromHeightFull::forced_import::<WeightToVbytes>(
|
||||
db,
|
||||
"block_vbytes",
|
||||
version,
|
||||
indexer.vecs.blocks.weight.clone(),
|
||||
&indexer.vecs.blocks.weight,
|
||||
indexes,
|
||||
|height: Height, weight_iter| {
|
||||
weight_iter
|
||||
.get_at(height.to_usize())
|
||||
.map(|w| StoredU64::from(w.to_vbytes_floor()))
|
||||
},
|
||||
)?,
|
||||
size: ComputedHeightDerivedFull::forced_import(
|
||||
db,
|
||||
"block_size",
|
||||
indexer.vecs.blocks.total_size.boxed_clone(),
|
||||
indexer.vecs.blocks.total_size.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{StoredU64, Weight};
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedHeightDerivedFull, LazyComputedFromHeightFull};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub vbytes: LazyComputedFromHeightFull<StoredU64, Weight>,
|
||||
pub size: ComputedHeightDerivedFull<StoredU64>,
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub vbytes: LazyComputedFromHeightFull<StoredU64, Weight, M>,
|
||||
pub size: ComputedHeightDerivedFull<StoredU64, M>,
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::Timestamp;
|
||||
use vecdb::{Exit, TypedVecIterator};
|
||||
use vecdb::{Exit, ReadableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, indexes};
|
||||
|
||||
impl Vecs {
|
||||
/// Compute height-to-time fields early, before indexes are computed.
|
||||
/// These are needed by indexes::block to compute height_to_dateindex.
|
||||
pub fn compute_early(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
starting_height: brk_types::Height,
|
||||
@@ -19,15 +15,11 @@ impl Vecs {
|
||||
self.timestamp_monotonic.compute_transform(
|
||||
starting_height,
|
||||
&indexer.vecs.blocks.timestamp,
|
||||
|(h, timestamp, height_to_timestamp_monotonic_iter)| {
|
||||
|(h, timestamp, this)| {
|
||||
if prev_timestamp_monotonic.is_none()
|
||||
&& let Some(prev_h) = h.decremented()
|
||||
{
|
||||
prev_timestamp_monotonic.replace(
|
||||
height_to_timestamp_monotonic_iter
|
||||
.into_iter()
|
||||
.get_unwrap(prev_h),
|
||||
);
|
||||
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));
|
||||
@@ -39,23 +31,4 @@ impl Vecs {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.timestamp.compute_all(|vec| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.dateindex,
|
||||
&indexes.dateindex.date,
|
||||
|(di, d, ..)| (di, Timestamp::from(d)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{Date, Height, Version};
|
||||
use vecdb::{Database, EagerVec, ImportableVec, IterableCloneableVec, LazyVecFrom1};
|
||||
use vecdb::{Database, EagerVec, ImportableVec, ReadableCloneableVec, LazyVecFrom1};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{indexes, internal::ComputedHeightDerivedFirst};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexer: &Indexer,
|
||||
@@ -20,17 +20,16 @@ impl Vecs {
|
||||
date: LazyVecFrom1::init(
|
||||
"date",
|
||||
version,
|
||||
timestamp_monotonic.boxed_clone(),
|
||||
|height: Height, timestamp_iter| timestamp_iter.get(height).map(Date::from),
|
||||
timestamp_monotonic.read_only_boxed_clone(),
|
||||
|_height: Height, timestamp| Date::from(timestamp),
|
||||
),
|
||||
timestamp_monotonic,
|
||||
timestamp: ComputedHeightDerivedFirst::forced_import(
|
||||
db,
|
||||
"timestamp",
|
||||
indexer.vecs.blocks.timestamp.boxed_clone(),
|
||||
indexer.vecs.blocks.timestamp.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Date, Height, Timestamp};
|
||||
use vecdb::{EagerVec, LazyVecFrom1, PcoVec};
|
||||
use vecdb::{EagerVec, LazyVecFrom1, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::ComputedHeightDerivedFirst;
|
||||
|
||||
/// Timestamp and date metrics for blocks
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub date: LazyVecFrom1<Height, Date, Height, Timestamp>,
|
||||
pub timestamp_monotonic: EagerVec<PcoVec<Height, Timestamp>>,
|
||||
pub timestamp_monotonic: M::Stored<EagerVec<PcoVec<Height, Timestamp>>>,
|
||||
pub timestamp: ComputedHeightDerivedFirst<Timestamp>,
|
||||
}
|
||||
|
||||
@@ -3,18 +3,17 @@ use brk_indexer::Indexer;
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, indexes};
|
||||
use crate::ComputeIndexes;
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.weight
|
||||
.derive_from(indexes, starting_indexes, &indexer.vecs.blocks.weight, exit)?;
|
||||
.compute_cumulative(starting_indexes, &indexer.vecs.blocks.weight, exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::Version;
|
||||
use vecdb::{Database, IterableCloneableVec};
|
||||
use vecdb::{Database, ReadableCloneableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexer: &Indexer,
|
||||
@@ -19,7 +19,7 @@ impl Vecs {
|
||||
let weight = ComputedHeightDerivedFull::forced_import(
|
||||
db,
|
||||
"block_weight",
|
||||
indexer.vecs.blocks.weight.boxed_clone(),
|
||||
indexer.vecs.blocks.weight.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
)?;
|
||||
@@ -27,7 +27,7 @@ impl Vecs {
|
||||
let fullness = LazyFromHeightTransformDistribution::from_derived::<WeightToFullness>(
|
||||
"block_fullness",
|
||||
version,
|
||||
indexer.vecs.blocks.weight.boxed_clone(),
|
||||
indexer.vecs.blocks.weight.read_only_boxed_clone(),
|
||||
&weight,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{StoredF32, Weight};
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedHeightDerivedFull, LazyFromHeightTransformDistribution};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub weight: ComputedHeightDerivedFull<Weight>,
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub weight: ComputedHeightDerivedFull<Weight, M>,
|
||||
pub fullness: LazyFromHeightTransformDistribution<StoredF32, Weight>,
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{Bitcoin, CheckedSub, StoredF64};
|
||||
use vecdb::{Exit, TypedVecIterator};
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, distribution, indexes};
|
||||
use crate::{ComputeIndexes, distribution};
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
distribution: &distribution::Vecs,
|
||||
exit: &Exit,
|
||||
@@ -23,7 +22,7 @@ impl Vecs {
|
||||
.height;
|
||||
|
||||
self.coinblocks_created
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
.compute(starting_indexes, exit, |vec| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
circulating_supply,
|
||||
@@ -41,52 +40,37 @@ impl Vecs {
|
||||
.coinblocks_destroyed;
|
||||
|
||||
self.coinblocks_stored
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
let mut coinblocks_destroyed_iter = coinblocks_destroyed.height.into_iter();
|
||||
vec.compute_transform(
|
||||
.compute(starting_indexes, exit, |vec| {
|
||||
vec.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.coinblocks_created.height,
|
||||
|(i, created, ..)| {
|
||||
let destroyed = coinblocks_destroyed_iter.get_unwrap(i);
|
||||
(i, created.checked_sub(destroyed).unwrap())
|
||||
},
|
||||
&coinblocks_destroyed.height,
|
||||
|(i, created, destroyed, ..)| (i, created.checked_sub(destroyed).unwrap()),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.liveliness
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
coinblocks_destroyed.height_cumulative.inner(),
|
||||
self.coinblocks_created.height_cumulative.inner(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.liveliness.height.compute_divide(
|
||||
starting_indexes.height,
|
||||
&*coinblocks_destroyed.height_cumulative,
|
||||
&*self.coinblocks_created.height_cumulative,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.vaultedness
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.liveliness.height,
|
||||
|(i, v, ..)| (i, StoredF64::from(1.0).checked_sub(v).unwrap()),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.vaultedness.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.liveliness.height,
|
||||
|(i, v, ..)| (i, StoredF64::from(1.0).checked_sub(v).unwrap()),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.activity_to_vaultedness_ratio
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
&self.liveliness.height,
|
||||
&self.vaultedness.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.activity_to_vaultedness_ratio.height.compute_divide(
|
||||
starting_indexes.height,
|
||||
&self.liveliness.height,
|
||||
&self.vaultedness.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
Ok(Self {
|
||||
coinblocks_created: ComputedFromHeightSumCum::forced_import(
|
||||
db,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::StoredF64;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedFromHeightLast, ComputedFromHeightSumCum};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub coinblocks_created: ComputedFromHeightSumCum<StoredF64>,
|
||||
pub coinblocks_stored: ComputedFromHeightSumCum<StoredF64>,
|
||||
pub liveliness: ComputedFromHeightLast<StoredF64>,
|
||||
pub vaultedness: ComputedFromHeightLast<StoredF64>,
|
||||
pub activity_to_vaultedness_ratio: ComputedFromHeightLast<StoredF64>,
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub coinblocks_created: ComputedFromHeightSumCum<StoredF64, M>,
|
||||
pub coinblocks_stored: ComputedFromHeightSumCum<StoredF64, M>,
|
||||
pub liveliness: ComputedFromHeightLast<StoredF64, M>,
|
||||
pub vaultedness: ComputedFromHeightLast<StoredF64, M>,
|
||||
pub activity_to_vaultedness_ratio: ComputedFromHeightLast<StoredF64, M>,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{StoredF32, StoredF64};
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::super::activity;
|
||||
@@ -6,48 +7,40 @@ use super::Vecs;
|
||||
use crate::{ComputeIndexes, supply};
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
supply: &supply::Vecs,
|
||||
activity: &activity::Vecs,
|
||||
has_price: bool,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.cointime_adj_inflation_rate
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_multiply(
|
||||
starting_indexes.dateindex,
|
||||
activity.activity_to_vaultedness_ratio.dateindex.inner(),
|
||||
&supply.inflation.dateindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.cointime_adj_inflation_rate.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&activity.activity_to_vaultedness_ratio.height,
|
||||
&supply.inflation.height,
|
||||
|(h, ratio, inflation, ..)| (h, StoredF32::from((*ratio) * f64::from(*inflation))),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.cointime_adj_tx_btc_velocity
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_multiply(
|
||||
starting_indexes.dateindex,
|
||||
activity.activity_to_vaultedness_ratio.dateindex.inner(),
|
||||
&supply.velocity.btc.dateindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
.height
|
||||
.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&activity.activity_to_vaultedness_ratio.height,
|
||||
&supply.velocity.btc.height,
|
||||
|(h, ratio, vel, ..)| (h, StoredF64::from(*ratio * *vel)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
if has_price {
|
||||
self.cointime_adj_tx_usd_velocity
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_multiply(
|
||||
starting_indexes.dateindex,
|
||||
activity.activity_to_vaultedness_ratio.dateindex.inner(),
|
||||
&supply.velocity.usd.as_ref().unwrap().dateindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
self.cointime_adj_tx_usd_velocity
|
||||
.height
|
||||
.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&activity.activity_to_vaultedness_ratio.height,
|
||||
&supply.velocity.usd.height,
|
||||
|(h, ratio, vel, ..)| (h, StoredF64::from(*ratio * *vel)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,24 +3,24 @@ use brk_types::Version;
|
||||
use vecdb::Database;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{indexes, internal::ComputedFromDateLast};
|
||||
use crate::{indexes, internal::ComputedFromHeightLast};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
Ok(Self {
|
||||
cointime_adj_inflation_rate: ComputedFromDateLast::forced_import(
|
||||
cointime_adj_inflation_rate: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"cointime_adj_inflation_rate",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
cointime_adj_tx_btc_velocity: ComputedFromDateLast::forced_import(
|
||||
cointime_adj_tx_btc_velocity: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"cointime_adj_tx_btc_velocity",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
cointime_adj_tx_usd_velocity: ComputedFromDateLast::forced_import(
|
||||
cointime_adj_tx_usd_velocity: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"cointime_adj_tx_usd_velocity",
|
||||
version,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{StoredF32, StoredF64};
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::ComputedFromDateLast;
|
||||
use crate::internal::ComputedFromHeightLast;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub cointime_adj_inflation_rate: ComputedFromDateLast<StoredF32>,
|
||||
pub cointime_adj_tx_btc_velocity: ComputedFromDateLast<StoredF64>,
|
||||
pub cointime_adj_tx_usd_velocity: ComputedFromDateLast<StoredF64>,
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub cointime_adj_inflation_rate: ComputedFromHeightLast<StoredF32, M>,
|
||||
pub cointime_adj_tx_btc_velocity: ComputedFromHeightLast<StoredF64, M>,
|
||||
pub cointime_adj_tx_usd_velocity: ComputedFromHeightLast<StoredF64, M>,
|
||||
}
|
||||
|
||||
@@ -4,15 +4,14 @@ use vecdb::Exit;
|
||||
|
||||
use super::super::{activity, value};
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, blocks, distribution, indexes, utils::OptionExt};
|
||||
use crate::{ComputeIndexes, distribution, mining};
|
||||
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
blocks: &blocks::Vecs,
|
||||
mining: &mining::Vecs,
|
||||
distribution: &distribution::Vecs,
|
||||
activity: &activity::Vecs,
|
||||
value: &value::Vecs,
|
||||
@@ -23,7 +22,6 @@ impl Vecs {
|
||||
.all
|
||||
.metrics
|
||||
.realized
|
||||
.u()
|
||||
.realized_cap
|
||||
.height;
|
||||
|
||||
@@ -33,78 +31,51 @@ impl Vecs {
|
||||
.metrics
|
||||
.supply
|
||||
.total
|
||||
.bitcoin
|
||||
.btc
|
||||
.height;
|
||||
|
||||
self.thermo_cap
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&blocks
|
||||
.rewards
|
||||
.subsidy
|
||||
.dollars
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.height_cumulative
|
||||
.0,
|
||||
|(i, v, ..)| (i, v),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.thermo_cap.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&*mining.rewards.subsidy.usd.height_cumulative,
|
||||
|(i, v, ..)| (i, v),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.investor_cap
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_subtract(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
&self.thermo_cap.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.investor_cap.height.compute_subtract(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
&self.thermo_cap.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.vaulted_cap
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
&activity.vaultedness.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.vaulted_cap.height.compute_divide(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
&activity.vaultedness.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.active_cap
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
&activity.liveliness.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.active_cap.height.compute_multiply(
|
||||
starting_indexes.height,
|
||||
realized_cap,
|
||||
&activity.liveliness.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// cointime_cap = (cointime_value_destroyed_cumulative * circulating_supply) / coinblocks_stored_cumulative
|
||||
self.cointime_cap
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_transform3(
|
||||
starting_indexes.height,
|
||||
value.cointime_value_destroyed.height_cumulative.inner(),
|
||||
circulating_supply,
|
||||
activity.coinblocks_stored.height_cumulative.inner(),
|
||||
|(i, destroyed, supply, stored, ..)| {
|
||||
let destroyed: f64 = *destroyed;
|
||||
let supply: f64 = supply.into();
|
||||
let stored: f64 = *stored;
|
||||
(i, Dollars::from(destroyed * supply / stored))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.cointime_cap.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&value.cointime_value_destroyed.height_cumulative.0,
|
||||
circulating_supply,
|
||||
&activity.coinblocks_stored.height_cumulative.0,
|
||||
|(i, destroyed, supply, stored, ..)| {
|
||||
let destroyed: f64 = *destroyed;
|
||||
let supply: f64 = supply.into();
|
||||
let stored: f64 = *stored;
|
||||
(i, Dollars::from(destroyed * supply / stored))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use super::Vecs;
|
||||
use crate::{indexes, internal::ComputedFromHeightLast};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
Ok(Self {
|
||||
thermo_cap: ComputedFromHeightLast::forced_import(db, "thermo_cap", version, indexes)?,
|
||||
investor_cap: ComputedFromHeightLast::forced_import(db, "investor_cap", version, indexes)?,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Dollars;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::ComputedFromHeightLast;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub thermo_cap: ComputedFromHeightLast<Dollars>,
|
||||
pub investor_cap: ComputedFromHeightLast<Dollars>,
|
||||
pub vaulted_cap: ComputedFromHeightLast<Dollars>,
|
||||
pub active_cap: ComputedFromHeightLast<Dollars>,
|
||||
pub cointime_cap: ComputedFromHeightLast<Dollars>,
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub thermo_cap: ComputedFromHeightLast<Dollars, M>,
|
||||
pub investor_cap: ComputedFromHeightLast<Dollars, M>,
|
||||
pub vaulted_cap: ComputedFromHeightLast<Dollars, M>,
|
||||
pub active_cap: ComputedFromHeightLast<Dollars, M>,
|
||||
pub cointime_cap: ComputedFromHeightLast<Dollars, M>,
|
||||
}
|
||||
|
||||
@@ -2,27 +2,26 @@ use brk_error::Result;
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{blocks, distribution, indexes, price, supply, ComputeIndexes};
|
||||
use crate::{ComputeIndexes, blocks, distribution, mining, prices, supply};
|
||||
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
price: Option<&price::Vecs>,
|
||||
prices: &prices::Vecs,
|
||||
blocks: &blocks::Vecs,
|
||||
mining: &mining::Vecs,
|
||||
supply_vecs: &supply::Vecs,
|
||||
distribution: &distribution::Vecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// Activity computes first (liveliness, vaultedness, etc.)
|
||||
self.activity
|
||||
.compute(indexes, starting_indexes, distribution, exit)?;
|
||||
.compute(starting_indexes, distribution, exit)?;
|
||||
|
||||
// Supply computes next (depends on activity)
|
||||
self.supply.compute(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
distribution,
|
||||
&self.activity,
|
||||
@@ -30,57 +29,43 @@ impl Vecs {
|
||||
)?;
|
||||
|
||||
// Adjusted velocity metrics (BTC) - can compute without price
|
||||
self.adjusted.compute(
|
||||
self.adjusted
|
||||
.compute(starting_indexes, supply_vecs, &self.activity, exit)?;
|
||||
|
||||
// Value computes (cointime value destroyed/created/stored, VOCDD)
|
||||
self.value.compute(
|
||||
starting_indexes,
|
||||
supply_vecs,
|
||||
prices,
|
||||
distribution,
|
||||
&self.activity,
|
||||
price.is_some(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Price-dependent metrics
|
||||
if let Some(price) = price {
|
||||
// Value computes (cointime value destroyed/created/stored, VOCDD)
|
||||
self.value.compute(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
price,
|
||||
distribution,
|
||||
&self.activity,
|
||||
exit,
|
||||
)?;
|
||||
// Cap computes (thermo, investor, vaulted, active, cointime caps)
|
||||
self.cap.compute(
|
||||
starting_indexes,
|
||||
mining,
|
||||
distribution,
|
||||
&self.activity,
|
||||
&self.value,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Cap computes (thermo, investor, vaulted, active, cointime caps)
|
||||
self.cap.compute(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
blocks,
|
||||
distribution,
|
||||
&self.activity,
|
||||
&self.value,
|
||||
exit,
|
||||
)?;
|
||||
// Pricing computes (all prices derived from caps)
|
||||
self.pricing.compute(
|
||||
starting_indexes,
|
||||
prices,
|
||||
blocks,
|
||||
distribution,
|
||||
&self.activity,
|
||||
&self.supply,
|
||||
&self.cap,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Pricing computes (all prices derived from caps)
|
||||
self.pricing.compute(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
price,
|
||||
distribution,
|
||||
&self.activity,
|
||||
&self.supply,
|
||||
&self.cap,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Reserve Risk computes (depends on value.vocdd and price)
|
||||
self.reserve_risk.compute(
|
||||
starting_indexes,
|
||||
price,
|
||||
&self.value,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
// Reserve Risk computes (depends on value.vocdd and price)
|
||||
self.reserve_risk
|
||||
.compute(starting_indexes, blocks, prices, &self.value, exit)?;
|
||||
|
||||
let _lock = exit.lock();
|
||||
self.db.compact()?;
|
||||
|
||||
@@ -6,32 +6,30 @@ use brk_types::Version;
|
||||
use vecdb::{Database, PAGE_SIZE};
|
||||
|
||||
use super::{
|
||||
ActivityVecs, AdjustedVecs, CapVecs, PricingVecs, ReserveRiskVecs, SupplyVecs, ValueVecs, Vecs,
|
||||
DB_NAME, VERSION,
|
||||
ActivityVecs, AdjustedVecs, CapVecs, DB_NAME, PricingVecs, ReserveRiskVecs, SupplyVecs,
|
||||
VERSION, ValueVecs, Vecs,
|
||||
};
|
||||
use crate::{indexes, price};
|
||||
use crate::{indexes, prices};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
parent_path: &Path,
|
||||
parent_version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
prices: &prices::Vecs,
|
||||
) -> Result<Self> {
|
||||
let db = Database::open(&parent_path.join(DB_NAME))?;
|
||||
db.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
|
||||
let version = parent_version + VERSION;
|
||||
let v1 = version + Version::ONE;
|
||||
let compute_dollars = price.is_some();
|
||||
|
||||
let activity = ActivityVecs::forced_import(&db, version, indexes)?;
|
||||
let supply = SupplyVecs::forced_import(&db, v1, indexes, price)?;
|
||||
let supply = SupplyVecs::forced_import(&db, v1, indexes, prices)?;
|
||||
let value = ValueVecs::forced_import(&db, v1, indexes)?;
|
||||
let cap = CapVecs::forced_import(&db, v1, indexes)?;
|
||||
let pricing = PricingVecs::forced_import(&db, version, indexes)?;
|
||||
let adjusted = AdjustedVecs::forced_import(&db, version, indexes)?;
|
||||
let reserve_risk = ReserveRiskVecs::forced_import(&db, v1, indexes, compute_dollars)?;
|
||||
let reserve_risk = ReserveRiskVecs::forced_import(&db, v1, indexes)?;
|
||||
|
||||
let this = Self {
|
||||
db,
|
||||
|
||||
@@ -11,7 +11,7 @@ mod import;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use vecdb::Database;
|
||||
use vecdb::{Database, Rw, StorageMode};
|
||||
|
||||
pub use activity::Vecs as ActivityVecs;
|
||||
pub use adjusted::Vecs as AdjustedVecs;
|
||||
@@ -24,16 +24,16 @@ pub use value::Vecs as ValueVecs;
|
||||
pub const DB_NAME: &str = "cointime";
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
#[traversable(skip)]
|
||||
pub(crate) db: Database,
|
||||
|
||||
pub activity: ActivityVecs,
|
||||
pub supply: SupplyVecs,
|
||||
pub value: ValueVecs,
|
||||
pub cap: CapVecs,
|
||||
pub pricing: PricingVecs,
|
||||
pub adjusted: AdjustedVecs,
|
||||
pub reserve_risk: ReserveRiskVecs,
|
||||
pub activity: ActivityVecs<M>,
|
||||
pub supply: SupplyVecs<M>,
|
||||
pub value: ValueVecs<M>,
|
||||
pub cap: CapVecs<M>,
|
||||
pub pricing: PricingVecs<M>,
|
||||
pub adjusted: AdjustedVecs<M>,
|
||||
pub reserve_risk: ReserveRiskVecs<M>,
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@ use vecdb::Exit;
|
||||
|
||||
use super::super::{activity, cap, supply};
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, distribution, indexes, price, utils::OptionExt};
|
||||
use crate::{ComputeIndexes, blocks, distribution, prices};
|
||||
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
price: &price::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
blocks: &blocks::Vecs,
|
||||
distribution: &distribution::Vecs,
|
||||
activity: &activity::Vecs,
|
||||
supply: &supply::Vecs,
|
||||
@@ -24,88 +24,75 @@ impl Vecs {
|
||||
.metrics
|
||||
.supply
|
||||
.total
|
||||
.bitcoin
|
||||
.btc
|
||||
.height;
|
||||
let realized_price = &distribution
|
||||
.utxo_cohorts
|
||||
.all
|
||||
.metrics
|
||||
.realized
|
||||
.u()
|
||||
.realized_price
|
||||
.height;
|
||||
|
||||
self.vaulted_price
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
realized_price,
|
||||
&activity.vaultedness.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.vaulted_price.height.compute_divide(
|
||||
starting_indexes.height,
|
||||
realized_price,
|
||||
&activity.vaultedness.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.vaulted_price_ratio.compute_rest(
|
||||
price,
|
||||
blocks,
|
||||
prices,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.vaulted_price.dateindex.0),
|
||||
Some(&self.vaulted_price.height),
|
||||
)?;
|
||||
|
||||
self.active_price
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
realized_price,
|
||||
&activity.liveliness.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.active_price.height.compute_multiply(
|
||||
starting_indexes.height,
|
||||
realized_price,
|
||||
&activity.liveliness.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.active_price_ratio.compute_rest(
|
||||
price,
|
||||
blocks,
|
||||
prices,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.active_price.dateindex.0),
|
||||
Some(&self.active_price.height),
|
||||
)?;
|
||||
|
||||
self.true_market_mean
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
&cap.investor_cap.height,
|
||||
&supply.active_supply.bitcoin.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.true_market_mean.height.compute_divide(
|
||||
starting_indexes.height,
|
||||
&cap.investor_cap.height,
|
||||
&supply.active_supply.btc.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.true_market_mean_ratio.compute_rest(
|
||||
price,
|
||||
blocks,
|
||||
prices,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.true_market_mean.dateindex.0),
|
||||
Some(&self.true_market_mean.height),
|
||||
)?;
|
||||
|
||||
// cointime_price = cointime_cap / circulating_supply
|
||||
self.cointime_price
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_divide(
|
||||
starting_indexes.height,
|
||||
&cap.cointime_cap.height,
|
||||
circulating_supply,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.cointime_price.height.compute_divide(
|
||||
starting_indexes.height,
|
||||
&cap.cointime_cap.height,
|
||||
circulating_supply,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.cointime_price_ratio.compute_rest(
|
||||
price,
|
||||
blocks,
|
||||
prices,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.cointime_price.dateindex.0),
|
||||
Some(&self.cointime_price.height),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -5,17 +5,17 @@ use vecdb::Database;
|
||||
use super::Vecs;
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromDateRatio, PriceFromHeight},
|
||||
internal::{ComputedFromHeightRatio, PriceFromHeight},
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let vaulted_price = PriceFromHeight::forced_import(db, "vaulted_price", version, indexes)?;
|
||||
let vaulted_price_ratio = ComputedFromDateRatio::forced_import(
|
||||
let vaulted_price_ratio = ComputedFromHeightRatio::forced_import(
|
||||
db,
|
||||
"vaulted_price",
|
||||
Some(&vaulted_price),
|
||||
@@ -25,7 +25,7 @@ impl Vecs {
|
||||
)?;
|
||||
|
||||
let active_price = PriceFromHeight::forced_import(db, "active_price", version, indexes)?;
|
||||
let active_price_ratio = ComputedFromDateRatio::forced_import(
|
||||
let active_price_ratio = ComputedFromHeightRatio::forced_import(
|
||||
db,
|
||||
"active_price",
|
||||
Some(&active_price),
|
||||
@@ -36,7 +36,7 @@ impl Vecs {
|
||||
|
||||
let true_market_mean =
|
||||
PriceFromHeight::forced_import(db, "true_market_mean", version, indexes)?;
|
||||
let true_market_mean_ratio = ComputedFromDateRatio::forced_import(
|
||||
let true_market_mean_ratio = ComputedFromHeightRatio::forced_import(
|
||||
db,
|
||||
"true_market_mean",
|
||||
Some(&true_market_mean),
|
||||
@@ -47,7 +47,7 @@ impl Vecs {
|
||||
|
||||
let cointime_price =
|
||||
PriceFromHeight::forced_import(db, "cointime_price", version, indexes)?;
|
||||
let cointime_price_ratio = ComputedFromDateRatio::forced_import(
|
||||
let cointime_price_ratio = ComputedFromHeightRatio::forced_import(
|
||||
db,
|
||||
"cointime_price",
|
||||
Some(&cointime_price),
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Dollars;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedFromDateRatio, PriceFromHeight};
|
||||
use crate::internal::{ComputedFromHeightLast, ComputedFromHeightRatio, Price};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub vaulted_price: PriceFromHeight,
|
||||
pub vaulted_price_ratio: ComputedFromDateRatio,
|
||||
pub active_price: PriceFromHeight,
|
||||
pub active_price_ratio: ComputedFromDateRatio,
|
||||
pub true_market_mean: PriceFromHeight,
|
||||
pub true_market_mean_ratio: ComputedFromDateRatio,
|
||||
pub cointime_price: PriceFromHeight,
|
||||
pub cointime_price_ratio: ComputedFromDateRatio,
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub vaulted_price: Price<ComputedFromHeightLast<Dollars, M>>,
|
||||
pub vaulted_price_ratio: ComputedFromHeightRatio<M>,
|
||||
pub active_price: Price<ComputedFromHeightLast<Dollars, M>>,
|
||||
pub active_price_ratio: ComputedFromHeightRatio<M>,
|
||||
pub true_market_mean: Price<ComputedFromHeightLast<Dollars, M>>,
|
||||
pub true_market_mean_ratio: ComputedFromHeightRatio<M>,
|
||||
pub cointime_price: Price<ComputedFromHeightLast<Dollars, M>>,
|
||||
pub cointime_price_ratio: ComputedFromHeightRatio<M>,
|
||||
}
|
||||
|
||||
@@ -1,48 +1,40 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{Close, Dollars, StoredF64};
|
||||
use brk_types::StoredF64;
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::{super::value, Vecs};
|
||||
use crate::{price, ComputeIndexes};
|
||||
use crate::{blocks, ComputeIndexes, prices, traits::ComputeRollingMedianFromStarts};
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
price: &price::Vecs,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
value: &value::Vecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let vocdd_dateindex_sum = &value.vocdd.dateindex.sum.0;
|
||||
|
||||
self.vocdd_365d_median.compute_rolling_median(
|
||||
starting_indexes.dateindex,
|
||||
vocdd_dateindex_sum,
|
||||
365,
|
||||
self.vocdd_365d_median.compute_rolling_median_from_starts(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1y_ago,
|
||||
&value.vocdd.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let price_close = &price.usd.split.close.dateindex;
|
||||
|
||||
self.hodl_bank.compute_cumulative_transformed_binary(
|
||||
starting_indexes.dateindex,
|
||||
price_close,
|
||||
starting_indexes.height,
|
||||
&prices.usd.price,
|
||||
&self.vocdd_365d_median,
|
||||
|price: Close<Dollars>, median: StoredF64| StoredF64::from(f64::from(price) - f64::from(median)),
|
||||
|price, median| StoredF64::from(f64::from(price) - f64::from(median)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
if let Some(reserve_risk) = self.reserve_risk.as_mut() {
|
||||
reserve_risk.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_divide(
|
||||
starting_indexes.dateindex,
|
||||
price_close,
|
||||
&self.hodl_bank,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
self.reserve_risk.height.compute_divide(
|
||||
starting_indexes.height,
|
||||
&prices.usd.price,
|
||||
&self.hodl_bank,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,22 +3,19 @@ use brk_types::Version;
|
||||
use vecdb::{Database, EagerVec, ImportableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{indexes, internal::ComputedFromDateLast};
|
||||
use crate::{indexes, internal::ComputedFromHeightLast};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
compute_dollars: bool,
|
||||
) -> Result<Self> {
|
||||
let v1 = version + Version::ONE;
|
||||
Ok(Self {
|
||||
vocdd_365d_median: EagerVec::forced_import(db, "vocdd_365d_median", v1)?,
|
||||
hodl_bank: EagerVec::forced_import(db, "hodl_bank", v1)?,
|
||||
reserve_risk: compute_dollars
|
||||
.then(|| ComputedFromDateLast::forced_import(db, "reserve_risk", v1, indexes))
|
||||
.transpose()?,
|
||||
reserve_risk: ComputedFromHeightLast::forced_import(db, "reserve_risk", v1, indexes)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{DateIndex, StoredF64};
|
||||
use vecdb::{EagerVec, PcoVec};
|
||||
use brk_types::{Height, StoredF64};
|
||||
use vecdb::{EagerVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::ComputedFromDateLast;
|
||||
use crate::internal::ComputedFromHeightLast;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub vocdd_365d_median: EagerVec<PcoVec<DateIndex, StoredF64>>,
|
||||
pub hodl_bank: EagerVec<PcoVec<DateIndex, StoredF64>>,
|
||||
pub reserve_risk: Option<ComputedFromDateLast<StoredF64>>,
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub vocdd_365d_median: M::Stored<EagerVec<PcoVec<Height, StoredF64>>>,
|
||||
pub hodl_bank: M::Stored<EagerVec<PcoVec<Height, StoredF64>>>,
|
||||
pub reserve_risk: ComputedFromHeightLast<StoredF64, M>,
|
||||
}
|
||||
|
||||
@@ -3,12 +3,11 @@ use vecdb::Exit;
|
||||
|
||||
use super::super::activity;
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, distribution, indexes};
|
||||
use crate::{ComputeIndexes, distribution};
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
distribution: &distribution::Vecs,
|
||||
activity: &activity::Vecs,
|
||||
@@ -23,27 +22,19 @@ impl Vecs {
|
||||
.sats
|
||||
.height;
|
||||
|
||||
self.vaulted_supply
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
circulating_supply,
|
||||
&activity.vaultedness.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.vaulted_supply.sats.height.compute_multiply(
|
||||
starting_indexes.height,
|
||||
circulating_supply,
|
||||
&activity.vaultedness.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.active_supply
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
circulating_supply,
|
||||
&activity.liveliness.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.active_supply.sats.height.compute_multiply(
|
||||
starting_indexes.height,
|
||||
circulating_supply,
|
||||
&activity.liveliness.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ use brk_types::Version;
|
||||
use vecdb::Database;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{indexes, internal::ValueFromHeightLast, price};
|
||||
use crate::{indexes, internal::ValueFromHeightLast, prices};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
prices: &prices::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
vaulted_supply: ValueFromHeightLast::forced_import(
|
||||
@@ -18,14 +18,14 @@ impl Vecs {
|
||||
"vaulted_supply",
|
||||
version,
|
||||
indexes,
|
||||
price,
|
||||
prices,
|
||||
)?,
|
||||
active_supply: ValueFromHeightLast::forced_import(
|
||||
db,
|
||||
"active_supply",
|
||||
version,
|
||||
indexes,
|
||||
price,
|
||||
prices,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use brk_traversable::Traversable;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::ValueFromHeightLast;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub vaulted_supply: ValueFromHeightLast,
|
||||
pub active_supply: ValueFromHeightLast,
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub vaulted_supply: ValueFromHeightLast<M>,
|
||||
pub active_supply: ValueFromHeightLast<M>,
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{Bitcoin, Close, Dollars, StoredF64};
|
||||
use vecdb::{Exit, TypedVecIterator};
|
||||
use brk_types::{Bitcoin, Dollars, StoredF64};
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::super::activity;
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, distribution, indexes, price};
|
||||
use crate::{ComputeIndexes, distribution, prices};
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
price: &price::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
distribution: &distribution::Vecs,
|
||||
activity: &activity::Vecs,
|
||||
exit: &Exit,
|
||||
@@ -36,14 +35,14 @@ impl Vecs {
|
||||
.metrics
|
||||
.supply
|
||||
.total
|
||||
.bitcoin
|
||||
.btc
|
||||
.height;
|
||||
|
||||
self.cointime_value_destroyed
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
.compute(starting_indexes, exit,|vec| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&price.usd.split.close.height,
|
||||
&prices.usd.price,
|
||||
&coinblocks_destroyed.height,
|
||||
exit,
|
||||
)?;
|
||||
@@ -51,10 +50,10 @@ impl Vecs {
|
||||
})?;
|
||||
|
||||
self.cointime_value_created
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
.compute(starting_indexes, exit,|vec| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&price.usd.split.close.height,
|
||||
&prices.usd.price,
|
||||
&activity.coinblocks_created.height,
|
||||
exit,
|
||||
)?;
|
||||
@@ -62,10 +61,10 @@ impl Vecs {
|
||||
})?;
|
||||
|
||||
self.cointime_value_stored
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
.compute(starting_indexes, exit,|vec| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&price.usd.split.close.height,
|
||||
&prices.usd.price,
|
||||
&activity.coinblocks_stored.height,
|
||||
exit,
|
||||
)?;
|
||||
@@ -76,14 +75,13 @@ impl Vecs {
|
||||
// Supply-adjusted to account for growing supply over time
|
||||
// This is a key input for Reserve Risk / HODL Bank calculation
|
||||
self.vocdd
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
let mut supply_iter = circulating_supply.into_iter();
|
||||
vec.compute_transform2(
|
||||
.compute(starting_indexes, exit,|vec| {
|
||||
vec.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&price.usd.split.close.height,
|
||||
&prices.usd.price,
|
||||
&coindays_destroyed.height,
|
||||
|(i, price, cdd, _): (_, Close<Dollars>, StoredF64, _)| {
|
||||
let supply: Bitcoin = supply_iter.get_unwrap(i);
|
||||
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))
|
||||
|
||||
@@ -6,7 +6,7 @@ use super::Vecs;
|
||||
use crate::{indexes, internal::ComputedFromHeightSumCum};
|
||||
|
||||
impl Vecs {
|
||||
pub fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
Ok(Self {
|
||||
cointime_value_destroyed: ComputedFromHeightSumCum::forced_import(
|
||||
db,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::StoredF64;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::ComputedFromHeightSumCum;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub cointime_value_destroyed: ComputedFromHeightSumCum<StoredF64>,
|
||||
pub cointime_value_created: ComputedFromHeightSumCum<StoredF64>,
|
||||
pub cointime_value_stored: ComputedFromHeightSumCum<StoredF64>,
|
||||
pub vocdd: ComputedFromHeightSumCum<StoredF64>,
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub cointime_value_destroyed: ComputedFromHeightSumCum<StoredF64, M>,
|
||||
pub cointime_value_created: ComputedFromHeightSumCum<StoredF64, M>,
|
||||
pub cointime_value_stored: ComputedFromHeightSumCum<StoredF64, M>,
|
||||
pub vocdd: ComputedFromHeightSumCum<StoredF64, M>,
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ pub struct Vecs {
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub fn new(version: Version, indexes: &indexes::Vecs) -> Self {
|
||||
pub(crate) fn new(version: Version, indexes: &indexes::Vecs) -> Self {
|
||||
let v = version;
|
||||
|
||||
Self {
|
||||
|
||||
@@ -20,9 +20,9 @@ use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredU32, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, GenericStoredVec};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{ComputeIndexes, indexes, internal::ComputedFromHeightDistribution};
|
||||
use crate::{indexes, internal::ComputedFromHeightDistribution};
|
||||
|
||||
/// Per-block activity counts - reset each block.
|
||||
///
|
||||
@@ -40,7 +40,7 @@ pub struct BlockActivityCounts {
|
||||
impl BlockActivityCounts {
|
||||
/// Reset all counts to zero.
|
||||
#[inline]
|
||||
pub fn reset(&mut self) {
|
||||
pub(crate) fn reset(&mut self) {
|
||||
*self = Self::default();
|
||||
}
|
||||
}
|
||||
@@ -51,12 +51,12 @@ pub struct AddressTypeToActivityCounts(pub ByAddressType<BlockActivityCounts>);
|
||||
|
||||
impl AddressTypeToActivityCounts {
|
||||
/// Reset all per-type counts.
|
||||
pub fn reset(&mut self) {
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.0.values_mut().for_each(|v| v.reset());
|
||||
}
|
||||
|
||||
/// Sum all types to get totals.
|
||||
pub fn totals(&self) -> BlockActivityCounts {
|
||||
pub(crate) fn totals(&self) -> BlockActivityCounts {
|
||||
let mut total = BlockActivityCounts::default();
|
||||
for counts in self.0.values() {
|
||||
total.reactivated += counts.reactivated;
|
||||
@@ -69,18 +69,18 @@ impl AddressTypeToActivityCounts {
|
||||
}
|
||||
|
||||
/// Activity count vectors for a single category (e.g., one address type or "all").
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct ActivityCountVecs {
|
||||
pub reactivated: ComputedFromHeightDistribution<StoredU32>,
|
||||
pub sending: ComputedFromHeightDistribution<StoredU32>,
|
||||
pub receiving: ComputedFromHeightDistribution<StoredU32>,
|
||||
pub balance_increased: ComputedFromHeightDistribution<StoredU32>,
|
||||
pub balance_decreased: ComputedFromHeightDistribution<StoredU32>,
|
||||
pub both: ComputedFromHeightDistribution<StoredU32>,
|
||||
#[derive(Traversable)]
|
||||
pub struct ActivityCountVecs<M: StorageMode = Rw> {
|
||||
pub reactivated: ComputedFromHeightDistribution<StoredU32, M>,
|
||||
pub sending: ComputedFromHeightDistribution<StoredU32, M>,
|
||||
pub receiving: ComputedFromHeightDistribution<StoredU32, M>,
|
||||
pub balance_increased: ComputedFromHeightDistribution<StoredU32, M>,
|
||||
pub balance_decreased: ComputedFromHeightDistribution<StoredU32, M>,
|
||||
pub both: ComputedFromHeightDistribution<StoredU32, M>,
|
||||
}
|
||||
|
||||
impl ActivityCountVecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
@@ -126,7 +126,7 @@ impl ActivityCountVecs {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn min_stateful_height(&self) -> usize {
|
||||
pub(crate) fn min_stateful_height(&self) -> usize {
|
||||
self.reactivated
|
||||
.height
|
||||
.len()
|
||||
@@ -137,7 +137,7 @@ impl ActivityCountVecs {
|
||||
.min(self.both.height.len())
|
||||
}
|
||||
|
||||
pub fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
[
|
||||
&mut self.reactivated.height as &mut dyn AnyStoredVec,
|
||||
&mut self.sending.height as &mut dyn AnyStoredVec,
|
||||
@@ -149,7 +149,7 @@ impl ActivityCountVecs {
|
||||
.into_par_iter()
|
||||
}
|
||||
|
||||
pub fn reset_height(&mut self) -> Result<()> {
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.reactivated.height.reset()?;
|
||||
self.sending.height.reset()?;
|
||||
self.receiving.height.reset()?;
|
||||
@@ -159,7 +159,7 @@ impl ActivityCountVecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn truncate_push_height(
|
||||
pub(crate) fn truncate_push_height(
|
||||
&mut self,
|
||||
height: Height,
|
||||
counts: &BlockActivityCounts,
|
||||
@@ -187,30 +187,11 @@ impl ActivityCountVecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.reactivated
|
||||
.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.sending
|
||||
.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.receiving
|
||||
.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.balance_increased
|
||||
.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.balance_decreased
|
||||
.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.both.compute_rest(indexes, starting_indexes, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-address-type activity count vecs.
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
pub struct AddressTypeToActivityCountVecs(ByAddressType<ActivityCountVecs>);
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct AddressTypeToActivityCountVecs<M: StorageMode = Rw>(ByAddressType<ActivityCountVecs<M>>);
|
||||
|
||||
impl From<ByAddressType<ActivityCountVecs>> for AddressTypeToActivityCountVecs {
|
||||
#[inline]
|
||||
@@ -220,7 +201,7 @@ impl From<ByAddressType<ActivityCountVecs>> for AddressTypeToActivityCountVecs {
|
||||
}
|
||||
|
||||
impl AddressTypeToActivityCountVecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
@@ -233,11 +214,11 @@ impl AddressTypeToActivityCountVecs {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn min_stateful_height(&self) -> usize {
|
||||
pub(crate) fn min_stateful_height(&self) -> usize {
|
||||
self.0.values().map(|v| v.min_stateful_height()).min().unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
let inner = &mut self.0;
|
||||
let mut vecs: Vec<&mut dyn AnyStoredVec> = Vec::new();
|
||||
for type_vecs in [
|
||||
@@ -260,7 +241,7 @@ impl AddressTypeToActivityCountVecs {
|
||||
vecs.into_par_iter()
|
||||
}
|
||||
|
||||
pub fn reset_height(&mut self) -> Result<()> {
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.p2pk65.reset_height()?;
|
||||
self.p2pk33.reset_height()?;
|
||||
self.p2pkh.reset_height()?;
|
||||
@@ -272,7 +253,7 @@ impl AddressTypeToActivityCountVecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn truncate_push_height(
|
||||
pub(crate) fn truncate_push_height(
|
||||
&mut self,
|
||||
height: Height,
|
||||
counts: &AddressTypeToActivityCounts,
|
||||
@@ -293,34 +274,18 @@ impl AddressTypeToActivityCountVecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.p2pk65.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2pk33.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2pkh.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2sh.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2wpkh.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2wsh.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2tr.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2a.compute_rest(indexes, starting_indexes, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Storage for activity metrics (global + per type).
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct AddressActivityVecs {
|
||||
pub all: ActivityCountVecs,
|
||||
#[derive(Traversable)]
|
||||
pub struct AddressActivityVecs<M: StorageMode = Rw> {
|
||||
pub all: ActivityCountVecs<M>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: AddressTypeToActivityCountVecs,
|
||||
pub by_addresstype: AddressTypeToActivityCountVecs<M>,
|
||||
}
|
||||
|
||||
impl AddressActivityVecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
@@ -334,23 +299,23 @@ impl AddressActivityVecs {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn min_stateful_height(&self) -> usize {
|
||||
pub(crate) fn min_stateful_height(&self) -> usize {
|
||||
self.all.min_stateful_height().min(self.by_addresstype.min_stateful_height())
|
||||
}
|
||||
|
||||
pub fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
self.all
|
||||
.par_iter_height_mut()
|
||||
.chain(self.by_addresstype.par_iter_height_mut())
|
||||
}
|
||||
|
||||
pub fn reset_height(&mut self) -> Result<()> {
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.all.reset_height()?;
|
||||
self.by_addresstype.reset_height()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn truncate_push_height(
|
||||
pub(crate) fn truncate_push_height(
|
||||
&mut self,
|
||||
height: Height,
|
||||
counts: &AddressTypeToActivityCounts,
|
||||
@@ -361,15 +326,4 @@ impl AddressActivityVecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.all.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.by_addresstype
|
||||
.compute_rest(indexes, starting_indexes, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,21 +5,22 @@ use brk_types::{Height, StoredF64, StoredU64, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, Database, EagerVec, Exit, GenericStoredVec, PcoVec, TypedVecIterator,
|
||||
AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode,
|
||||
WritableVec,
|
||||
};
|
||||
|
||||
use crate::{ComputeIndexes, indexes, internal::{ComputedFromDateLast, ComputedFromHeightLast}};
|
||||
use crate::{ComputeIndexes, blocks, indexes, internal::ComputedFromHeightLast};
|
||||
|
||||
/// Address count with 30d change metric for a single type.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct AddrCountVecs {
|
||||
#[derive(Traversable)]
|
||||
pub struct AddrCountVecs<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub count: ComputedFromHeightLast<StoredU64>,
|
||||
pub _30d_change: ComputedFromDateLast<StoredF64>,
|
||||
pub count: ComputedFromHeightLast<StoredU64, M>,
|
||||
pub _30d_change: ComputedFromHeightLast<StoredF64, M>,
|
||||
}
|
||||
|
||||
impl AddrCountVecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
@@ -27,7 +28,7 @@ impl AddrCountVecs {
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
count: ComputedFromHeightLast::forced_import(db, name, version, indexes)?,
|
||||
_30d_change: ComputedFromDateLast::forced_import(
|
||||
_30d_change: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
&format!("{name}_30d_change"),
|
||||
version,
|
||||
@@ -36,24 +37,18 @@ impl AddrCountVecs {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.count.compute_rest(indexes, starting_indexes, exit)?;
|
||||
|
||||
self._30d_change
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_change(
|
||||
starting_indexes.dateindex,
|
||||
&*self.count.dateindex,
|
||||
30,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self._30d_change.height.compute_rolling_change(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
&self.count.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -65,7 +60,7 @@ pub struct AddressTypeToAddressCount(ByAddressType<u64>);
|
||||
|
||||
impl AddressTypeToAddressCount {
|
||||
#[inline]
|
||||
pub fn sum(&self) -> u64 {
|
||||
pub(crate) fn sum(&self) -> u64 {
|
||||
self.0.values().sum()
|
||||
}
|
||||
}
|
||||
@@ -75,56 +70,14 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount {
|
||||
fn from((groups, starting_height): (&AddressTypeToAddrCountVecs, Height)) -> Self {
|
||||
if let Some(prev_height) = starting_height.decremented() {
|
||||
Self(ByAddressType {
|
||||
p2pk65: groups
|
||||
.p2pk65
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2pk33: groups
|
||||
.p2pk33
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2pkh: groups
|
||||
.p2pkh
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2sh: groups
|
||||
.p2sh
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2wpkh: groups
|
||||
.p2wpkh
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2wsh: groups
|
||||
.p2wsh
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2tr: groups
|
||||
.p2tr
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2a: groups.p2a.count.height.into_iter().get_unwrap(prev_height).into(),
|
||||
p2pk65: groups.p2pk65.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2pk33: groups.p2pk33.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2pkh: groups.p2pkh.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2sh: groups.p2sh.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2wpkh: groups.p2wpkh.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2wsh: groups.p2wsh.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2tr: groups.p2tr.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2a: groups.p2a.count.height.collect_one(prev_height).unwrap().into(),
|
||||
})
|
||||
} else {
|
||||
Default::default()
|
||||
@@ -133,8 +86,8 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount {
|
||||
}
|
||||
|
||||
/// Address count per address type, with height + derived indexes + 30d change.
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
pub struct AddressTypeToAddrCountVecs(ByAddressType<AddrCountVecs>);
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct AddressTypeToAddrCountVecs<M: StorageMode = Rw>(ByAddressType<AddrCountVecs<M>>);
|
||||
|
||||
impl From<ByAddressType<AddrCountVecs>> for AddressTypeToAddrCountVecs {
|
||||
#[inline]
|
||||
@@ -144,7 +97,7 @@ impl From<ByAddressType<AddrCountVecs>> for AddressTypeToAddrCountVecs {
|
||||
}
|
||||
|
||||
impl AddressTypeToAddrCountVecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
@@ -162,7 +115,7 @@ impl AddressTypeToAddrCountVecs {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn min_stateful_height(&self) -> usize {
|
||||
pub(crate) fn min_stateful_height(&self) -> usize {
|
||||
self.p2pk65
|
||||
.count
|
||||
.height
|
||||
@@ -176,7 +129,7 @@ impl AddressTypeToAddrCountVecs {
|
||||
.min(self.p2a.count.height.len())
|
||||
}
|
||||
|
||||
pub fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
let inner = &mut self.0;
|
||||
[
|
||||
&mut inner.p2pk65.count.height as &mut dyn AnyStoredVec,
|
||||
@@ -191,7 +144,7 @@ impl AddressTypeToAddrCountVecs {
|
||||
.into_par_iter()
|
||||
}
|
||||
|
||||
pub fn truncate_push_height(
|
||||
pub(crate) fn truncate_push_height(
|
||||
&mut self,
|
||||
height: Height,
|
||||
addr_counts: &AddressTypeToAddressCount,
|
||||
@@ -231,8 +184,8 @@ impl AddressTypeToAddrCountVecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn reset_height(&mut self) -> Result<()> {
|
||||
use vecdb::GenericStoredVec;
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
use vecdb::WritableVec;
|
||||
self.p2pk65.count.height.reset()?;
|
||||
self.p2pk33.count.height.reset()?;
|
||||
self.p2pkh.count.height.reset()?;
|
||||
@@ -244,24 +197,24 @@ impl AddressTypeToAddrCountVecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.p2pk65.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2pk33.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2pkh.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2sh.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2wpkh.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2wsh.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2tr.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2a.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.p2pk65.compute_rest(blocks, starting_indexes, exit)?;
|
||||
self.p2pk33.compute_rest(blocks, starting_indexes, exit)?;
|
||||
self.p2pkh.compute_rest(blocks, starting_indexes, exit)?;
|
||||
self.p2sh.compute_rest(blocks, starting_indexes, exit)?;
|
||||
self.p2wpkh.compute_rest(blocks, starting_indexes, exit)?;
|
||||
self.p2wsh.compute_rest(blocks, starting_indexes, exit)?;
|
||||
self.p2tr.compute_rest(blocks, starting_indexes, exit)?;
|
||||
self.p2a.compute_rest(blocks, starting_indexes, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn by_height(&self) -> Vec<&EagerVec<PcoVec<Height, StoredU64>>> {
|
||||
pub(crate) fn by_height(&self) -> Vec<&EagerVec<PcoVec<Height, StoredU64>>> {
|
||||
vec![
|
||||
&self.p2pk65.count.height,
|
||||
&self.p2pk33.count.height,
|
||||
@@ -275,15 +228,15 @@ impl AddressTypeToAddrCountVecs {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct AddrCountsVecs {
|
||||
pub all: AddrCountVecs,
|
||||
#[derive(Traversable)]
|
||||
pub struct AddrCountsVecs<M: StorageMode = Rw> {
|
||||
pub all: AddrCountVecs<M>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: AddressTypeToAddrCountVecs,
|
||||
pub by_addresstype: AddressTypeToAddrCountVecs<M>,
|
||||
}
|
||||
|
||||
impl AddrCountsVecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
@@ -295,22 +248,22 @@ impl AddrCountsVecs {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn min_stateful_height(&self) -> usize {
|
||||
pub(crate) fn min_stateful_height(&self) -> usize {
|
||||
self.all.count.height.len().min(self.by_addresstype.min_stateful_height())
|
||||
}
|
||||
|
||||
pub fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
rayon::iter::once(&mut self.all.count.height as &mut dyn AnyStoredVec)
|
||||
.chain(self.by_addresstype.par_iter_height_mut())
|
||||
}
|
||||
|
||||
pub fn reset_height(&mut self) -> Result<()> {
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.all.count.height.reset()?;
|
||||
self.by_addresstype.reset_height()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn truncate_push_height(
|
||||
pub(crate) fn truncate_push_height(
|
||||
&mut self,
|
||||
height: Height,
|
||||
total: u64,
|
||||
@@ -322,32 +275,27 @@ impl AddrCountsVecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.by_addresstype
|
||||
.compute_rest(indexes, starting_indexes, exit)?;
|
||||
.compute_rest(blocks, starting_indexes, exit)?;
|
||||
|
||||
let sources = self.by_addresstype.by_height();
|
||||
self.all
|
||||
.count
|
||||
.compute_all(indexes, starting_indexes, exit, |height_vec| {
|
||||
Ok(height_vec.compute_sum_of_others(starting_indexes.height, &sources, exit)?)
|
||||
})?;
|
||||
.height
|
||||
.compute_sum_of_others(starting_indexes.height, &sources, exit)?;
|
||||
|
||||
self.all._30d_change
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_change(
|
||||
starting_indexes.dateindex,
|
||||
&*self.all.count.dateindex,
|
||||
30,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.all._30d_change.height.compute_rolling_change(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
&self.all.count.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,46 +1,30 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
EmptyAddressData, EmptyAddressIndex, FundedAddressData, FundedAddressIndex, Height, Version,
|
||||
EmptyAddressData, EmptyAddressIndex, FundedAddressData, FundedAddressIndex, Height,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{
|
||||
AnyStoredVec, BytesVec, Database, GenericStoredVec, ImportOptions, ImportableVec, Stamp,
|
||||
AnyStoredVec, BytesVec, Rw, Stamp, StorageMode, WritableVec,
|
||||
};
|
||||
|
||||
const SAVED_STAMPED_CHANGES: u16 = 10;
|
||||
|
||||
/// Storage for both funded and empty address data.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct AddressesDataVecs {
|
||||
pub funded: BytesVec<FundedAddressIndex, FundedAddressData>,
|
||||
pub empty: BytesVec<EmptyAddressIndex, EmptyAddressData>,
|
||||
#[derive(Traversable)]
|
||||
pub struct AddressesDataVecs<M: StorageMode = Rw> {
|
||||
pub funded: M::Stored<BytesVec<FundedAddressIndex, FundedAddressData>>,
|
||||
pub empty: M::Stored<BytesVec<EmptyAddressIndex, EmptyAddressData>>,
|
||||
}
|
||||
|
||||
impl AddressesDataVecs {
|
||||
/// Import from database.
|
||||
pub fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
funded: BytesVec::forced_import_with(
|
||||
ImportOptions::new(db, "fundedaddressdata", version)
|
||||
.with_saved_stamped_changes(SAVED_STAMPED_CHANGES),
|
||||
)?,
|
||||
empty: BytesVec::forced_import_with(
|
||||
ImportOptions::new(db, "emptyaddressdata", version)
|
||||
.with_saved_stamped_changes(SAVED_STAMPED_CHANGES),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get minimum stamped height across funded and empty data.
|
||||
pub fn min_stamped_height(&self) -> Height {
|
||||
pub(crate) fn min_stamped_height(&self) -> Height {
|
||||
Height::from(self.funded.stamp())
|
||||
.incremented()
|
||||
.min(Height::from(self.empty.stamp()).incremented())
|
||||
}
|
||||
|
||||
/// Rollback both funded and empty data to before the given stamp.
|
||||
pub fn rollback_before(&mut self, stamp: Stamp) -> Result<[Stamp; 2]> {
|
||||
pub(crate) fn rollback_before(&mut self, stamp: Stamp) -> Result<[Stamp; 2]> {
|
||||
Ok([
|
||||
self.funded.rollback_before(stamp)?,
|
||||
self.empty.rollback_before(stamp)?,
|
||||
@@ -48,23 +32,14 @@ impl AddressesDataVecs {
|
||||
}
|
||||
|
||||
/// Reset both funded and empty data.
|
||||
pub fn reset(&mut self) -> Result<()> {
|
||||
pub(crate) fn reset(&mut self) -> Result<()> {
|
||||
self.funded.reset()?;
|
||||
self.empty.reset()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Flush both funded and empty data with stamp.
|
||||
pub fn write(&mut self, stamp: Stamp, with_changes: bool) -> Result<()> {
|
||||
self.funded
|
||||
.stamped_write_maybe_with_changes(stamp, with_changes)?;
|
||||
self.empty
|
||||
.stamped_write_maybe_with_changes(stamp, with_changes)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a parallel iterator over all vecs for parallel writing.
|
||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.funded as &mut dyn AnyStoredVec,
|
||||
&mut self.empty as &mut dyn AnyStoredVec,
|
||||
|
||||
@@ -4,10 +4,10 @@ use brk_cohort::{ByAddressType, zip2_by_addresstype};
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredF32, StoredU64, Version};
|
||||
use vecdb::{Database, Exit, IterableCloneableVec};
|
||||
use vecdb::ReadableCloneableVec;
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, indexes,
|
||||
indexes,
|
||||
internal::{LazyBinaryComputedFromHeightDistribution, RatioU64F32},
|
||||
};
|
||||
|
||||
@@ -26,67 +26,50 @@ pub struct GrowthRateVecs {
|
||||
}
|
||||
|
||||
impl GrowthRateVecs {
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
pub(crate) fn forced_import(
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
new_addr_count: &NewAddrCountVecs,
|
||||
addr_count: &AddrCountsVecs,
|
||||
) -> Result<Self> {
|
||||
let all = make_growth_rate(
|
||||
db,
|
||||
"growth_rate",
|
||||
version,
|
||||
indexes,
|
||||
&new_addr_count.all.height,
|
||||
&addr_count.all.count.height,
|
||||
)?;
|
||||
);
|
||||
|
||||
let by_addresstype: GrowthRateByType = zip2_by_addresstype(
|
||||
&new_addr_count.by_addresstype,
|
||||
&addr_count.by_addresstype,
|
||||
|name, new, addr| {
|
||||
make_growth_rate(
|
||||
db,
|
||||
Ok(make_growth_rate(
|
||||
&format!("{name}_growth_rate"),
|
||||
version,
|
||||
indexes,
|
||||
&new.height,
|
||||
&addr.count.height,
|
||||
)
|
||||
))
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(Self { all, by_addresstype })
|
||||
}
|
||||
|
||||
pub fn derive_from(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.all.derive_from(indexes, starting_indexes, exit)?;
|
||||
for vecs in self.by_addresstype.values_mut() {
|
||||
vecs.derive_from(indexes, starting_indexes, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn make_growth_rate<V1, V2>(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
new: &V1,
|
||||
addr: &V2,
|
||||
) -> Result<LazyBinaryComputedFromHeightDistribution<StoredF32, StoredU64, StoredU64>>
|
||||
) -> LazyBinaryComputedFromHeightDistribution<StoredF32, StoredU64, StoredU64>
|
||||
where
|
||||
V1: IterableCloneableVec<Height, StoredU64>,
|
||||
V2: IterableCloneableVec<Height, StoredU64>,
|
||||
V1: ReadableCloneableVec<Height, StoredU64>,
|
||||
V2: ReadableCloneableVec<Height, StoredU64>,
|
||||
{
|
||||
LazyBinaryComputedFromHeightDistribution::<StoredF32, StoredU64, StoredU64>::forced_import::<
|
||||
RatioU64F32,
|
||||
>(db, name, version, new.boxed_clone(), addr.boxed_clone(), indexes)
|
||||
>(name, version, new.read_only_boxed_clone(), addr.read_only_boxed_clone(), indexes)
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ use brk_types::{
|
||||
use rayon::prelude::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, BytesVec, Database, GenericStoredVec, ImportOptions, ImportableVec,
|
||||
Reader, Stamp,
|
||||
AnyStoredVec, AnyVec, BytesVec, Database, ReadableVec, WritableVec, ImportOptions, ImportableVec,
|
||||
Reader, Rw, Stamp, StorageMode,
|
||||
};
|
||||
|
||||
use super::super::AddressTypeToTypeIndexMap;
|
||||
@@ -22,14 +22,14 @@ const SAVED_STAMPED_CHANGES: u16 = 10;
|
||||
/// Macro to define AnyAddressIndexesVecs and its methods.
|
||||
macro_rules! define_any_address_indexes_vecs {
|
||||
($(($field:ident, $variant:ident, $index:ty)),* $(,)?) => {
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct AnyAddressIndexesVecs {
|
||||
$(pub $field: BytesVec<$index, AnyAddressIndex>,)*
|
||||
#[derive(Traversable)]
|
||||
pub struct AnyAddressIndexesVecs<M: StorageMode = Rw> {
|
||||
$(pub $field: M::Stored<BytesVec<$index, AnyAddressIndex>>,)*
|
||||
}
|
||||
|
||||
impl AnyAddressIndexesVecs {
|
||||
/// Import from database.
|
||||
pub fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
$($field: BytesVec::forced_import_with(
|
||||
ImportOptions::new(db, "anyaddressindex", version)
|
||||
@@ -39,7 +39,7 @@ macro_rules! define_any_address_indexes_vecs {
|
||||
}
|
||||
|
||||
/// Get minimum stamped height across all address types.
|
||||
pub fn min_stamped_height(&self) -> Height {
|
||||
pub(crate) fn min_stamped_height(&self) -> Height {
|
||||
[$(Height::from(self.$field.stamp()).incremented()),*]
|
||||
.into_iter()
|
||||
.min()
|
||||
@@ -47,78 +47,42 @@ macro_rules! define_any_address_indexes_vecs {
|
||||
}
|
||||
|
||||
/// Rollback all address types to before the given stamp.
|
||||
pub fn rollback_before(&mut self, stamp: Stamp) -> Result<Vec<Stamp>> {
|
||||
pub(crate) fn rollback_before(&mut self, stamp: Stamp) -> Result<Vec<Stamp>> {
|
||||
Ok(vec![$(self.$field.rollback_before(stamp)?),*])
|
||||
}
|
||||
|
||||
/// Reset all address types.
|
||||
pub fn reset(&mut self) -> Result<()> {
|
||||
pub(crate) fn reset(&mut self) -> Result<()> {
|
||||
$(self.$field.reset()?;)*
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get address index for a given type and typeindex.
|
||||
/// Uses get_any_or_read_at_unwrap to check updated layer (needed after rollback).
|
||||
pub fn get(&self, address_type: OutputType, typeindex: TypeIndex, reader: &Reader) -> AnyAddressIndex {
|
||||
/// Uses get_any_or_read_at to check updated layer (needed after rollback).
|
||||
pub(crate) fn get(&self, address_type: OutputType, typeindex: TypeIndex, reader: &Reader) -> Result<AnyAddressIndex> {
|
||||
match address_type {
|
||||
$(OutputType::$variant => self.$field.get_any_or_read_at_unwrap(typeindex.into(), reader),)*
|
||||
$(OutputType::$variant => Ok(self.$field.get_any_or_read_at(typeindex.into(), reader)?.unwrap()),)*
|
||||
_ => unreachable!("Invalid address type: {:?}", address_type),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get address index with single read (no caching).
|
||||
pub fn get_once(&self, address_type: OutputType, typeindex: TypeIndex) -> Result<AnyAddressIndex> {
|
||||
match address_type {
|
||||
$(OutputType::$variant => self.$field.read_at_once(typeindex.into()).map_err(Into::into),)*
|
||||
_ => Err(Error::UnsupportedType(address_type.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update or push address index for a given type.
|
||||
pub fn update_or_push(&mut self, address_type: OutputType, typeindex: TypeIndex, index: AnyAddressIndex) -> Result<()> {
|
||||
match address_type {
|
||||
$(OutputType::$variant => self.$field.update_or_push(typeindex.into(), index)?,)*
|
||||
_ => unreachable!("Invalid address type: {:?}", address_type),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get length for a given address type.
|
||||
pub fn len_of(&self, address_type: OutputType) -> usize {
|
||||
match address_type {
|
||||
$(OutputType::$variant => self.$field.len(),)*
|
||||
_ => unreachable!("Invalid address type: {:?}", address_type),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update existing entry (must be within bounds).
|
||||
pub fn update(&mut self, address_type: OutputType, typeindex: TypeIndex, index: AnyAddressIndex) -> Result<()> {
|
||||
match address_type {
|
||||
$(OutputType::$variant => self.$field.update(typeindex.into(), index)?,)*
|
||||
_ => unreachable!("Invalid address type: {:?}", address_type),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Push new entry (must be at exactly len position).
|
||||
pub fn push(&mut self, address_type: OutputType, index: AnyAddressIndex) {
|
||||
match address_type {
|
||||
$(OutputType::$variant => self.$field.push(index),)*
|
||||
_ => unreachable!("Invalid address type: {:?}", address_type),
|
||||
}
|
||||
}
|
||||
|
||||
/// Write all address types with stamp.
|
||||
pub fn write(&mut self, stamp: Stamp, with_changes: bool) -> Result<()> {
|
||||
$(self.$field.stamped_write_maybe_with_changes(stamp, with_changes)?;)*
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a parallel iterator over all vecs for parallel writing.
|
||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
vec![$(&mut self.$field as &mut dyn AnyStoredVec),*].into_par_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: StorageMode> AnyAddressIndexesVecs<M> {
|
||||
/// Get address index with single read (no caching).
|
||||
pub fn get_once(&self, address_type: OutputType, typeindex: TypeIndex) -> Result<AnyAddressIndex> {
|
||||
match address_type {
|
||||
$(OutputType::$variant => self.$field
|
||||
.collect_one(<$index>::from(usize::from(typeindex)))
|
||||
.ok_or_else(|| Error::UnsupportedType(address_type.to_string())),)*
|
||||
_ => Err(Error::UnsupportedType(address_type.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -139,7 +103,7 @@ impl AnyAddressIndexesVecs {
|
||||
/// Accepts two maps (e.g. from empty and funded processing) and merges per-thread.
|
||||
/// Updates existing entries and pushes new ones (sorted).
|
||||
/// Returns (update_count, push_count).
|
||||
pub fn par_batch_update(
|
||||
pub(crate) fn par_batch_update(
|
||||
&mut self,
|
||||
updates1: AddressTypeToTypeIndexMap<AnyAddressIndex>,
|
||||
updates2: AddressTypeToTypeIndexMap<AnyAddressIndex>,
|
||||
|
||||
@@ -3,81 +3,66 @@
|
||||
use brk_cohort::{ByAddressType, zip_by_addresstype};
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredU64, Version};
|
||||
use vecdb::{Database, Exit, TypedVecIterator};
|
||||
use brk_types::{StoredU64, Version};
|
||||
use vecdb::{Database, Exit, Ident, Rw, StorageMode};
|
||||
|
||||
use crate::{ComputeIndexes, indexes, internal::LazyComputedFromHeightFull};
|
||||
|
||||
use super::TotalAddrCountVecs;
|
||||
|
||||
/// New addresses by type - lazy delta with stored dateindex stats
|
||||
pub type NewAddrCountByType = ByAddressType<LazyComputedFromHeightFull<StoredU64, StoredU64>>;
|
||||
/// New addresses by type - identity transform with stored day1 stats
|
||||
/// The delta is computed at the compute step, not lazily
|
||||
pub type NewAddrCountByType<M = Rw> = ByAddressType<LazyComputedFromHeightFull<StoredU64, StoredU64, M>>;
|
||||
|
||||
/// New address count per block (global + per-type)
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct NewAddrCountVecs {
|
||||
pub all: LazyComputedFromHeightFull<StoredU64, StoredU64>,
|
||||
#[derive(Traversable)]
|
||||
pub struct NewAddrCountVecs<M: StorageMode = Rw> {
|
||||
pub all: LazyComputedFromHeightFull<StoredU64, StoredU64, M>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: NewAddrCountByType,
|
||||
pub by_addresstype: NewAddrCountByType<M>,
|
||||
}
|
||||
|
||||
impl NewAddrCountVecs {
|
||||
pub fn forced_import(
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
total_addr_count: &TotalAddrCountVecs,
|
||||
) -> Result<Self> {
|
||||
let all = LazyComputedFromHeightFull::forced_import_with_init(
|
||||
let all = LazyComputedFromHeightFull::forced_import::<Ident>(
|
||||
db,
|
||||
"new_addr_count",
|
||||
version,
|
||||
total_addr_count.all.height.clone(),
|
||||
&total_addr_count.all.height,
|
||||
indexes,
|
||||
delta_init_fn,
|
||||
)?;
|
||||
|
||||
let by_addresstype: NewAddrCountByType = zip_by_addresstype(
|
||||
&total_addr_count.by_addresstype,
|
||||
|name, total| {
|
||||
LazyComputedFromHeightFull::forced_import_with_init(
|
||||
let by_addresstype: NewAddrCountByType =
|
||||
zip_by_addresstype(&total_addr_count.by_addresstype, |name, total| {
|
||||
LazyComputedFromHeightFull::forced_import::<Ident>(
|
||||
db,
|
||||
&format!("{name}_new_addr_count"),
|
||||
version,
|
||||
total.height.clone(),
|
||||
&total.height,
|
||||
indexes,
|
||||
delta_init_fn,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
})?;
|
||||
|
||||
Ok(Self { all, by_addresstype })
|
||||
Ok(Self {
|
||||
all,
|
||||
by_addresstype,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn derive_from(
|
||||
pub(crate) fn compute_cumulative(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.all.derive_from(indexes, starting_indexes, exit)?;
|
||||
self.all.compute_cumulative(starting_indexes, exit)?;
|
||||
for vecs in self.by_addresstype.values_mut() {
|
||||
vecs.derive_from(indexes, starting_indexes, exit)?;
|
||||
vecs.compute_cumulative(starting_indexes, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Delta init function: value[h] = source[h] - source[h-1]
|
||||
fn delta_init_fn(
|
||||
h: Height,
|
||||
total_iter: &mut dyn TypedVecIterator<I = Height, T = StoredU64, Item = StoredU64>,
|
||||
) -> Option<StoredU64> {
|
||||
let current: u64 = total_iter.get(h)?.into();
|
||||
let prev: u64 = h
|
||||
.decremented()
|
||||
.and_then(|prev_h| total_iter.get(prev_h))
|
||||
.map(|v: StoredU64| v.into())
|
||||
.unwrap_or(0);
|
||||
Some(StoredU64::from(current.saturating_sub(prev)))
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ use brk_cohort::{ByAddressType, zip2_by_addresstype};
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{StoredU64, Version};
|
||||
use vecdb::{Database, Exit, IterableCloneableVec};
|
||||
use vecdb::ReadableCloneableVec;
|
||||
|
||||
use crate::{ComputeIndexes, indexes, internal::{LazyBinaryComputedFromHeightLast, U64Plus}};
|
||||
use crate::{indexes, internal::{LazyBinaryComputedFromHeightLast, U64Plus}};
|
||||
|
||||
use super::AddrCountsVecs;
|
||||
|
||||
@@ -23,50 +23,35 @@ pub struct TotalAddrCountVecs {
|
||||
}
|
||||
|
||||
impl TotalAddrCountVecs {
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
pub(crate) fn forced_import(
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
addr_count: &AddrCountsVecs,
|
||||
empty_addr_count: &AddrCountsVecs,
|
||||
) -> Result<Self> {
|
||||
let all = LazyBinaryComputedFromHeightLast::forced_import::<U64Plus>(
|
||||
db,
|
||||
"total_addr_count",
|
||||
version,
|
||||
addr_count.all.count.height.boxed_clone(),
|
||||
empty_addr_count.all.count.height.boxed_clone(),
|
||||
addr_count.all.count.height.read_only_boxed_clone(),
|
||||
empty_addr_count.all.count.height.read_only_boxed_clone(),
|
||||
indexes,
|
||||
)?;
|
||||
);
|
||||
|
||||
let by_addresstype: TotalAddrCountByType = zip2_by_addresstype(
|
||||
&addr_count.by_addresstype,
|
||||
&empty_addr_count.by_addresstype,
|
||||
|name, addr, empty| {
|
||||
LazyBinaryComputedFromHeightLast::forced_import::<U64Plus>(
|
||||
db,
|
||||
Ok(LazyBinaryComputedFromHeightLast::forced_import::<U64Plus>(
|
||||
&format!("{name}_total_addr_count"),
|
||||
version,
|
||||
addr.count.height.boxed_clone(),
|
||||
empty.count.height.boxed_clone(),
|
||||
addr.count.height.read_only_boxed_clone(),
|
||||
empty.count.height.read_only_boxed_clone(),
|
||||
indexes,
|
||||
)
|
||||
))
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(Self { all, by_addresstype })
|
||||
}
|
||||
|
||||
pub fn derive_from(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.all.derive_from(indexes, starting_indexes, exit)?;
|
||||
for vecs in self.by_addresstype.values_mut() {
|
||||
vecs.derive_from(indexes, starting_indexes, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ pub struct HeightToAddressTypeToVec<T>(FxHashMap<Height, AddressTypeToVec<T>>);
|
||||
|
||||
impl<T> HeightToAddressTypeToVec<T> {
|
||||
/// Create with pre-allocated capacity for unique heights.
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
pub(crate) fn with_capacity(capacity: usize) -> Self {
|
||||
Self(FxHashMap::with_capacity_and_hasher(
|
||||
capacity,
|
||||
Default::default(),
|
||||
@@ -20,7 +20,7 @@ impl<T> HeightToAddressTypeToVec<T> {
|
||||
|
||||
impl<T> HeightToAddressTypeToVec<T> {
|
||||
/// Consume and iterate over (Height, AddressTypeToVec) pairs.
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (Height, AddressTypeToVec<T>)> {
|
||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Height, AddressTypeToVec<T>)> {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use rustc_hash::FxHashMap;
|
||||
use smallvec::{Array, SmallVec};
|
||||
|
||||
/// A hashmap for each address type, keyed by TypeIndex.
|
||||
#[derive(Debug, Clone, Deref, DerefMut)]
|
||||
#[derive(Debug, Deref, DerefMut)]
|
||||
pub struct AddressTypeToTypeIndexMap<T>(ByAddressType<FxHashMap<TypeIndex, T>>);
|
||||
|
||||
impl<T> Default for AddressTypeToTypeIndexMap<T> {
|
||||
@@ -27,7 +27,7 @@ impl<T> Default for AddressTypeToTypeIndexMap<T> {
|
||||
|
||||
impl<T> AddressTypeToTypeIndexMap<T> {
|
||||
/// Create with pre-allocated capacity per address type.
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
pub(crate) fn with_capacity(capacity: usize) -> Self {
|
||||
Self(ByAddressType {
|
||||
p2a: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
|
||||
p2pk33: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
|
||||
@@ -40,19 +40,6 @@ impl<T> AddressTypeToTypeIndexMap<T> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Merge two maps, consuming other and extending self.
|
||||
pub fn merge(mut self, mut other: Self) -> Self {
|
||||
Self::merge_single(&mut self.p2a, &mut other.p2a);
|
||||
Self::merge_single(&mut self.p2pk33, &mut other.p2pk33);
|
||||
Self::merge_single(&mut self.p2pk65, &mut other.p2pk65);
|
||||
Self::merge_single(&mut self.p2pkh, &mut other.p2pkh);
|
||||
Self::merge_single(&mut self.p2sh, &mut other.p2sh);
|
||||
Self::merge_single(&mut self.p2tr, &mut other.p2tr);
|
||||
Self::merge_single(&mut self.p2wpkh, &mut other.p2wpkh);
|
||||
Self::merge_single(&mut self.p2wsh, &mut other.p2wsh);
|
||||
self
|
||||
}
|
||||
|
||||
fn merge_single(own: &mut FxHashMap<TypeIndex, T>, other: &mut FxHashMap<TypeIndex, T>) {
|
||||
if own.len() < other.len() {
|
||||
mem::swap(own, other);
|
||||
@@ -61,7 +48,7 @@ impl<T> AddressTypeToTypeIndexMap<T> {
|
||||
}
|
||||
|
||||
/// Merge another map into self, consuming other.
|
||||
pub fn merge_mut(&mut self, mut other: Self) {
|
||||
pub(crate) fn merge_mut(&mut self, mut other: Self) {
|
||||
Self::merge_single(&mut self.p2a, &mut other.p2a);
|
||||
Self::merge_single(&mut self.p2pk33, &mut other.p2pk33);
|
||||
Self::merge_single(&mut self.p2pk65, &mut other.p2pk65);
|
||||
@@ -73,32 +60,23 @@ impl<T> AddressTypeToTypeIndexMap<T> {
|
||||
}
|
||||
|
||||
/// Insert a value for a specific address type and typeindex.
|
||||
pub fn insert_for_type(&mut self, address_type: OutputType, typeindex: TypeIndex, value: T) {
|
||||
pub(crate) fn insert_for_type(&mut self, address_type: OutputType, typeindex: TypeIndex, value: T) {
|
||||
self.get_mut(address_type).unwrap().insert(typeindex, value);
|
||||
}
|
||||
|
||||
/// Iterate over sorted entries by address type.
|
||||
pub fn into_sorted_iter(self) -> impl Iterator<Item = (OutputType, Vec<(TypeIndex, T)>)> {
|
||||
self.0.into_iter().map(|(output_type, map)| {
|
||||
let mut sorted: Vec<_> = map.into_iter().collect();
|
||||
sorted.sort_unstable_by_key(|(typeindex, _)| *typeindex);
|
||||
(output_type, sorted)
|
||||
})
|
||||
}
|
||||
|
||||
/// Consume and iterate over entries by address type.
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (OutputType, FxHashMap<TypeIndex, T>)> {
|
||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = (OutputType, FxHashMap<TypeIndex, T>)> {
|
||||
self.0.into_iter()
|
||||
}
|
||||
|
||||
/// Consume and return the inner ByAddressType.
|
||||
pub fn into_inner(self) -> ByAddressType<FxHashMap<TypeIndex, T>> {
|
||||
pub(crate) fn into_inner(self) -> ByAddressType<FxHashMap<TypeIndex, T>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Iterate mutably over entries by address type.
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (OutputType, &mut FxHashMap<TypeIndex, T>)> {
|
||||
pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = (OutputType, &mut FxHashMap<TypeIndex, T>)> {
|
||||
self.0.iter_mut()
|
||||
}
|
||||
}
|
||||
@@ -108,7 +86,7 @@ where
|
||||
T: Array,
|
||||
{
|
||||
/// Merge two maps of SmallVec values, concatenating vectors.
|
||||
pub fn merge_vec(mut self, other: Self) -> Self {
|
||||
pub(crate) fn merge_vec(mut self, other: Self) -> Self {
|
||||
for (address_type, other_map) in other.0.into_iter() {
|
||||
let self_map = self.0.get_mut_unwrap(address_type);
|
||||
for (typeindex, mut other_vec) in other_map {
|
||||
|
||||
@@ -22,7 +22,7 @@ impl<T> Default for AddressTypeToVec<T> {
|
||||
|
||||
impl<T> AddressTypeToVec<T> {
|
||||
/// Create with pre-allocated capacity per address type.
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
pub(crate) fn with_capacity(capacity: usize) -> Self {
|
||||
Self(ByAddressType {
|
||||
p2a: Vec::with_capacity(capacity),
|
||||
p2pk33: Vec::with_capacity(capacity),
|
||||
@@ -38,7 +38,7 @@ impl<T> AddressTypeToVec<T> {
|
||||
|
||||
impl<T> AddressTypeToVec<T> {
|
||||
/// Unwrap the inner ByAddressType.
|
||||
pub fn unwrap(self) -> ByAddressType<Vec<T>> {
|
||||
pub(crate) fn unwrap(self) -> ByAddressType<Vec<T>> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_types::{AnyAddressDataIndexEnum, FundedAddressData, OutputType, TypeIndex};
|
||||
|
||||
use crate::distribution::{
|
||||
@@ -27,7 +28,7 @@ impl Default for AddressCache {
|
||||
}
|
||||
|
||||
impl AddressCache {
|
||||
pub fn new() -> Self {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
funded: AddressTypeToTypeIndexMap::default(),
|
||||
empty: AddressTypeToTypeIndexMap::default(),
|
||||
@@ -36,7 +37,7 @@ impl AddressCache {
|
||||
|
||||
/// Check if address is in cache (either funded or empty).
|
||||
#[inline]
|
||||
pub fn contains(&self, address_type: OutputType, typeindex: TypeIndex) -> bool {
|
||||
pub(crate) fn contains(&self, address_type: OutputType, typeindex: TypeIndex) -> bool {
|
||||
self.funded
|
||||
.get(address_type)
|
||||
.is_some_and(|m| m.contains_key(&typeindex))
|
||||
@@ -48,13 +49,13 @@ impl AddressCache {
|
||||
|
||||
/// Merge address data into funded cache.
|
||||
#[inline]
|
||||
pub fn merge_funded(&mut self, data: AddressTypeToTypeIndexMap<FundedAddressDataWithSource>) {
|
||||
pub(crate) fn merge_funded(&mut self, data: AddressTypeToTypeIndexMap<FundedAddressDataWithSource>) {
|
||||
self.funded.merge_mut(data);
|
||||
}
|
||||
|
||||
/// Create an AddressLookup view into this cache.
|
||||
#[inline]
|
||||
pub fn as_lookup(&mut self) -> AddressLookup<'_> {
|
||||
pub(crate) fn as_lookup(&mut self) -> AddressLookup<'_> {
|
||||
AddressLookup {
|
||||
funded: &mut self.funded,
|
||||
empty: &mut self.empty,
|
||||
@@ -62,12 +63,12 @@ impl AddressCache {
|
||||
}
|
||||
|
||||
/// Update transaction counts for addresses.
|
||||
pub fn update_tx_counts(&mut self, txindex_vecs: AddressTypeToTypeIndexMap<TxIndexVec>) {
|
||||
pub(crate) fn update_tx_counts(&mut self, txindex_vecs: AddressTypeToTypeIndexMap<TxIndexVec>) {
|
||||
update_tx_counts(&mut self.funded, &mut self.empty, txindex_vecs);
|
||||
}
|
||||
|
||||
/// Take the cache contents for flushing, leaving empty caches.
|
||||
pub fn take(
|
||||
pub(crate) fn take(
|
||||
&mut self,
|
||||
) -> (
|
||||
AddressTypeToTypeIndexMap<EmptyAddressDataWithSource>,
|
||||
@@ -84,7 +85,7 @@ impl AddressCache {
|
||||
///
|
||||
/// Returns None if address is already in cache (funded or empty).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn load_uncached_address_data(
|
||||
pub(crate) fn load_uncached_address_data(
|
||||
address_type: OutputType,
|
||||
typeindex: TypeIndex,
|
||||
first_addressindexes: &ByAddressType<TypeIndex>,
|
||||
@@ -92,38 +93,38 @@ pub fn load_uncached_address_data(
|
||||
vr: &VecsReaders,
|
||||
any_address_indexes: &AnyAddressIndexesVecs,
|
||||
addresses_data: &AddressesDataVecs,
|
||||
) -> Option<FundedAddressDataWithSource> {
|
||||
) -> Result<Option<FundedAddressDataWithSource>> {
|
||||
// Check if this is a new address (typeindex >= first for this height)
|
||||
let first = *first_addressindexes.get(address_type).unwrap();
|
||||
if first <= typeindex {
|
||||
return Some(WithAddressDataSource::New(FundedAddressData::default()));
|
||||
return Ok(Some(WithAddressDataSource::New(FundedAddressData::default())));
|
||||
}
|
||||
|
||||
// Skip if already in cache
|
||||
if cache.contains(address_type, typeindex) {
|
||||
return None;
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Read from storage
|
||||
let reader = vr.address_reader(address_type);
|
||||
let anyaddressindex = any_address_indexes.get(address_type, typeindex, reader);
|
||||
let anyaddressindex = any_address_indexes.get(address_type, typeindex, reader)?;
|
||||
|
||||
Some(match anyaddressindex.to_enum() {
|
||||
Ok(Some(match anyaddressindex.to_enum() {
|
||||
AnyAddressDataIndexEnum::Funded(funded_index) => {
|
||||
let reader = &vr.anyaddressindex_to_anyaddressdata.funded;
|
||||
// Use get_any_or_read_unwrap to check updated layer (needed after rollback)
|
||||
let funded_data = addresses_data
|
||||
.funded
|
||||
.get_any_or_read_unwrap(funded_index, reader);
|
||||
.get_any_or_read_at(funded_index.into(), reader)?
|
||||
.unwrap();
|
||||
WithAddressDataSource::FromFunded(funded_index, funded_data)
|
||||
}
|
||||
AnyAddressDataIndexEnum::Empty(empty_index) => {
|
||||
let reader = &vr.anyaddressindex_to_anyaddressdata.empty;
|
||||
// Use get_any_or_read_unwrap to check updated layer (needed after rollback)
|
||||
let empty_data = addresses_data
|
||||
.empty
|
||||
.get_any_or_read_unwrap(empty_index, reader);
|
||||
.get_any_or_read_at(empty_index.into(), reader)?
|
||||
.unwrap();
|
||||
WithAddressDataSource::FromEmpty(empty_index, empty_data.into())
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ pub struct AddressLookup<'a> {
|
||||
}
|
||||
|
||||
impl<'a> AddressLookup<'a> {
|
||||
pub fn get_or_create_for_receive(
|
||||
pub(crate) fn get_or_create_for_receive(
|
||||
&mut self,
|
||||
output_type: OutputType,
|
||||
type_index: TypeIndex,
|
||||
@@ -79,7 +79,7 @@ impl<'a> AddressLookup<'a> {
|
||||
}
|
||||
|
||||
/// Get address data for a send operation (must exist in cache).
|
||||
pub fn get_for_send(
|
||||
pub(crate) fn get_for_send(
|
||||
&mut self,
|
||||
output_type: OutputType,
|
||||
type_index: TypeIndex,
|
||||
@@ -92,7 +92,7 @@ impl<'a> AddressLookup<'a> {
|
||||
}
|
||||
|
||||
/// Move address from funded to empty set.
|
||||
pub fn move_to_empty(&mut self, output_type: OutputType, type_index: TypeIndex) {
|
||||
pub(crate) fn move_to_empty(&mut self, output_type: OutputType, type_index: TypeIndex) {
|
||||
let data = self
|
||||
.funded
|
||||
.get_mut(output_type)
|
||||
|
||||
@@ -15,7 +15,7 @@ use super::with_source::{EmptyAddressDataWithSource, FundedAddressDataWithSource
|
||||
/// - New funded address: push to funded storage
|
||||
/// - Updated funded address (was funded): update in place
|
||||
/// - Transition empty -> funded: delete from empty, push to funded
|
||||
pub fn process_funded_addresses(
|
||||
pub(crate) fn process_funded_addresses(
|
||||
addresses_data: &mut AddressesDataVecs,
|
||||
funded_updates: AddressTypeToTypeIndexMap<FundedAddressDataWithSource>,
|
||||
) -> Result<AddressTypeToTypeIndexMap<AnyAddressIndex>> {
|
||||
@@ -86,7 +86,7 @@ pub fn process_funded_addresses(
|
||||
/// - New empty address: push to empty storage
|
||||
/// - Updated empty address (was empty): update in place
|
||||
/// - Transition funded -> empty: delete from funded, push to empty
|
||||
pub fn process_empty_addresses(
|
||||
pub(crate) fn process_empty_addresses(
|
||||
addresses_data: &mut AddressesDataVecs,
|
||||
empty_updates: AddressTypeToTypeIndexMap<EmptyAddressDataWithSource>,
|
||||
) -> Result<AddressTypeToTypeIndexMap<AnyAddressIndex>> {
|
||||
|
||||
@@ -4,8 +4,8 @@ mod sent;
|
||||
mod tx_counts;
|
||||
mod with_source;
|
||||
|
||||
pub use address_updates::*;
|
||||
pub use received::*;
|
||||
pub use sent::*;
|
||||
pub use tx_counts::*;
|
||||
pub use with_source::*;
|
||||
pub(crate) use address_updates::*;
|
||||
pub(crate) use received::*;
|
||||
pub(crate) use sent::*;
|
||||
pub(crate) use tx_counts::*;
|
||||
pub(crate) use with_source::*;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use brk_cohort::{AmountBucket, ByAddressType};
|
||||
use brk_types::{CentsUnsigned, Sats, TypeIndex};
|
||||
use brk_types::{Cents, Sats, TypeIndex};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::distribution::{
|
||||
@@ -10,11 +10,11 @@ use crate::distribution::{
|
||||
use super::super::cache::{AddressLookup, TrackingStatus};
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn process_received(
|
||||
pub(crate) fn process_received(
|
||||
received_data: AddressTypeToVec<(TypeIndex, Sats)>,
|
||||
cohorts: &mut AddressCohorts,
|
||||
lookup: &mut AddressLookup<'_>,
|
||||
price: Option<CentsUnsigned>,
|
||||
price: Cents,
|
||||
addr_count: &mut ByAddressType<u64>,
|
||||
empty_addr_count: &mut ByAddressType<u64>,
|
||||
activity_counts: &mut AddressTypeToActivityCounts,
|
||||
@@ -118,7 +118,7 @@ pub fn process_received(
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive_outputs(addr_data, total_value, price.unwrap(), output_count);
|
||||
.receive_outputs(addr_data, total_value, price, output_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_cohort::{AmountBucket, ByAddressType};
|
||||
use brk_error::Result;
|
||||
use brk_types::{Age, CentsUnsigned, CheckedSub, Height, Sats, Timestamp, TypeIndex};
|
||||
use brk_types::{Age, Cents, CheckedSub, Height, Sats, Timestamp, TypeIndex};
|
||||
use rustc_hash::FxHashSet;
|
||||
use vecdb::{VecIndex, unlikely};
|
||||
|
||||
@@ -26,17 +26,17 @@ use super::super::cache::AddressLookup;
|
||||
/// `price_range_max` is used to compute the peak price during each UTXO's holding period
|
||||
/// for accurate peak regret calculation.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn process_sent(
|
||||
pub(crate) fn process_sent(
|
||||
sent_data: HeightToAddressTypeToVec<(TypeIndex, Sats)>,
|
||||
cohorts: &mut AddressCohorts,
|
||||
lookup: &mut AddressLookup<'_>,
|
||||
current_price: Option<CentsUnsigned>,
|
||||
price_range_max: Option<&PriceRangeMax>,
|
||||
current_price: Cents,
|
||||
price_range_max: &PriceRangeMax,
|
||||
addr_count: &mut ByAddressType<u64>,
|
||||
empty_addr_count: &mut ByAddressType<u64>,
|
||||
activity_counts: &mut AddressTypeToActivityCounts,
|
||||
received_addresses: &ByAddressType<FxHashSet<TypeIndex>>,
|
||||
height_to_price: Option<&[CentsUnsigned]>,
|
||||
height_to_price: &[Cents],
|
||||
height_to_timestamp: &[Timestamp],
|
||||
current_height: Height,
|
||||
current_timestamp: Timestamp,
|
||||
@@ -45,22 +45,21 @@ pub fn process_sent(
|
||||
let mut seen_senders: ByAddressType<FxHashSet<TypeIndex>> = ByAddressType::default();
|
||||
|
||||
for (receive_height, by_type) in sent_data.into_iter() {
|
||||
let prev_price = height_to_price.map(|v| v[receive_height.to_usize()]);
|
||||
let prev_price = height_to_price[receive_height.to_usize()];
|
||||
let prev_timestamp = height_to_timestamp[receive_height.to_usize()];
|
||||
let blocks_old = current_height.to_usize() - receive_height.to_usize();
|
||||
let age = Age::new(current_timestamp, prev_timestamp, blocks_old);
|
||||
|
||||
// Compute peak price during holding period for peak regret
|
||||
// This is the max HIGH price between receive and send heights
|
||||
let peak_price: Option<CentsUnsigned> =
|
||||
price_range_max.map(|t| t.max_between(receive_height, current_height));
|
||||
let peak_price = price_range_max.max_between(receive_height, current_height);
|
||||
|
||||
for (output_type, vec) in by_type.unwrap().into_iter() {
|
||||
// Cache mutable refs for this address type
|
||||
let type_addr_count = addr_count.get_mut(output_type).unwrap();
|
||||
let type_empty_count = empty_addr_count.get_mut(output_type).unwrap();
|
||||
let type_activity = activity_counts.get_mut_unwrap(output_type);
|
||||
let type_received = received_addresses.get_unwrap(output_type);
|
||||
let type_received = received_addresses.get(output_type);
|
||||
let type_seen = seen_senders.get_mut_unwrap(output_type);
|
||||
|
||||
for (type_index, value) in vec {
|
||||
@@ -74,7 +73,7 @@ pub fn process_sent(
|
||||
type_activity.sending += 1;
|
||||
|
||||
// Track "both" - addresses that sent AND received this block
|
||||
if type_received.contains(&type_index) {
|
||||
if type_received.is_some_and(|s| s.contains(&type_index)) {
|
||||
type_activity.both += 1;
|
||||
}
|
||||
}
|
||||
@@ -154,9 +153,9 @@ pub fn process_sent(
|
||||
.send(
|
||||
addr_data,
|
||||
value,
|
||||
current_price.unwrap(),
|
||||
prev_price.unwrap(),
|
||||
peak_price.unwrap(),
|
||||
current_price,
|
||||
prev_price,
|
||||
peak_price,
|
||||
age,
|
||||
)?;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user