Compare commits

..

25 Commits

Author SHA1 Message Date
nym21 0a789fe551 release: v0.0.30 2025-04-23 00:07:41 +02:00
nym21 caa8ff23ed kibo: fix charts data fetch 2025-04-23 00:04:09 +02:00
nym21 ee30d1d36d release: v0.0.29 2025-04-22 19:34:55 +02:00
nym21 0d9415db9d kibo: fix add ts-ignore 2025-04-22 19:33:57 +02:00
nym21 8020e1126f kibo: database: part 2 2025-04-22 19:31:30 +02:00
nym21 3439422057 kibo: database: part 1 2025-04-21 23:17:37 +02:00
nym21 68d2bf736f release: v0.0.28 2025-04-19 11:46:05 +02:00
nym21 d78c39fd8c computer + kibo: part 14 - fixes 2025-04-19 11:45:26 +02:00
nym21 b1dcad86b4 release: v0.0.27 2025-04-18 19:41:13 +02:00
nym21 9b6124074d dist: another fix 2025-04-18 19:40:09 +02:00
nym21 02cbaa1e80 release: v0.0.26 2025-04-18 19:00:19 +02:00
nym21 a12f1321c7 dist: fix format tag version 2025-04-18 18:59:53 +02:00
nym21 8b67f592ac release: v0.0.25 2025-04-18 18:48:10 +02:00
nym21 319d17b337 dist: fix ubuntu version 2025-04-18 18:45:27 +02:00
nym21 476eaa85da github: add manual trigger 2025-04-18 18:37:51 +02:00
nym21 d26099855c dist: update ubuntu version 2025-04-18 18:36:18 +02:00
nym21 e47456da17 release: v0.0.24 2025-04-18 18:22:23 +02:00
nym21 a464d5d0b6 crates: upgrade 2025-04-18 18:20:40 +02:00
nym21 1cfb7b5615 computer + kibo: part 13 2025-04-18 18:08:11 +02:00
nym21 ac7c2f3d03 kibo: cleanup 2025-04-17 17:06:44 +02:00
nym21 638d9e6e01 computer: part 12 2025-04-17 15:22:34 +02:00
nym21 8b9df2a396 parser: readme 2025-04-15 14:40:56 +02:00
nym21 d7fe911bde parser: readme 2025-04-15 11:56:50 +02:00
nym21 0acc3d511b parser: readme 2025-04-15 11:27:48 +02:00
nym21 4cf465f419 computer: unwrap 2025-04-15 11:18:31 +02:00
46 changed files with 4553 additions and 2154 deletions
+4 -4
View File
@@ -47,7 +47,7 @@ on:
jobs:
# Run 'dist plan' (or host) to determine what tasks we need to do
plan:
runs-on: "ubuntu-20.04"
runs-on: "ubuntu-latest"
outputs:
val: ${{ steps.plan.outputs.manifest }}
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
@@ -168,7 +168,7 @@ jobs:
needs:
- plan
- build-local-artifacts
runs-on: "ubuntu-20.04"
runs-on: "ubuntu-latest"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
@@ -218,7 +218,7 @@ jobs:
if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: "ubuntu-20.04"
runs-on: "ubuntu-latest"
outputs:
val: ${{ steps.host.outputs.manifest }}
steps:
@@ -282,7 +282,7 @@ jobs:
# still allowing individual publish jobs to skip themselves (for prereleases).
# "host" however must run to completion, no skipping allowed!
if: ${{ always() && needs.host.result == 'success' }}
runs-on: "ubuntu-20.04"
runs-on: "ubuntu-latest"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
Generated
+92 -90
View File
@@ -152,9 +152,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "async-compression"
version = "0.4.22"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a194f9d963d8099596278594b3107448656ba73831c9d8c783e613ce86da64"
checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07"
dependencies = [
"brotli",
"flate2",
@@ -374,7 +374,7 @@ dependencies = [
[[package]]
name = "brk"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"brk_cli",
"brk_computer",
@@ -391,7 +391,7 @@ dependencies = [
[[package]]
name = "brk_cli"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"brk_computer",
"brk_core",
@@ -412,7 +412,7 @@ dependencies = [
[[package]]
name = "brk_computer"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"brk_core",
"brk_exit",
@@ -427,7 +427,7 @@ dependencies = [
[[package]]
name = "brk_core"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -444,7 +444,7 @@ dependencies = [
[[package]]
name = "brk_exit"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"brk_logger",
"ctrlc",
@@ -453,7 +453,7 @@ dependencies = [
[[package]]
name = "brk_fetcher"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"brk_core",
"brk_logger",
@@ -466,7 +466,7 @@ dependencies = [
[[package]]
name = "brk_indexer"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -485,7 +485,7 @@ dependencies = [
[[package]]
name = "brk_logger"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"color-eyre",
"env_logger",
@@ -495,7 +495,7 @@ dependencies = [
[[package]]
name = "brk_parser"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -510,7 +510,7 @@ dependencies = [
[[package]]
name = "brk_query"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"brk_computer",
"brk_indexer",
@@ -526,7 +526,7 @@ dependencies = [
[[package]]
name = "brk_server"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"axum",
"brk_computer",
@@ -552,7 +552,7 @@ dependencies = [
[[package]]
name = "brk_vec"
version = "0.0.23"
version = "0.0.30"
dependencies = [
"arc-swap",
"axum",
@@ -566,9 +566,9 @@ dependencies = [
[[package]]
name = "brotli"
version = "7.0.0"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
checksum = "cf19e729cdbd51af9a397fb9ef8ac8378007b797f8273cfbfdf45dcaa316167b"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@@ -577,9 +577,9 @@ dependencies = [
[[package]]
name = "brotli-decompressor"
version = "4.0.2"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37"
checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@@ -694,9 +694,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.36"
version = "4.5.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
dependencies = [
"clap_builder",
"clap_derive",
@@ -704,9 +704,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.36"
version = "4.5.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
dependencies = [
"anstream",
"anstyle",
@@ -1107,9 +1107,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "fjall"
version = "2.8.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b2ced3483989a62b3533c9f99054d73b527c6c0045cf22b00fe87956f1a46f"
checksum = "958511f67d1f80e6bff9ffac05c626bb340d4602ca6ea5617d9901c218c894f0"
dependencies = [
"byteorder",
"byteview",
@@ -1138,6 +1138,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
@@ -1252,6 +1258,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
]
[[package]]
@@ -1470,9 +1478,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jiff"
version = "0.2.8"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0"
checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6"
dependencies = [
"jiff-static",
"jiff-tzdb-platform",
@@ -1485,9 +1493,9 @@ dependencies = [
[[package]]
name = "jiff-static"
version = "0.2.8"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605"
checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254"
dependencies = [
"proc-macro2",
"quote",
@@ -1549,9 +1557,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.171"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "linux-raw-sys"
@@ -1569,12 +1577,6 @@ dependencies = [
"scopeguard",
]
[[package]]
name = "lockfree-object-pool"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
[[package]]
name = "log"
version = "0.4.27"
@@ -1583,9 +1585,9 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "lsm-tree"
version = "2.8.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a63a5e98a38b51765274137d8aedfbd848da5f4d016867e186b673fcc06a8c"
checksum = "87d58bdef2dcbf50fce9f343265bdbd7fb08a458d241eb837ce426be22d674b4"
dependencies = [
"byteorder",
"crossbeam-skiplist",
@@ -1789,9 +1791,9 @@ checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564"
[[package]]
name = "oxc"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa680279066565502d5cc612d82dc2841499518f5430d047aff33869ec5592a5"
checksum = "339134dd7e4bb36c2d0e97601fe4193b37649f729e94e7c26667feecd439b27e"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1832,9 +1834,9 @@ dependencies = [
[[package]]
name = "oxc_allocator"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f90c485c4f2781e0c8b4703a5fa205f49ecc6d496d2b88260db27d60f9f1661"
checksum = "56c3dd872f5f819775c28163977399a1dd4f2177f3745dcb1bfd4214bae27e43"
dependencies = [
"allocator-api2",
"bumpalo",
@@ -1846,9 +1848,9 @@ dependencies = [
[[package]]
name = "oxc_ast"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2552b2248c0d320c97f865d0620946821e687a0c02200cc6aa08161862447f8d"
checksum = "a1bebb401e36d73f35400ee9609ab6372b457543ce7bb1ef17c922235f24ba7b"
dependencies = [
"bitflags",
"cow-utils",
@@ -1863,9 +1865,9 @@ dependencies = [
[[package]]
name = "oxc_ast_macros"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccd0b70b7752610fc00a22e3cbf92fa2b0a007b022a976fba75a254f6ea21f18"
checksum = "3b997c2a787321814d6e92f212486b4d590af4dbfff5c2265d3b0386e016eafe"
dependencies = [
"proc-macro2",
"quote",
@@ -1874,9 +1876,9 @@ dependencies = [
[[package]]
name = "oxc_ast_visit"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef6a46d3d3158668e5d8a9231bce6f5c8abc4a818a48583a254f4aa4a9682fa0"
checksum = "e1ee363bd1ff2000ae2a1052bb4d68e827e62f76e0139449ad1301ad9adc6ac4"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1886,9 +1888,9 @@ dependencies = [
[[package]]
name = "oxc_cfg"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e0e5fa83ea1b3bbcc5496cf6acbbc46f7d147cbd342ecf968df500239c73bd"
checksum = "77cb606168f768c747885ae48272287a028de5374db865c29d6d307d3b601a97"
dependencies = [
"bitflags",
"itertools",
@@ -1901,9 +1903,9 @@ dependencies = [
[[package]]
name = "oxc_codegen"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "454e75b9152f3aea41e4de64154bcb4a261a50df75746b08136b9994130f3a00"
checksum = "4afef88d94fe9eee073332e3f908bd429baa989300d83152dab10dad99bd2a66"
dependencies = [
"bitflags",
"cow-utils",
@@ -1922,15 +1924,15 @@ dependencies = [
[[package]]
name = "oxc_data_structures"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77ea3b4eae572b066a314edd33ccd07e0d0b148bdea635fdd1a44ff97b1a9fb"
checksum = "235fe6251a3f06813e77b45f3cd9c7ec5d0a23741c33f6c308b5af393d5d2409"
[[package]]
name = "oxc_diagnostics"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5f38bf4f67832d521bcd99a79d4483e9b571d37c81e6b09a0a397b18931df8b"
checksum = "2cd0f62519773d942baf1a17474e53b535182182f2cb41319ad1c2782e34310d"
dependencies = [
"cow-utils",
"oxc-miette",
@@ -1938,9 +1940,9 @@ dependencies = [
[[package]]
name = "oxc_ecmascript"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a754f033637a086a80d7f81620ff1123f20ad70789f37873bc635ec89f0021"
checksum = "5d073fdd2a3e1adb63731faab3c3861d0f9244cc15e88453d78460a4b2a604ca"
dependencies = [
"cow-utils",
"num-bigint",
@@ -1952,9 +1954,9 @@ dependencies = [
[[package]]
name = "oxc_estree"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b7edeb47c4700027cc113d22ed899df45fda2ed5b29af2e471cad2be09244d"
checksum = "59df015cfd58d084b27d049281835094556a4469db3e2ac45b0676336e0aeed8"
[[package]]
name = "oxc_index"
@@ -1964,9 +1966,9 @@ checksum = "2fa07b0cfa997730afed43705766ef27792873fdf5215b1391949fec678d2392"
[[package]]
name = "oxc_mangler"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a837c659bb2e340f491d2196d4565d8e75ffa19e7a8c6b35eeab67159cda546"
checksum = "7dfc3363726f4ade77ba4d9e56cc38cc306436feee7b72e1c69436fb30c02b9b"
dependencies = [
"fixedbitset",
"itertools",
@@ -1981,9 +1983,9 @@ dependencies = [
[[package]]
name = "oxc_minifier"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d186a202e49cebf004903e45eccecc3fc8bd10d4f6803ad73eb9eba51b4770b4"
checksum = "f095580f12a6277d4cb532310ba95cc6d1f719b6c59341fb392b71e37d1207e6"
dependencies = [
"cow-utils",
"oxc_allocator",
@@ -2003,9 +2005,9 @@ dependencies = [
[[package]]
name = "oxc_parser"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a54ce76e891cdfa2bf1b946492e94f17a5d6a5d9731e59aaa1b4ae435f8439ef"
checksum = "8d54fd003e5347dcb5ccdcc96cb283bf32a648ff291912b5a6e5e718b3359935"
dependencies = [
"bitflags",
"cow-utils",
@@ -2026,9 +2028,9 @@ dependencies = [
[[package]]
name = "oxc_regular_expression"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d43967a36f4ce1577099e3d70927845fb9f1cbfdd57298e7fd69897c1f434d"
checksum = "5e6e8058355e43a2388112eddf73031d8749b40255ad6f3a824e35fe0c6addec"
dependencies = [
"oxc_allocator",
"oxc_ast_macros",
@@ -2042,9 +2044,9 @@ dependencies = [
[[package]]
name = "oxc_semantic"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c8f0eb991e07aa1aab2efd85f61879452374d406145bdc6d32681ddda8d74c8"
checksum = "3f9f297db174c99b43e1b268b0960dcc588767093575cc39bb5517196a7a21d3"
dependencies = [
"itertools",
"oxc_allocator",
@@ -2078,9 +2080,9 @@ dependencies = [
[[package]]
name = "oxc_span"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cdfaea137d47979b406de82e3b3c13f03b4bda4bc237c1b6fdbb8c46b436e70"
checksum = "69575b94d8fdac097784abf48a5e43ee38375aa377f8e9bb8bc80f2ef3d2269c"
dependencies = [
"compact_str",
"oxc-miette",
@@ -2091,9 +2093,9 @@ dependencies = [
[[package]]
name = "oxc_syntax"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb675497fc1051b3cec9c5cfa224ead942a830c8583394deaadcae5eb54669fa"
checksum = "32c4940ed3e38ce51fb6bc3e880edb815182bdcdc19c6dc110857625aac9c9a7"
dependencies = [
"bitflags",
"cow-utils",
@@ -2112,9 +2114,9 @@ dependencies = [
[[package]]
name = "oxc_traverse"
version = "0.63.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f89e6801eb9ef451e1c785fdee237050be91da172b9fbcad849f9703d37a8ae8"
checksum = "5f8868dcb4ac2cd1e08dadbf34cd4fd9bbf36b70ef64dfa3376e1042a9f705a3"
dependencies = [
"compact_str",
"itoa",
@@ -2199,12 +2201,14 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "petgraph"
version = "0.7.1"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
checksum = "7a98c6720655620a521dcc722d0ad66cd8afd5d86e34a89ef691c50b7b24de06"
dependencies = [
"fixedbitset",
"hashbrown 0.15.2",
"indexmap 2.9.0",
"serde",
]
[[package]]
@@ -2321,18 +2325,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.94"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quick_cache"
version = "0.6.12"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f8ed0655cbaf18a26966142ad23b95d8ab47221c50c4f73a1db7d0d2d6e3da8"
checksum = "287e56aac5a2b4fb25a6fb050961d157635924c8696305a5c937a76f29841a0f"
dependencies = [
"equivalent",
"hashbrown 0.15.2",
@@ -2714,9 +2718,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [
"libc",
]
@@ -3149,9 +3153,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "value-log"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd29b17c041f94e0885179637289815cd038f0c9fc19c4549d5a97017404fb7d"
checksum = "62fc7c4ce161f049607ecea654dca3f2d727da5371ae85e2e4f14ce2b98ed67c"
dependencies = [
"byteorder",
"byteview",
@@ -3505,15 +3509,13 @@ dependencies = [
[[package]]
name = "zopfli"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946"
checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7"
dependencies = [
"bumpalo",
"crc32fast",
"lockfree-object-pool",
"log",
"once_cell",
"simd-adler32",
]
+12 -4
View File
@@ -4,7 +4,7 @@ members = ["crates/*"]
package.description = "The Bitcoin Research Kit is a suite of tools designed to extract, compute and display data stored on a Bitcoin Core node"
package.license = "MIT"
package.edition = "2024"
package.version = "0.0.23"
package.version = "0.0.30"
package.repository = "https://github.com/bitcoinresearchkit/brk"
[profile.release]
@@ -31,11 +31,11 @@ brk_query = { version = "0", path = "crates/brk_query" }
brk_server = { version = "0", path = "crates/brk_server" }
brk_vec = { version = "0", path = "crates/brk_vec" }
byteview = "0.6.1"
clap = { version = "4.5.36", features = ["derive", "string"] }
clap = { version = "4.5.37", features = ["derive", "string"] }
color-eyre = "0.6.3"
derive_deref = "1.1.1"
fjall = "2.8.0"
jiff = "0.2.8"
fjall = "2.9.0"
jiff = "0.2.10"
log = { version = "0.4.27" }
minreq = { version = "2.13.4", features = ["https", "serde_json"] }
rayon = "1.10.0"
@@ -60,3 +60,11 @@ targets = [
"x86_64-apple-darwin",
"x86_64-unknown-linux-gnu",
]
[workspace.metadata.dist.github-custom-runners]
global = "ubuntu-latest"
aarch64-apple-darwin.runner = "macos-14"
x86_64-unknown-linux-gnu.runner = "ubuntu-latest"
x86_64-unknown-linux-gnu.container = { image = "quay.io/pypa/manylinux_2_28_x86_64", host = "x86_64-unknown-linux-musl" }
aarch64-unknown-linux-gnu.runner = "ubuntu-latest"
aarch64-unknown-linux-gnu.container = { image = "quay.io/pypa/manylinux_2_28_x86_64", host = "x86_64-unknown-linux-musl" }
+1
View File
@@ -92,6 +92,7 @@ Heartfelt thanks go out to every donor on [Nostr](https://primal.net/p/npub1jagm
If you'd like to have your own instance hosted for you please contact [hosting@bitcoinresearchkit.org](mailto:hosting@bitcoinresearchkit.org).
- 2 separate dedicated servers (1 GB/s each) with different ISPs and Cloudflare integration for enhanced performance and optimal availability
- 99.9% SLA
- Direct communication for feature requests and support
- Updates delivered at your convenience
- Optional subdomains: `*.bitcoinresearchkit.org`, `*.kibo.money` and `*.satonomics.xyz`
+112 -13
View File
@@ -6,11 +6,10 @@ use std::{
path::{Path, PathBuf},
};
use brk_core::CheckedSub;
use brk_core::{Bitcoin, CheckedSub, Close, Dollars, Height, Sats, Txindex};
use brk_exit::Exit;
use brk_vec::{
Compressed, DynamicVec, Error, GenericVec, Result, StoredIndex, StoredType, StoredVec, Value,
Version,
Compressed, DynamicVec, Error, GenericVec, Result, StoredIndex, StoredType, StoredVec, Version,
};
use log::info;
@@ -120,8 +119,12 @@ where
&mut self.inner
}
pub fn cached_get(&mut self, index: I) -> Result<Option<Value<T>>> {
self.inner.cached_get(index)
pub fn unwrap_cached_get(&mut self, index: I) -> Option<T> {
self.inner.unwrap_cached_get(index)
}
#[inline]
pub fn double_unwrap_cached_get(&mut self, index: I) -> T {
self.inner.double_unwrap_cached_get(index)
}
pub fn collect_inclusive_range(&self, from: I, to: I) -> Result<Vec<T>> {
@@ -151,6 +154,30 @@ where
Ok(())
}
pub fn compute_range<A, F>(
&mut self,
max_from: I,
other: &mut StoredVec<I, A>,
mut t: F,
exit: &Exit,
) -> Result<()>
where
A: StoredType,
F: FnMut(I) -> (I, T),
{
self.validate_computed_version_or_reset_file(
Version::ZERO + self.version() + other.version(),
)?;
let index = max_from.min(I::from(self.len()));
(index.to_usize()?..other.len()).try_for_each(|i| {
let (i, v) = t(I::from(i));
self.forced_push_at(i, v, exit)
})?;
self.safe_flush(exit)
}
pub fn compute_transform<A, B, F>(
&mut self,
max_from: A,
@@ -196,7 +223,7 @@ where
.map_or_else(T::default, |v| v.into_inner()),
);
other.iter_from(index, |(v, i, ..)| {
if self.cached_get(i).unwrap().is_none_or(|old_v| *old_v > v) {
if self.unwrap_cached_get(i).is_none_or(|old_v| old_v > v) {
self.forced_push_at(i, v, exit)
} else {
Ok(())
@@ -224,7 +251,7 @@ where
let index = max_from.min(T::from(self.len()));
first_indexes.iter_from(index, |(value, first_index, ..)| {
let first_index = (first_index).to_usize()?;
let last_index = (last_indexes.cached_get(value)?.unwrap()).to_usize()?;
let last_index = (last_indexes.double_unwrap_cached_get(value)).to_usize()?;
(first_index..last_index)
.try_for_each(|index| self.forced_push_at(I::from(index), value, exit))
})?;
@@ -344,8 +371,8 @@ where
let index = max_from.min(I::from(self.len()));
first_indexes.iter_from(index, |(i, first_index, ..)| {
let last_index = last_indexes.cached_get(i)?.unwrap().into_inner();
let range = first_index.to_usize().unwrap()..=last_index.to_usize().unwrap();
let last_index = last_indexes.double_unwrap_cached_get(i);
let range = first_index.unwrap_to_usize()..=last_index.unwrap_to_usize();
let count = if let Some(filter) = filter.as_mut() {
range.into_iter().filter(|i| filter(T2::from(*i))).count()
} else {
@@ -377,7 +404,7 @@ where
self_to_other.iter_from(index, |(i, other, ..)| {
self.forced_push_at(
i,
T::from(other_to_self.cached_get(other)?.unwrap().into_inner() == i),
T::from(other_to_self.double_unwrap_cached_get(other) == i),
exit,
)
})?;
@@ -403,11 +430,11 @@ where
let index = max_from.min(I::from(self.len()));
first_indexes.iter_from(index, |(i, first_index, ..)| {
let last_index = last_indexes.cached_get(i)?.unwrap().into_inner();
let range = first_index.to_usize().unwrap()..=last_index.to_usize().unwrap();
let last_index = last_indexes.double_unwrap_cached_get(i);
let range = first_index.unwrap_to_usize()..=last_index.unwrap_to_usize();
let mut sum = T::from(0_usize);
range.into_iter().for_each(|i| {
sum = sum.clone() + source.cached_get_(i).unwrap().unwrap().into_inner();
sum = sum.clone() + source.double_unwrap_cached_get(T2::from(i));
});
self.forced_push_at(i, sum, exit)
})?;
@@ -416,6 +443,78 @@ where
}
}
impl<I> ComputedVec<I, Bitcoin>
where
I: StoredIndex,
{
pub fn compute_from_sats(
&mut self,
max_from: I,
sats: &mut StoredVec<I, Sats>,
exit: &Exit,
) -> Result<()> {
self.validate_computed_version_or_reset_file(
Version::ZERO + self.version() + sats.version(),
)?;
let index = max_from.min(I::from(self.len()));
sats.iter_from(index, |(i, sats, ..)| {
let (i, v) = (i, Bitcoin::from(sats));
self.forced_push_at(i, v, exit)
})?;
self.safe_flush(exit)
}
}
impl ComputedVec<Height, Dollars> {
pub fn compute_from_bitcoin(
&mut self,
max_from: Height,
bitcoin: &mut StoredVec<Height, Bitcoin>,
price: &mut StoredVec<Height, Close<Dollars>>,
exit: &Exit,
) -> Result<()> {
self.validate_computed_version_or_reset_file(
Version::ZERO + self.version() + bitcoin.version(),
)?;
let index = max_from.min(Height::from(self.len()));
bitcoin.iter_from(index, |(i, bitcoin, ..)| {
let dollars = price.double_unwrap_cached_get(i);
let (i, v) = (i, *dollars * bitcoin);
self.forced_push_at(i, v, exit)
})?;
self.safe_flush(exit)
}
}
impl ComputedVec<Txindex, Dollars> {
pub fn compute_from_bitcoin(
&mut self,
max_from: Txindex,
bitcoin: &mut StoredVec<Txindex, Bitcoin>,
i_to_height: &mut StoredVec<Txindex, Height>,
price: &mut StoredVec<Height, Close<Dollars>>,
exit: &Exit,
) -> Result<()> {
self.validate_computed_version_or_reset_file(
Version::ZERO + self.version() + bitcoin.version(),
)?;
let index = max_from.min(Txindex::from(self.len()));
bitcoin.iter_from(index, |(i, bitcoin, ..)| {
let height = i_to_height.double_unwrap_cached_get(i);
let dollars = price.double_unwrap_cached_get(height);
let (i, v) = (i, *dollars * bitcoin);
self.forced_push_at(i, v, exit)
})?;
self.safe_flush(exit)
}
}
impl<I, T> Clone for ComputedVec<I, T>
where
I: StoredIndex,
@@ -97,7 +97,7 @@ impl Vecs {
indexer.mut_vecs().height_to_timestamp.mut_vec(),
|(height, timestamp, _, height_to_timestamp)| {
let interval = height.decremented().map_or(Timestamp::ZERO, |prev_h| {
let prev_timestamp = *height_to_timestamp.cached_get(prev_h).unwrap().unwrap();
let prev_timestamp = height_to_timestamp.double_unwrap_cached_get(prev_h);
timestamp
.checked_sub(prev_timestamp)
.unwrap_or(Timestamp::ZERO)
@@ -120,10 +120,10 @@ impl Vecs {
starting_indexes,
exit,
|v, indexer, _, starting_indexes, exit| {
v.compute_transform(
v.compute_range(
starting_indexes.height,
indexer.mut_vecs().height_to_weight.mut_vec(),
|(h, ..)| (h, StoredU32::from(1_u32)),
|h| (h, StoredU32::from(1_u32)),
exit,
)
},
@@ -16,20 +16,22 @@ where
I: StoredIndex,
T: ComputedType,
{
pub first: Option<ComputedVec<I, T>>,
pub average: Option<ComputedVec<I, T>>,
pub sum: Option<ComputedVec<I, T>>,
pub max: Option<ComputedVec<I, T>>,
pub _90p: Option<ComputedVec<I, T>>,
pub _75p: Option<ComputedVec<I, T>>,
pub median: Option<ComputedVec<I, T>>,
pub _25p: Option<ComputedVec<I, T>>,
pub _10p: Option<ComputedVec<I, T>>,
pub min: Option<ComputedVec<I, T>>,
pub last: Option<ComputedVec<I, T>>,
pub total: Option<ComputedVec<I, T>>,
first: Option<ComputedVec<I, T>>,
average: Option<ComputedVec<I, T>>,
sum: Option<ComputedVec<I, T>>,
max: Option<ComputedVec<I, T>>,
_90p: Option<ComputedVec<I, T>>,
_75p: Option<ComputedVec<I, T>>,
median: Option<ComputedVec<I, T>>,
_25p: Option<ComputedVec<I, T>>,
_10p: Option<ComputedVec<I, T>>,
min: Option<ComputedVec<I, T>>,
last: Option<ComputedVec<I, T>>,
total: Option<ComputedVec<I, T>>,
}
const VERSION: Version = Version::ZERO;
impl<I, T> ComputedVecBuilder<I, T>
where
I: StoredIndex,
@@ -38,6 +40,7 @@ where
pub fn forced_import(
path: &Path,
name: &str,
version: Version,
compressed: Compressed,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
@@ -67,50 +70,100 @@ where
}
};
let version = VERSION + version;
let s = Self {
first: options.first.then(|| {
ComputedVec::forced_import(&maybe_prefix("first"), Version::ZERO, compressed)
.unwrap()
ComputedVec::forced_import(
&maybe_prefix("first"),
version + Version::ZERO,
compressed,
)
.unwrap()
}),
last: options.last.then(|| {
ComputedVec::forced_import(
&path.join(format!("{key}_to_{name}")),
Version::ZERO,
version + Version::ZERO,
compressed,
)
.unwrap()
}),
min: options.min.then(|| {
ComputedVec::forced_import(&maybe_suffix("min"), Version::ZERO, compressed).unwrap()
ComputedVec::forced_import(
&maybe_suffix("min"),
version + Version::ZERO,
compressed,
)
.unwrap()
}),
max: options.max.then(|| {
ComputedVec::forced_import(&maybe_suffix("max"), Version::ZERO, compressed).unwrap()
ComputedVec::forced_import(
&maybe_suffix("max"),
version + Version::ZERO,
compressed,
)
.unwrap()
}),
median: options.median.then(|| {
ComputedVec::forced_import(&maybe_suffix("median"), Version::ZERO, compressed)
.unwrap()
ComputedVec::forced_import(
&maybe_suffix("median"),
version + Version::ZERO,
compressed,
)
.unwrap()
}),
average: options.average.then(|| {
ComputedVec::forced_import(&maybe_suffix("average"), Version::ZERO, compressed)
.unwrap()
ComputedVec::forced_import(
&maybe_suffix("average"),
version + Version::ZERO,
compressed,
)
.unwrap()
}),
sum: options.sum.then(|| {
ComputedVec::forced_import(&maybe_suffix("sum"), Version::ZERO, compressed).unwrap()
ComputedVec::forced_import(
&maybe_suffix("sum"),
version + Version::ZERO,
compressed,
)
.unwrap()
}),
total: options.total.then(|| {
ComputedVec::forced_import(&prefix("total"), Version::ZERO, compressed).unwrap()
ComputedVec::forced_import(&prefix("total"), version + Version::ZERO, compressed)
.unwrap()
}),
_90p: options._90p.then(|| {
ComputedVec::forced_import(&maybe_suffix("90p"), Version::ZERO, compressed).unwrap()
ComputedVec::forced_import(
&maybe_suffix("90p"),
version + Version::ZERO,
compressed,
)
.unwrap()
}),
_75p: options._75p.then(|| {
ComputedVec::forced_import(&maybe_suffix("75p"), Version::ZERO, compressed).unwrap()
ComputedVec::forced_import(
&maybe_suffix("75p"),
version + Version::ZERO,
compressed,
)
.unwrap()
}),
_25p: options._25p.then(|| {
ComputedVec::forced_import(&maybe_suffix("25p"), Version::ZERO, compressed).unwrap()
ComputedVec::forced_import(
&maybe_suffix("25p"),
version + Version::ZERO,
compressed,
)
.unwrap()
}),
_10p: options._10p.then(|| {
ComputedVec::forced_import(&maybe_suffix("10p"), Version::ZERO, compressed).unwrap()
ComputedVec::forced_import(
&maybe_suffix("10p"),
version + Version::ZERO,
compressed,
)
.unwrap()
}),
};
@@ -128,14 +181,12 @@ where
source.iter_from(index, |(i, v, ..)| {
let prev = i
.to_usize()
.unwrap()
.unwrap_to_usize()
.checked_sub(1)
.map_or(T::from(0_usize), |prev_i| {
total_vec
.cached_get(I::from(prev_i))
.unwrap()
.map_or(T::from(0_usize), |v| v.into_inner())
.unwrap_cached_get(I::from(prev_i))
.unwrap_or(T::from(0_usize))
});
let value = v.clone() + prev;
total_vec.forced_push_at(i, value, exit)?;
@@ -158,27 +209,19 @@ where
) -> Result<()>
where
I2: StoredIndex + StoredType,
T: Ord + From<f64>,
f64: From<T>,
{
let index = self.starting_index(max_from);
first_indexes.iter_from(index, |(i, first_index, first_indexes)| {
let last_index = last_indexes.cached_get(i)?.unwrap().into_inner();
let last_index = last_indexes.double_unwrap_cached_get(i);
if let Some(first) = self.first.as_mut() {
let v = source.cached_get(first_index)?.unwrap().into_inner();
let v = source.double_unwrap_cached_get(first_index);
first.forced_push_at(index, v, exit)?;
}
if let Some(last) = self.last.as_mut() {
let v = source
.cached_get(last_index)
.inspect_err(|_| {
dbg!(last.path(), last_index);
})?
.unwrap()
.into_inner();
let v = source.double_unwrap_cached_get(last_index);
last.forced_push_at(index, v, exit)?;
}
@@ -250,36 +293,25 @@ where
if needs_average_sum_or_total {
let len = values.len();
let sum = values.into_iter().fold(T::from(0), |a, b| a + b);
if let Some(average) = self.average.as_mut() {
let len = len as f64;
let total = values
.iter()
.map(|v| f64::from(v.clone()))
.fold(0.0, |a, b| a + b);
let avg = T::from(total / len);
let avg = sum.clone() / len;
average.forced_push_at(i, avg, exit)?;
}
if needs_sum_or_total {
let sum = values.into_iter().fold(T::from(0), |a, b| a + b);
if let Some(sum_vec) = self.sum.as_mut() {
sum_vec.forced_push_at(i, sum.clone(), exit)?;
}
if let Some(total_vec) = self.total.as_mut() {
let prev = i.to_usize().unwrap().checked_sub(1).map_or(
T::from(0_usize),
|prev_i| {
total_vec
.cached_get(I::from(prev_i))
.unwrap()
.unwrap()
.to_owned()
.into_inner()
},
);
let prev = i
.unwrap_to_usize()
.checked_sub(1)
.map_or(T::from(0_usize), |prev_i| {
total_vec.double_unwrap_cached_get(I::from(prev_i))
});
total_vec.forced_push_at(i, prev + sum, exit)?;
}
}
@@ -305,8 +337,6 @@ where
) -> Result<()>
where
I2: StoredIndex + StoredType,
T: Ord + From<f64>,
f64: From<T>,
{
if self._90p.is_some()
|| self._75p.is_some()
@@ -320,17 +350,14 @@ where
let index = self.starting_index(max_from);
first_indexes.iter_from(index, |(i, first_index, ..)| {
let last_index = *last_indexes.cached_get(i).unwrap().unwrap();
let last_index = last_indexes.double_unwrap_cached_get(i);
if let Some(first) = self.first.as_mut() {
let v = source
.first
.as_mut()
.unwrap()
.cached_get(first_index)
.unwrap()
.unwrap()
.into_inner();
.double_unwrap_cached_get(first_index);
first.forced_push_at(index, v, exit)?;
}
@@ -339,10 +366,7 @@ where
.last
.as_mut()
.unwrap()
.cached_get(last_index)
.unwrap()
.unwrap()
.into_inner();
.double_unwrap_cached_get(last_index);
last.forced_push_at(index, v, exit)?;
}
@@ -381,14 +405,12 @@ where
.as_ref()
.unwrap()
.collect_inclusive_range(first_index, last_index)?;
let len = values.len() as f64;
let total = values
.into_iter()
.map(|v| f64::from(v))
.fold(0.0, |a, b| a + b);
let len = values.len();
let total = values.into_iter().fold(T::from(0), |a, b| a + b);
// TODO: Multiply by count then divide by total
// Right now it's not 100% accurate as there could be more or less elements in the lower timeframe (28 days vs 31 days in a month for example)
let avg = T::from(total / len);
let avg = total / len;
average.forced_push_at(i, avg, exit)?;
}
@@ -398,6 +420,7 @@ where
.as_ref()
.unwrap()
.collect_inclusive_range(first_index, last_index)?;
let sum = values.into_iter().fold(T::from(0), |a, b| a + b);
if let Some(sum_vec) = self.sum.as_mut() {
@@ -405,16 +428,12 @@ where
}
if let Some(total_vec) = self.total.as_mut() {
let prev = i.to_usize().unwrap().checked_sub(1).map_or(
T::from(0_usize),
|prev_i| {
total_vec
.cached_get(I::from(prev_i))
.unwrap()
.unwrap()
.into_inner()
},
);
let prev = i
.unwrap_to_usize()
.checked_sub(1)
.map_or(T::from(0_usize), |prev_i| {
total_vec.double_unwrap_cached_get(I::from(prev_i))
});
total_vec.forced_push_at(i, prev + sum, exit)?;
}
}
@@ -457,6 +476,43 @@ where
))
}
pub fn unwrap_first(&mut self) -> &mut ComputedVec<I, T> {
self.first.as_mut().unwrap()
}
pub fn unwrap_average(&mut self) -> &mut ComputedVec<I, T> {
self.average.as_mut().unwrap()
}
pub fn unwrap_sum(&mut self) -> &mut ComputedVec<I, T> {
self.sum.as_mut().unwrap()
}
pub fn unwrap_max(&mut self) -> &mut ComputedVec<I, T> {
self.max.as_mut().unwrap()
}
pub fn unwrap_90p(&mut self) -> &mut ComputedVec<I, T> {
self._90p.as_mut().unwrap()
}
pub fn unwrap_75p(&mut self) -> &mut ComputedVec<I, T> {
self._75p.as_mut().unwrap()
}
pub fn unwrap_median(&mut self) -> &mut ComputedVec<I, T> {
self.median.as_mut().unwrap()
}
pub fn unwrap_25p(&mut self) -> &mut ComputedVec<I, T> {
self._25p.as_mut().unwrap()
}
pub fn unwrap_10p(&mut self) -> &mut ComputedVec<I, T> {
self._10p.as_mut().unwrap()
}
pub fn unwrap_min(&mut self) -> &mut ComputedVec<I, T> {
self.min.as_mut().unwrap()
}
pub fn unwrap_last(&mut self) -> &mut ComputedVec<I, T> {
self.last.as_mut().unwrap()
}
pub fn unwrap_total(&mut self) -> &mut ComputedVec<I, T> {
self.total.as_mut().unwrap()
}
pub fn any_vecs(&self) -> Vec<&dyn brk_vec::AnyStoredVec> {
let mut v: Vec<&dyn brk_vec::AnyStoredVec> = vec![];
@@ -23,10 +23,11 @@ where
pub decadeindex: ComputedVecBuilder<Decadeindex, T>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromDateindex<T>
where
T: ComputedType + Ord + From<f64>,
f64: From<T>,
T: ComputedType,
{
pub fn forced_import(
path: &Path,
@@ -35,8 +36,15 @@ where
compressed: Compressed,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
let dateindex_extra =
ComputedVecBuilder::forced_import(path, name, compressed, options.copy_self_extra())?;
let version = VERSION + version;
let dateindex_extra = ComputedVecBuilder::forced_import(
path,
name,
version,
compressed,
options.copy_self_extra(),
)?;
let options = options.remove_percentiles();
@@ -47,11 +55,17 @@ where
compressed,
)?,
dateindex_extra,
weekindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
monthindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
quarterindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
yearindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
decadeindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
weekindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
monthindex: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
quarterindex: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
yearindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
decadeindex: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
})
}
@@ -28,6 +28,8 @@ where
pub decadeindex: ComputedVecBuilder<Decadeindex, T>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromHeight<T>
where
T: ComputedType + Ord + From<f64>,
@@ -41,15 +43,23 @@ where
compressed: Compressed,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
let version = VERSION + version;
let height = compute_source.then(|| {
ComputedVec::forced_import(&path.join(format!("height_to_{name}")), version, compressed)
.unwrap()
});
let height_extra =
ComputedVecBuilder::forced_import(path, name, compressed, options.copy_self_extra())?;
let height_extra = ComputedVecBuilder::forced_import(
path,
name,
version,
compressed,
options.copy_self_extra(),
)?;
let dateindex = ComputedVecBuilder::forced_import(path, name, compressed, options)?;
let dateindex =
ComputedVecBuilder::forced_import(path, name, version, compressed, options)?;
let options = options.remove_percentiles();
@@ -57,13 +67,21 @@ where
height,
height_extra,
dateindex,
weekindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
difficultyepoch: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
monthindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
quarterindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
yearindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
// halvingepoch: StorableVecGeneator::forced_import(path, name, compressed, options)?,
decadeindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
weekindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
difficultyepoch: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
monthindex: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
quarterindex: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
yearindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
// halvingepoch: StorableVecGeneator::forced_import(path, name, version, compressed, options)?,
decadeindex: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
})
}
@@ -20,6 +20,8 @@ where
// TODO: pub halvingepoch: StorableVecGeneator<Halvingepoch, T>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromHeightStrict<T>
where
T: ComputedType + Ord + From<f64>,
@@ -32,22 +34,31 @@ where
compressed: Compressed,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
let version = VERSION + version;
let height = ComputedVec::forced_import(
&path.join(format!("height_to_{name}")),
version,
compressed,
)?;
let height_extra =
ComputedVecBuilder::forced_import(path, name, compressed, options.copy_self_extra())?;
let height_extra = ComputedVecBuilder::forced_import(
path,
name,
version,
compressed,
options.copy_self_extra(),
)?;
let options = options.remove_percentiles();
Ok(Self {
height,
height_extra,
difficultyepoch: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
// halvingepoch: StorableVecGeneator::forced_import(path, name, compressed, options)?,
difficultyepoch: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
// halvingepoch: StorableVecGeneator::forced_import(path, name, version, compressed, options)?,
})
}
@@ -29,6 +29,8 @@ where
pub decadeindex: ComputedVecBuilder<Decadeindex, T>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromTxindex<T>
where
T: ComputedType + Ord + From<f64>,
@@ -42,6 +44,8 @@ where
compressed: Compressed,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
let version = VERSION + version;
let txindex = compute_source.then(|| {
ComputedVec::forced_import(
&path.join(format!("txindex_to_{name}")),
@@ -51,21 +55,29 @@ where
.unwrap()
});
let height = ComputedVecBuilder::forced_import(path, name, compressed, options)?;
let height = ComputedVecBuilder::forced_import(path, name, version, compressed, options)?;
let options = options.remove_percentiles();
Ok(Self {
txindex,
height,
dateindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
weekindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
difficultyepoch: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
monthindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
quarterindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
yearindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
// halvingepoch: StorableVecGeneator::forced_import(path, name, compressed, options)?,
decadeindex: ComputedVecBuilder::forced_import(path, name, compressed, options)?,
dateindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
weekindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
difficultyepoch: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
monthindex: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
quarterindex: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
yearindex: ComputedVecBuilder::forced_import(path, name, version, compressed, options)?,
// halvingepoch: StorableVecGeneator::forced_import(path, name, version, compressed, options)?,
decadeindex: ComputedVecBuilder::forced_import(
path, name, version, compressed, options,
)?,
})
}
@@ -4,6 +4,8 @@ mod from_height;
mod from_height_strict;
mod from_txindex;
mod stored_type;
mod value_from_height;
mod value_from_txindex;
pub use builder::*;
pub use from_dateindex::*;
@@ -11,3 +13,5 @@ pub use from_height::*;
pub use from_height_strict::*;
pub use from_txindex::*;
pub use stored_type::*;
pub use value_from_height::*;
pub use value_from_txindex::*;
@@ -4,10 +4,10 @@ use brk_vec::StoredType;
pub trait ComputedType
where
Self: StoredType + From<usize> + Div<usize, Output = Self> + Add<Output = Self>,
Self: StoredType + From<usize> + Div<usize, Output = Self> + Add<Output = Self> + Ord,
{
}
impl<T> ComputedType for T where
T: StoredType + From<usize> + Div<usize, Output = Self> + Add<Output = Self>
T: StoredType + From<usize> + Div<usize, Output = Self> + Add<Output = Self> + Ord
{
}
@@ -0,0 +1,157 @@
use std::path::Path;
use brk_core::{Bitcoin, Dollars, Height, Sats};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyStoredVec, Compressed, Result, StoredVec, Version};
use crate::storage::{
base::ComputedVec,
marketprice,
vecs::{Indexes, indexes},
};
use super::{ComputedVecsFromHeight, StorableVecGeneatorOptions};
#[derive(Clone)]
pub struct ComputedValueVecsFromHeight {
pub sats: ComputedVecsFromHeight<Sats>,
pub bitcoin: ComputedVecsFromHeight<Bitcoin>,
pub dollars: Option<ComputedVecsFromHeight<Dollars>>,
}
const VERSION: Version = Version::ONE;
impl ComputedValueVecsFromHeight {
pub fn forced_import(
path: &Path,
name: &str,
compute_source: bool,
version: Version,
compressed: Compressed,
options: StorableVecGeneatorOptions,
compute_dollars: bool,
) -> color_eyre::Result<Self> {
Ok(Self {
sats: ComputedVecsFromHeight::forced_import(
path,
name,
compute_source,
VERSION + version,
compressed,
options,
)?,
bitcoin: ComputedVecsFromHeight::forced_import(
path,
&format!("{name}_in_btc"),
true,
VERSION + version,
compressed,
options,
)?,
dollars: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&format!("{name}_in_usd"),
true,
VERSION + version,
compressed,
options,
)
.unwrap()
}),
})
}
pub fn compute_all<F>(
&mut self,
indexer: &mut Indexer,
indexes: &mut indexes::Vecs,
marketprices: &mut Option<&mut marketprice::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
mut compute: F,
) -> color_eyre::Result<()>
where
F: FnMut(
&mut ComputedVec<Height, Sats>,
&mut Indexer,
&mut indexes::Vecs,
&Indexes,
&Exit,
) -> Result<()>,
{
compute(
self.sats.height.as_mut().unwrap(),
indexer,
indexes,
starting_indexes,
exit,
)?;
self.compute_rest(indexer, indexes, marketprices, starting_indexes, exit, None)?;
Ok(())
}
pub fn compute_rest(
&mut self,
indexer: &mut Indexer,
indexes: &mut indexes::Vecs,
marketprices: &mut Option<&mut marketprice::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
mut height: Option<&mut StoredVec<Height, Sats>>,
) -> color_eyre::Result<()> {
if let Some(height) = height.as_mut() {
self.sats
.compute_rest(indexes, starting_indexes, exit, Some(height))?;
} else {
self.sats
.compute_rest(indexes, starting_indexes, exit, None)?;
}
let height = height.unwrap_or_else(|| self.sats.height.as_mut().unwrap().mut_vec());
self.bitcoin.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_from_sats(starting_indexes.height, height, exit)
},
)?;
let txindex = self.bitcoin.height.as_mut().unwrap().mut_vec();
let price = marketprices
.as_mut()
.unwrap()
.chainindexes_to_close
.height
.mut_vec();
if let Some(dollars) = self.dollars.as_mut() {
dollars.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_from_bitcoin(starting_indexes.height, txindex, price, exit)
},
)?;
}
Ok(())
}
pub fn any_vecs(&self) -> Vec<&dyn AnyStoredVec> {
[
self.sats.any_vecs(),
self.bitcoin.any_vecs(),
self.dollars.as_ref().map_or(vec![], |v| v.any_vecs()),
]
.concat()
}
}
@@ -0,0 +1,163 @@
use std::path::Path;
use brk_core::{Bitcoin, Dollars, Sats, Txindex};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{AnyStoredVec, Compressed, Result, StoredVec, Version};
use crate::storage::{
base::ComputedVec,
marketprice,
vecs::{Indexes, indexes},
};
use super::{ComputedVecsFromTxindex, StorableVecGeneatorOptions};
#[derive(Clone)]
pub struct ComputedValueVecsFromTxindex {
pub sats: ComputedVecsFromTxindex<Sats>,
pub bitcoin: ComputedVecsFromTxindex<Bitcoin>,
pub dollars: Option<ComputedVecsFromTxindex<Dollars>>,
}
const VERSION: Version = Version::ONE;
impl ComputedValueVecsFromTxindex {
pub fn forced_import(
path: &Path,
name: &str,
compute_source: bool,
version: Version,
compressed: Compressed,
options: StorableVecGeneatorOptions,
compute_dollars: bool,
) -> color_eyre::Result<Self> {
Ok(Self {
sats: ComputedVecsFromTxindex::forced_import(
path,
name,
compute_source,
VERSION + version,
compressed,
options,
)?,
bitcoin: ComputedVecsFromTxindex::forced_import(
path,
&format!("{name}_in_btc"),
true,
VERSION + version,
compressed,
options,
)?,
dollars: compute_dollars.then(|| {
ComputedVecsFromTxindex::forced_import(
path,
&format!("{name}_in_usd"),
true,
VERSION + version,
compressed,
options,
)
.unwrap()
}),
})
}
pub fn compute_all<F>(
&mut self,
indexer: &mut Indexer,
indexes: &mut indexes::Vecs,
marketprices: &mut Option<&mut marketprice::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
mut compute: F,
) -> color_eyre::Result<()>
where
F: FnMut(
&mut ComputedVec<Txindex, Sats>,
&mut Indexer,
&mut indexes::Vecs,
&Indexes,
&Exit,
) -> Result<()>,
{
compute(
self.sats.txindex.as_mut().unwrap(),
indexer,
indexes,
starting_indexes,
exit,
)?;
self.compute_rest(indexer, indexes, marketprices, starting_indexes, exit, None)?;
Ok(())
}
pub fn compute_rest(
&mut self,
indexer: &mut Indexer,
indexes: &mut indexes::Vecs,
marketprices: &mut Option<&mut marketprice::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
mut txindex: Option<&mut StoredVec<Txindex, Sats>>,
) -> color_eyre::Result<()> {
if let Some(txindex) = txindex.as_mut() {
self.sats
.compute_rest(indexer, indexes, starting_indexes, exit, Some(txindex))?;
} else {
self.sats
.compute_rest(indexer, indexes, starting_indexes, exit, None)?;
}
let txindex = txindex.unwrap_or_else(|| self.sats.txindex.as_mut().unwrap().mut_vec());
self.bitcoin.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_from_sats(starting_indexes.txindex, txindex, exit)
},
)?;
let txindex = self.bitcoin.txindex.as_mut().unwrap().mut_vec();
let price = marketprices
.as_mut()
.unwrap()
.chainindexes_to_close
.height
.mut_vec();
if let Some(dollars) = self.dollars.as_mut() {
dollars.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, indexer, _, starting_indexes, exit| {
v.compute_from_bitcoin(
starting_indexes.txindex,
txindex,
indexer.mut_vecs().txindex_to_height.mut_vec(),
price,
exit,
)
},
)?;
}
Ok(())
}
pub fn any_vecs(&self) -> Vec<&dyn AnyStoredVec> {
[
self.sats.any_vecs(),
self.bitcoin.any_vecs(),
self.dollars.as_ref().map_or(vec![], |v| v.any_vecs()),
]
.concat()
}
}
+278 -99
View File
@@ -1,8 +1,10 @@
use std::{fs, ops::Deref, path::Path};
use brk_core::{
Date, Dateindex, Decadeindex, Difficultyepoch, Halvingepoch, Height, Monthindex, Quarterindex,
Timestamp, Txindex, Txinindex, Txoutindex, Weekindex, Yearindex,
Addressindex, Date, Dateindex, Decadeindex, Difficultyepoch, Emptyindex, Halvingepoch, Height,
Monthindex, Multisigindex, Opreturnindex, P2PK33index, P2PK65index, P2PKHindex, P2SHindex,
P2TRindex, P2WPKHindex, P2WSHindex, Pushonlyindex, Quarterindex, Timestamp, Txindex, Txinindex,
Txoutindex, Unknownindex, Weekindex, Yearindex,
};
use brk_exit::Exit;
use brk_indexer::Indexer;
@@ -12,6 +14,7 @@ use super::ComputedVec;
#[derive(Clone)]
pub struct Vecs {
pub addressindex_to_addressindex: ComputedVec<Addressindex, Addressindex>,
pub dateindex_to_date: ComputedVec<Dateindex, Date>,
pub dateindex_to_dateindex: ComputedVec<Dateindex, Dateindex>,
pub dateindex_to_first_height: ComputedVec<Dateindex, Height>,
@@ -27,6 +30,7 @@ pub struct Vecs {
pub difficultyepoch_to_first_height: ComputedVec<Difficultyepoch, Height>,
pub difficultyepoch_to_last_height: ComputedVec<Difficultyepoch, Height>,
pub difficultyepoch_to_timestamp: ComputedVec<Difficultyepoch, Timestamp>,
pub emptyindex_to_emptyindex: ComputedVec<Emptyindex, Emptyindex>,
pub halvingepoch_to_first_height: ComputedVec<Halvingepoch, Height>,
pub halvingepoch_to_halvingepoch: ComputedVec<Halvingepoch, Halvingepoch>,
pub halvingepoch_to_last_height: ComputedVec<Halvingepoch, Height>,
@@ -45,12 +49,26 @@ pub struct Vecs {
pub monthindex_to_quarterindex: ComputedVec<Monthindex, Quarterindex>,
pub monthindex_to_timestamp: ComputedVec<Monthindex, Timestamp>,
pub monthindex_to_yearindex: ComputedVec<Monthindex, Yearindex>,
pub multisigindex_to_multisigindex: ComputedVec<Multisigindex, Multisigindex>,
pub opreturnindex_to_opreturnindex: ComputedVec<Opreturnindex, Opreturnindex>,
pub p2pk33index_to_p2pk33index: ComputedVec<P2PK33index, P2PK33index>,
pub p2pk65index_to_p2pk65index: ComputedVec<P2PK65index, P2PK65index>,
pub p2pkhindex_to_p2pkhindex: ComputedVec<P2PKHindex, P2PKHindex>,
pub p2shindex_to_p2shindex: ComputedVec<P2SHindex, P2SHindex>,
pub p2trindex_to_p2trindex: ComputedVec<P2TRindex, P2TRindex>,
pub p2wpkhindex_to_p2wpkhindex: ComputedVec<P2WPKHindex, P2WPKHindex>,
pub p2wshindex_to_p2wshindex: ComputedVec<P2WSHindex, P2WSHindex>,
pub pushonlyindex_to_pushonlyindex: ComputedVec<Pushonlyindex, Pushonlyindex>,
pub quarterindex_to_first_monthindex: ComputedVec<Quarterindex, Monthindex>,
pub quarterindex_to_last_monthindex: ComputedVec<Quarterindex, Monthindex>,
pub quarterindex_to_quarterindex: ComputedVec<Quarterindex, Quarterindex>,
pub quarterindex_to_timestamp: ComputedVec<Quarterindex, Timestamp>,
pub txindex_to_last_txinindex: ComputedVec<Txindex, Txinindex>,
pub txindex_to_last_txoutindex: ComputedVec<Txindex, Txoutindex>,
pub txindex_to_txindex: ComputedVec<Txindex, Txindex>,
pub txinindex_to_txinindex: ComputedVec<Txinindex, Txinindex>,
pub txoutindex_to_txoutindex: ComputedVec<Txoutindex, Txoutindex>,
pub unknownindex_to_unknownindex: ComputedVec<Unknownindex, Unknownindex>,
pub weekindex_to_first_dateindex: ComputedVec<Weekindex, Dateindex>,
pub weekindex_to_last_dateindex: ComputedVec<Weekindex, Dateindex>,
pub weekindex_to_timestamp: ComputedVec<Weekindex, Timestamp>,
@@ -307,6 +325,86 @@ impl Vecs {
Version::ZERO,
compressed,
)?,
p2pk33index_to_p2pk33index: ComputedVec::forced_import(
&path.join("p2pk33index_to_p2pk33index"),
Version::ZERO,
compressed,
)?,
p2pk65index_to_p2pk65index: ComputedVec::forced_import(
&path.join("p2pk65index_to_p2pk65index"),
Version::ZERO,
compressed,
)?,
p2pkhindex_to_p2pkhindex: ComputedVec::forced_import(
&path.join("p2pkhindex_to_p2pkhindex"),
Version::ZERO,
compressed,
)?,
p2shindex_to_p2shindex: ComputedVec::forced_import(
&path.join("p2shindex_to_p2shindex"),
Version::ZERO,
compressed,
)?,
p2trindex_to_p2trindex: ComputedVec::forced_import(
&path.join("p2trindex_to_p2trindex"),
Version::ZERO,
compressed,
)?,
p2wpkhindex_to_p2wpkhindex: ComputedVec::forced_import(
&path.join("p2wpkhindex_to_p2wpkhindex"),
Version::ZERO,
compressed,
)?,
p2wshindex_to_p2wshindex: ComputedVec::forced_import(
&path.join("p2wshindex_to_p2wshindex"),
Version::ZERO,
compressed,
)?,
txindex_to_txindex: ComputedVec::forced_import(
&path.join("txindex_to_txindex"),
Version::ZERO,
compressed,
)?,
txinindex_to_txinindex: ComputedVec::forced_import(
&path.join("txinindex_to_txinindex"),
Version::ZERO,
compressed,
)?,
emptyindex_to_emptyindex: ComputedVec::forced_import(
&path.join("emptyindex_to_emptyindex"),
Version::ZERO,
compressed,
)?,
multisigindex_to_multisigindex: ComputedVec::forced_import(
&path.join("multisigindex_to_multisigindex"),
Version::ZERO,
compressed,
)?,
opreturnindex_to_opreturnindex: ComputedVec::forced_import(
&path.join("opreturnindex_to_opreturnindex"),
Version::ZERO,
compressed,
)?,
pushonlyindex_to_pushonlyindex: ComputedVec::forced_import(
&path.join("pushonlyindex_to_pushonlyindex"),
Version::ZERO,
compressed,
)?,
unknownindex_to_unknownindex: ComputedVec::forced_import(
&path.join("unknownindex_to_unknownindex"),
Version::ZERO,
compressed,
)?,
addressindex_to_addressindex: ComputedVec::forced_import(
&path.join("addressindex_to_addressindex"),
Version::ZERO,
compressed,
)?,
txoutindex_to_txoutindex: ComputedVec::forced_import(
&path.join("txoutindex_to_txoutindex"),
Version::ZERO,
compressed,
)?,
})
}
@@ -323,10 +421,10 @@ impl Vecs {
let txinindexes_count = indexer_vecs.txinindex_to_txoutindex.len();
let txoutindexes_count = indexer_vecs.txoutindex_to_addressindex.len();
self.height_to_height.compute_transform(
self.height_to_height.compute_range(
starting_indexes.height,
indexer_vecs.height_to_timestamp.mut_vec(),
|(h, ..)| (h, h),
|h| (h, h),
exit,
)?;
@@ -340,16 +438,12 @@ impl Vecs {
self.height_to_fixed_timestamp.compute_transform(
starting_indexes.height,
indexer_vecs.height_to_timestamp.mut_vec(),
|(h, d, s, ..)| {
let d = h
|(h, timestamp, s, ..)| {
let timestamp = h
.decremented()
.and_then(|h| s.cached_get(h).ok())
.flatten()
.map_or(d, |prev_d| {
let prev_d = *prev_d;
if prev_d > d { prev_d } else { d }
});
(h, d)
.and_then(|h| s.unwrap_cached_get(h))
.map_or(timestamp, |prev_d| prev_d.max(timestamp));
(h, timestamp)
},
exit,
)?;
@@ -365,8 +459,8 @@ impl Vecs {
let starting_dateindex = self
.height_to_dateindex
.cached_get(decremented_starting_height)?
.map_or_else(Default::default, |v| v.into_inner());
.unwrap_cached_get(decremented_starting_height)
.unwrap_or_default();
self.height_to_dateindex.compute_transform(
starting_indexes.height,
@@ -377,8 +471,7 @@ impl Vecs {
let starting_dateindex = if let Some(dateindex) = self
.height_to_dateindex
.cached_get(decremented_starting_height)?
.map(|v| v.into_inner())
.unwrap_cached_get(decremented_starting_height)
{
starting_dateindex.min(dateindex)
} else {
@@ -402,17 +495,17 @@ impl Vecs {
exit,
)?;
self.dateindex_to_dateindex.compute_transform(
self.dateindex_to_dateindex.compute_range(
starting_dateindex,
self.dateindex_to_first_height.mut_vec(),
|(di, ..)| (di, di),
|di| (di, di),
exit,
)?;
self.dateindex_to_date.compute_transform(
self.dateindex_to_date.compute_range(
starting_dateindex,
self.dateindex_to_dateindex.mut_vec(),
|(di, ..)| (di, Date::from(di)),
|di| (di, Date::from(di)),
exit,
)?;
@@ -450,13 +543,13 @@ impl Vecs {
let starting_weekindex = self
.dateindex_to_weekindex
.cached_get(starting_dateindex)?
.map_or_else(Default::default, |v| v.into_inner());
.unwrap_cached_get(starting_dateindex)
.unwrap_or_default();
self.dateindex_to_weekindex.compute_transform(
self.dateindex_to_weekindex.compute_range(
starting_dateindex,
self.dateindex_to_dateindex.mut_vec(),
|(di, ..)| (di, Weekindex::from(di)),
|di| (di, Weekindex::from(di)),
exit,
)?;
@@ -475,22 +568,17 @@ impl Vecs {
exit,
)?;
self.weekindex_to_weekindex.compute_transform(
self.weekindex_to_weekindex.compute_range(
starting_weekindex,
self.weekindex_to_first_dateindex.mut_vec(),
|(wi, ..)| (wi, wi),
|wi| (wi, wi),
exit,
)?;
self.weekindex_to_timestamp.compute_transform(
starting_weekindex,
self.weekindex_to_first_dateindex.mut_vec(),
|(i, d, ..)| {
(
i,
*self.dateindex_to_timestamp.cached_get(d).unwrap().unwrap(),
)
},
|(i, d, ..)| (i, self.dateindex_to_timestamp.double_unwrap_cached_get(d)),
exit,
)?;
@@ -498,13 +586,13 @@ impl Vecs {
let starting_monthindex = self
.dateindex_to_monthindex
.cached_get(starting_dateindex)?
.map_or_else(Default::default, |v| v.into_inner());
.unwrap_cached_get(starting_dateindex)
.unwrap_or_default();
self.dateindex_to_monthindex.compute_transform(
self.dateindex_to_monthindex.compute_range(
starting_dateindex,
self.dateindex_to_dateindex.mut_vec(),
|(di, ..)| (di, Monthindex::from(di)),
|di| (di, Monthindex::from(di)),
exit,
)?;
@@ -525,22 +613,17 @@ impl Vecs {
exit,
)?;
self.monthindex_to_monthindex.compute_transform(
self.monthindex_to_monthindex.compute_range(
starting_monthindex,
self.monthindex_to_first_dateindex.mut_vec(),
|(mi, ..)| (mi, mi),
|mi| (mi, mi),
exit,
)?;
self.monthindex_to_timestamp.compute_transform(
starting_monthindex,
self.monthindex_to_first_dateindex.mut_vec(),
|(i, d, ..)| {
(
i,
*self.dateindex_to_timestamp.cached_get(d).unwrap().unwrap(),
)
},
|(i, d, ..)| (i, self.dateindex_to_timestamp.double_unwrap_cached_get(d)),
exit,
)?;
@@ -548,13 +631,13 @@ impl Vecs {
let starting_quarterindex = self
.monthindex_to_quarterindex
.cached_get(starting_monthindex)?
.map_or_else(Default::default, |v| v.into_inner());
.unwrap_cached_get(starting_monthindex)
.unwrap_or_default();
self.monthindex_to_quarterindex.compute_transform(
self.monthindex_to_quarterindex.compute_range(
starting_monthindex,
self.monthindex_to_monthindex.mut_vec(),
|(mi, ..)| (mi, Quarterindex::from(mi)),
|mi| (mi, Quarterindex::from(mi)),
exit,
)?;
@@ -575,22 +658,17 @@ impl Vecs {
exit,
)?;
self.quarterindex_to_quarterindex.compute_transform(
self.quarterindex_to_quarterindex.compute_range(
starting_quarterindex,
self.quarterindex_to_first_monthindex.mut_vec(),
|(yi, ..)| (yi, yi),
|i| (i, i),
exit,
)?;
self.quarterindex_to_timestamp.compute_transform(
starting_quarterindex,
self.quarterindex_to_first_monthindex.mut_vec(),
|(i, m, ..)| {
(
i,
*self.monthindex_to_timestamp.cached_get(m).unwrap().unwrap(),
)
},
|(i, m, ..)| (i, self.monthindex_to_timestamp.double_unwrap_cached_get(m)),
exit,
)?;
@@ -598,13 +676,13 @@ impl Vecs {
let starting_yearindex = self
.monthindex_to_yearindex
.cached_get(starting_monthindex)?
.map_or_else(Default::default, |v| v.into_inner());
.unwrap_cached_get(starting_monthindex)
.unwrap_or_default();
self.monthindex_to_yearindex.compute_transform(
self.monthindex_to_yearindex.compute_range(
starting_monthindex,
self.monthindex_to_monthindex.mut_vec(),
|(mi, ..)| (mi, Yearindex::from(mi)),
|i| (i, Yearindex::from(i)),
exit,
)?;
@@ -625,22 +703,17 @@ impl Vecs {
exit,
)?;
self.yearindex_to_yearindex.compute_transform(
self.yearindex_to_yearindex.compute_range(
starting_yearindex,
self.yearindex_to_first_monthindex.mut_vec(),
|(yi, ..)| (yi, yi),
|i| (i, i),
exit,
)?;
self.yearindex_to_timestamp.compute_transform(
starting_yearindex,
self.yearindex_to_first_monthindex.mut_vec(),
|(i, m, ..)| {
(
i,
*self.monthindex_to_timestamp.cached_get(m).unwrap().unwrap(),
)
},
|(i, m, ..)| (i, self.monthindex_to_timestamp.double_unwrap_cached_get(m)),
exit,
)?;
@@ -648,13 +721,13 @@ impl Vecs {
let starting_decadeindex = self
.yearindex_to_decadeindex
.cached_get(starting_yearindex)?
.map_or_else(Default::default, |v| v.into_inner());
.unwrap_cached_get(starting_yearindex)
.unwrap_or_default();
self.yearindex_to_decadeindex.compute_transform(
self.yearindex_to_decadeindex.compute_range(
starting_yearindex,
self.yearindex_to_yearindex.mut_vec(),
|(yi, ..)| (yi, Decadeindex::from(yi)),
|i| (i, Decadeindex::from(i)),
exit,
)?;
@@ -673,22 +746,17 @@ impl Vecs {
exit,
)?;
self.decadeindex_to_decadeindex.compute_transform(
self.decadeindex_to_decadeindex.compute_range(
starting_decadeindex,
self.decadeindex_to_first_yearindex.mut_vec(),
|(di, ..)| (di, di),
|i| (i, i),
exit,
)?;
self.decadeindex_to_timestamp.compute_transform(
starting_decadeindex,
self.decadeindex_to_first_yearindex.mut_vec(),
|(i, y, ..)| {
(
i,
*self.yearindex_to_timestamp.cached_get(y).unwrap().unwrap(),
)
},
|(i, y, ..)| (i, self.yearindex_to_timestamp.double_unwrap_cached_get(y)),
exit,
)?;
@@ -696,13 +764,13 @@ impl Vecs {
let starting_difficultyepoch = self
.height_to_difficultyepoch
.cached_get(decremented_starting_height)?
.map_or_else(Default::default, |v| v.into_inner());
.unwrap_cached_get(decremented_starting_height)
.unwrap_or_default();
self.height_to_difficultyepoch.compute_transform(
self.height_to_difficultyepoch.compute_range(
starting_indexes.height,
self.height_to_height.mut_vec(),
|(h, ..)| (h, Difficultyepoch::from(h)),
|h| (h, Difficultyepoch::from(h)),
exit,
)?;
@@ -721,10 +789,10 @@ impl Vecs {
exit,
)?;
self.difficultyepoch_to_difficultyepoch.compute_transform(
self.difficultyepoch_to_difficultyepoch.compute_range(
starting_difficultyepoch,
self.difficultyepoch_to_first_height.mut_vec(),
|(de, ..)| (de, de),
|i| (i, i),
exit,
)?;
@@ -734,11 +802,7 @@ impl Vecs {
|(i, h, ..)| {
(
i,
*indexer_vecs
.height_to_timestamp
.cached_get(h)
.unwrap()
.unwrap(),
indexer_vecs.height_to_timestamp.double_unwrap_cached_get(h),
)
},
exit,
@@ -748,13 +812,13 @@ impl Vecs {
let starting_halvingepoch = self
.height_to_halvingepoch
.cached_get(decremented_starting_height)?
.map_or_else(Default::default, |v| v.into_inner());
.unwrap_cached_get(decremented_starting_height)
.unwrap_or_default();
self.height_to_halvingepoch.compute_transform(
self.height_to_halvingepoch.compute_range(
starting_indexes.height,
self.height_to_height.mut_vec(),
|(h, ..)| (h, Halvingepoch::from(h)),
|h| (h, Halvingepoch::from(h)),
exit,
)?;
@@ -773,10 +837,10 @@ impl Vecs {
exit,
)?;
self.halvingepoch_to_halvingepoch.compute_transform(
self.halvingepoch_to_halvingepoch.compute_range(
starting_halvingepoch,
self.halvingepoch_to_first_height.mut_vec(),
|(he, ..)| (he, he),
|i| (i, i),
exit,
)?;
@@ -786,12 +850,111 @@ impl Vecs {
// |(i, h, ..)| {
// (
// i,
// *indexer_vecs.height_to_timestamp.cached_get(h).unwrap().unwrap(),
// *indexer_vecs.height_to_timestamp.unwraped_cached_get(h).unwrap().unwrap(),
// )
// },
// exit,
// )?;
// ---
self.addressindex_to_addressindex.compute_range(
starting_indexes.addressindex,
indexer_vecs.addressindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.txoutindex_to_txoutindex.compute_range(
starting_indexes.txoutindex,
indexer_vecs.txoutindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2pk33index_to_p2pk33index.compute_range(
starting_indexes.p2pk33index,
indexer_vecs.p2pk33index_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2pk65index_to_p2pk65index.compute_range(
starting_indexes.p2pk65index,
indexer_vecs.p2pk65index_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2pkhindex_to_p2pkhindex.compute_range(
starting_indexes.p2pkhindex,
indexer_vecs.p2pkhindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2shindex_to_p2shindex.compute_range(
starting_indexes.p2shindex,
indexer_vecs.p2shindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2trindex_to_p2trindex.compute_range(
starting_indexes.p2trindex,
indexer_vecs.p2trindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2wpkhindex_to_p2wpkhindex.compute_range(
starting_indexes.p2wpkhindex,
indexer_vecs.p2wpkhindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2wshindex_to_p2wshindex.compute_range(
starting_indexes.p2wshindex,
indexer_vecs.p2wshindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.txindex_to_txindex.compute_range(
starting_indexes.txindex,
indexer_vecs.txindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.txinindex_to_txinindex.compute_range(
starting_indexes.txinindex,
indexer_vecs.txinindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.emptyindex_to_emptyindex.compute_range(
starting_indexes.emptyindex,
indexer_vecs.emptyindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.multisigindex_to_multisigindex.compute_range(
starting_indexes.multisigindex,
indexer_vecs.multisigindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.opreturnindex_to_opreturnindex.compute_range(
starting_indexes.opreturnindex,
indexer_vecs.opreturnindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.pushonlyindex_to_pushonlyindex.compute_range(
starting_indexes.pushonlyindex,
indexer_vecs.pushonlyindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.unknownindex_to_unknownindex.compute_range(
starting_indexes.unknownindex,
indexer_vecs.unknownindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
Ok(Indexes {
indexes: starting_indexes,
dateindex: starting_dateindex,
@@ -855,6 +1018,22 @@ impl Vecs {
self.quarterindex_to_last_monthindex.any_vec(),
self.quarterindex_to_quarterindex.any_vec(),
self.quarterindex_to_timestamp.any_vec(),
self.p2pk33index_to_p2pk33index.any_vec(),
self.p2pk65index_to_p2pk65index.any_vec(),
self.p2pkhindex_to_p2pkhindex.any_vec(),
self.p2shindex_to_p2shindex.any_vec(),
self.p2trindex_to_p2trindex.any_vec(),
self.p2wpkhindex_to_p2wpkhindex.any_vec(),
self.p2wshindex_to_p2wshindex.any_vec(),
self.txindex_to_txindex.any_vec(),
self.txinindex_to_txinindex.any_vec(),
self.emptyindex_to_emptyindex.any_vec(),
self.multisigindex_to_multisigindex.any_vec(),
self.opreturnindex_to_opreturnindex.any_vec(),
self.pushonlyindex_to_pushonlyindex.any_vec(),
self.unknownindex_to_unknownindex.any_vec(),
self.addressindex_to_addressindex.any_vec(),
self.txoutindex_to_txoutindex.any_vec(),
]
}
}
@@ -2,7 +2,7 @@ use std::{fs, path::Path};
use brk_core::{
Cents, Close, Dateindex, Decadeindex, Difficultyepoch, Dollars, Height, High, Low, Monthindex,
OHLCCents, OHLCDollars, Open, Quarterindex, Sats, Weekindex, Yearindex,
OHLCCents, OHLCDollars, OHLCSats, Open, Quarterindex, Sats, Weekindex, Yearindex,
};
use brk_exit::Exit;
use brk_fetcher::Fetcher;
@@ -23,33 +23,51 @@ pub struct Vecs {
pub dateindex_to_high_in_cents: ComputedVec<Dateindex, High<Cents>>,
pub dateindex_to_low_in_cents: ComputedVec<Dateindex, Low<Cents>>,
pub dateindex_to_ohlc: ComputedVec<Dateindex, OHLCDollars>,
pub dateindex_to_ohlc_in_sats: ComputedVec<Dateindex, OHLCSats>,
pub dateindex_to_ohlc_in_cents: ComputedVec<Dateindex, OHLCCents>,
pub dateindex_to_open_in_cents: ComputedVec<Dateindex, Open<Cents>>,
pub height_to_close_in_cents: ComputedVec<Height, Close<Cents>>,
pub height_to_high_in_cents: ComputedVec<Height, High<Cents>>,
pub height_to_low_in_cents: ComputedVec<Height, Low<Cents>>,
pub height_to_ohlc: ComputedVec<Height, OHLCDollars>,
pub height_to_ohlc_in_sats: ComputedVec<Height, OHLCSats>,
pub height_to_ohlc_in_cents: ComputedVec<Height, OHLCCents>,
pub height_to_open_in_cents: ComputedVec<Height, Open<Cents>>,
pub timeindexes_to_close: ComputedVecsFromDateindex<Close<Dollars>>,
pub timeindexes_to_high: ComputedVecsFromDateindex<High<Dollars>>,
pub timeindexes_to_low: ComputedVecsFromDateindex<Low<Dollars>>,
pub timeindexes_to_open: ComputedVecsFromDateindex<Open<Dollars>>,
pub timeindexes_to_sats_per_dollar: ComputedVecsFromDateindex<Close<Sats>>,
pub timeindexes_to_open_in_sats: ComputedVecsFromDateindex<Open<Sats>>,
pub timeindexes_to_high_in_sats: ComputedVecsFromDateindex<High<Sats>>,
pub timeindexes_to_low_in_sats: ComputedVecsFromDateindex<Low<Sats>>,
pub timeindexes_to_close_in_sats: ComputedVecsFromDateindex<Close<Sats>>,
pub chainindexes_to_close: ComputedVecsFromHeightStrict<Close<Dollars>>,
pub chainindexes_to_high: ComputedVecsFromHeightStrict<High<Dollars>>,
pub chainindexes_to_low: ComputedVecsFromHeightStrict<Low<Dollars>>,
pub chainindexes_to_open: ComputedVecsFromHeightStrict<Open<Dollars>>,
pub chainindexes_to_sats_per_dollar: ComputedVecsFromHeightStrict<Close<Sats>>,
pub chainindexes_to_open_in_sats: ComputedVecsFromHeightStrict<Open<Sats>>,
pub chainindexes_to_high_in_sats: ComputedVecsFromHeightStrict<High<Sats>>,
pub chainindexes_to_low_in_sats: ComputedVecsFromHeightStrict<Low<Sats>>,
pub chainindexes_to_close_in_sats: ComputedVecsFromHeightStrict<Close<Sats>>,
pub weekindex_to_ohlc: ComputedVec<Weekindex, OHLCDollars>,
pub weekindex_to_ohlc_in_sats: ComputedVec<Weekindex, OHLCSats>,
pub difficultyepoch_to_ohlc: ComputedVec<Difficultyepoch, OHLCDollars>,
pub difficultyepoch_to_ohlc_in_sats: ComputedVec<Difficultyepoch, OHLCSats>,
pub monthindex_to_ohlc: ComputedVec<Monthindex, OHLCDollars>,
pub monthindex_to_ohlc_in_sats: ComputedVec<Monthindex, OHLCSats>,
pub quarterindex_to_ohlc: ComputedVec<Quarterindex, OHLCDollars>,
pub quarterindex_to_ohlc_in_sats: ComputedVec<Quarterindex, OHLCSats>,
pub yearindex_to_ohlc: ComputedVec<Yearindex, OHLCDollars>,
pub yearindex_to_ohlc_in_sats: ComputedVec<Yearindex, OHLCSats>,
// pub halvingepoch_to_ohlc: StorableVec<Halvingepoch, OHLCDollars>,
// pub halvingepoch_to_ohlc_in_sats: StorableVec<Halvingepoch, OHLCSats>,
pub decadeindex_to_ohlc: ComputedVec<Decadeindex, OHLCDollars>,
pub decadeindex_to_ohlc_in_sats: ComputedVec<Decadeindex, OHLCSats>,
}
const VERSION: Version = Version::ZERO;
const VERSION_IN_SATS: Version = Version::ONE;
impl Vecs {
pub fn forced_import(path: &Path, compressed: Compressed) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
@@ -70,6 +88,11 @@ impl Vecs {
Version::ZERO,
compressed,
)?,
dateindex_to_ohlc_in_sats: ComputedVec::forced_import(
&path.join("dateindex_to_ohlc_in_sats"),
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
)?,
dateindex_to_close_in_cents: ComputedVec::forced_import(
&path.join("dateindex_to_close_in_cents"),
Version::ZERO,
@@ -100,6 +123,11 @@ impl Vecs {
Version::ZERO,
compressed,
)?,
height_to_ohlc_in_sats: ComputedVec::forced_import(
&path.join("height_to_ohlc_in_sats"),
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
)?,
height_to_close_in_cents: ComputedVec::forced_import(
&path.join("height_to_close_in_cents"),
Version::ZERO,
@@ -148,10 +176,31 @@ impl Vecs {
compressed,
StorableVecGeneatorOptions::default().add_last(),
)?,
timeindexes_to_sats_per_dollar: ComputedVecsFromDateindex::forced_import(
timeindexes_to_open_in_sats: ComputedVecsFromDateindex::forced_import(
path,
"sats_per_dollar",
Version::ZERO,
"open_in_sats",
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_first(),
)?,
timeindexes_to_high_in_sats: ComputedVecsFromDateindex::forced_import(
path,
"high_in_sats",
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_max(),
)?,
timeindexes_to_low_in_sats: ComputedVecsFromDateindex::forced_import(
path,
"low_in_sats",
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_min(),
)?,
timeindexes_to_close_in_sats: ComputedVecsFromDateindex::forced_import(
path,
"close_in_sats",
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_last(),
)?,
@@ -183,10 +232,31 @@ impl Vecs {
compressed,
StorableVecGeneatorOptions::default().add_last(),
)?,
chainindexes_to_sats_per_dollar: ComputedVecsFromHeightStrict::forced_import(
chainindexes_to_open_in_sats: ComputedVecsFromHeightStrict::forced_import(
path,
"sats_per_dollar",
Version::ZERO,
"open_in_sats",
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_first(),
)?,
chainindexes_to_high_in_sats: ComputedVecsFromHeightStrict::forced_import(
path,
"high_in_sats",
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_max(),
)?,
chainindexes_to_low_in_sats: ComputedVecsFromHeightStrict::forced_import(
path,
"low_in_sats",
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_min(),
)?,
chainindexes_to_close_in_sats: ComputedVecsFromHeightStrict::forced_import(
path,
"close_in_sats",
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_last(),
)?,
@@ -195,32 +265,62 @@ impl Vecs {
Version::ZERO,
compressed,
)?,
weekindex_to_ohlc_in_sats: ComputedVec::forced_import(
&path.join("weekindex_to_ohlc_in_sats"),
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
)?,
difficultyepoch_to_ohlc: ComputedVec::forced_import(
&path.join("difficultyepoch_to_ohlc"),
Version::ZERO,
compressed,
)?,
difficultyepoch_to_ohlc_in_sats: ComputedVec::forced_import(
&path.join("difficultyepoch_to_ohlc_in_sats"),
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
)?,
monthindex_to_ohlc: ComputedVec::forced_import(
&path.join("monthindex_to_ohlc"),
Version::ZERO,
compressed,
)?,
monthindex_to_ohlc_in_sats: ComputedVec::forced_import(
&path.join("monthindex_to_ohlc_in_sats"),
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
)?,
quarterindex_to_ohlc: ComputedVec::forced_import(
&path.join("quarterindex_to_ohlc"),
Version::ZERO,
compressed,
)?,
quarterindex_to_ohlc_in_sats: ComputedVec::forced_import(
&path.join("quarterindex_to_ohlc_in_sats"),
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
)?,
yearindex_to_ohlc: ComputedVec::forced_import(
&path.join("yearindex_to_ohlc"),
Version::ZERO,
compressed,
)?,
yearindex_to_ohlc_in_sats: ComputedVec::forced_import(
&path.join("yearindex_to_ohlc_in_sats"),
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
)?,
// halvingepoch_to_ohlc: StorableVec::forced_import(&path.join("halvingepoch_to_ohlc"), Version::ZERO, compressed)?,
decadeindex_to_ohlc: ComputedVec::forced_import(
&path.join("decadeindex_to_ohlc"),
Version::ZERO,
compressed,
)?,
decadeindex_to_ohlc_in_sats: ComputedVec::forced_import(
&path.join("decadeindex_to_ohlc_in_sats"),
VERSION + VERSION_IN_SATS + Version::ZERO,
compressed,
)?,
})
}
@@ -242,9 +342,8 @@ impl Vecs {
.get_height(
h,
t,
h.decremented().map(|prev_h| {
*height_to_timestamp.cached_get(prev_h).unwrap().unwrap()
}),
h.decremented()
.map(|prev_h| height_to_timestamp.double_unwrap_cached_get(prev_h)),
)
.unwrap();
(h, ohlc)
@@ -454,43 +553,26 @@ impl Vecs {
self.weekindex_to_ohlc.compute_transform(
starting_indexes.weekindex,
self.timeindexes_to_close
.weekindex
.last
.as_mut()
.unwrap()
.mut_vec(),
self.timeindexes_to_close.weekindex.unwrap_last().mut_vec(),
|(i, close, ..)| {
(
i,
OHLCDollars {
open: *self
open: self
.timeindexes_to_open
.weekindex
.first
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
high: *self
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.timeindexes_to_high
.weekindex
.max
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
low: *self
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.timeindexes_to_low
.weekindex
.min
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
@@ -502,41 +584,27 @@ impl Vecs {
starting_indexes.difficultyepoch,
self.chainindexes_to_close
.difficultyepoch
.last
.as_mut()
.unwrap()
.unwrap_last()
.mut_vec(),
|(i, close, ..)| {
(
i,
OHLCDollars {
open: *self
open: self
.chainindexes_to_open
.difficultyepoch
.first
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
high: *self
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.chainindexes_to_high
.difficultyepoch
.max
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
low: *self
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.chainindexes_to_low
.difficultyepoch
.min
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
@@ -546,43 +614,26 @@ impl Vecs {
self.monthindex_to_ohlc.compute_transform(
starting_indexes.monthindex,
self.timeindexes_to_close
.monthindex
.last
.as_mut()
.unwrap()
.mut_vec(),
self.timeindexes_to_close.monthindex.unwrap_last().mut_vec(),
|(i, close, ..)| {
(
i,
OHLCDollars {
open: *self
open: self
.timeindexes_to_open
.monthindex
.first
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
high: *self
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.timeindexes_to_high
.monthindex
.max
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
low: *self
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.timeindexes_to_low
.monthindex
.min
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
@@ -594,41 +645,27 @@ impl Vecs {
starting_indexes.quarterindex,
self.timeindexes_to_close
.quarterindex
.last
.as_mut()
.unwrap()
.unwrap_last()
.mut_vec(),
|(i, close, ..)| {
(
i,
OHLCDollars {
open: *self
open: self
.timeindexes_to_open
.quarterindex
.first
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
high: *self
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.timeindexes_to_high
.quarterindex
.max
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
low: *self
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.timeindexes_to_low
.quarterindex
.min
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
@@ -638,43 +675,26 @@ impl Vecs {
self.yearindex_to_ohlc.compute_transform(
starting_indexes.yearindex,
self.timeindexes_to_close
.yearindex
.last
.as_mut()
.unwrap()
.mut_vec(),
self.timeindexes_to_close.yearindex.unwrap_last().mut_vec(),
|(i, close, ..)| {
(
i,
OHLCDollars {
open: *self
open: self
.timeindexes_to_open
.yearindex
.first
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
high: *self
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.timeindexes_to_high
.yearindex
.max
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
low: *self
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.timeindexes_to_low
.yearindex
.min
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
@@ -689,42 +709,27 @@ impl Vecs {
starting_indexes.decadeindex,
self.timeindexes_to_close
.decadeindex
.last
.as_mut()
.as_mut()
.unwrap()
.unwrap_last()
.mut_vec(),
|(i, close, ..)| {
(
i,
OHLCDollars {
open: *self
open: self
.timeindexes_to_open
.decadeindex
.first
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
high: *self
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.timeindexes_to_high
.decadeindex
.max
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
low: *self
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.timeindexes_to_low
.decadeindex
.min
.as_mut()
.unwrap()
.cached_get(i)
.unwrap()
.unwrap(),
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
@@ -732,7 +737,52 @@ impl Vecs {
exit,
)?;
self.chainindexes_to_sats_per_dollar.compute(
self.chainindexes_to_open_in_sats.compute(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_transform(
starting_indexes.height,
self.chainindexes_to_open.height.mut_vec(),
|(i, open, ..)| (i, Open::new(Sats::ONE_BTC / *open)),
exit,
)
},
)?;
self.chainindexes_to_high_in_sats.compute(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_transform(
starting_indexes.height,
self.chainindexes_to_low.height.mut_vec(),
|(i, low, ..)| (i, High::new(Sats::ONE_BTC / *low)),
exit,
)
},
)?;
self.chainindexes_to_low_in_sats.compute(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_transform(
starting_indexes.height,
self.chainindexes_to_high.height.mut_vec(),
|(i, high, ..)| (i, Low::new(Sats::ONE_BTC / *high)),
exit,
)
},
)?;
self.chainindexes_to_close_in_sats.compute(
indexer,
indexes,
starting_indexes,
@@ -741,13 +791,58 @@ impl Vecs {
v.compute_transform(
starting_indexes.height,
self.chainindexes_to_close.height.mut_vec(),
|(i, close, ..)| (i, Close::from(Sats::ONE_BTC / *close)),
|(i, close, ..)| (i, Close::new(Sats::ONE_BTC / *close)),
exit,
)
},
)?;
self.timeindexes_to_sats_per_dollar.compute(
self.timeindexes_to_open_in_sats.compute(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_transform(
starting_indexes.dateindex,
self.timeindexes_to_open.dateindex.mut_vec(),
|(i, open, ..)| (i, Open::new(Sats::ONE_BTC / *open)),
exit,
)
},
)?;
self.timeindexes_to_high_in_sats.compute(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_transform(
starting_indexes.dateindex,
self.timeindexes_to_low.dateindex.mut_vec(),
|(i, low, ..)| (i, High::new(Sats::ONE_BTC / *low)),
exit,
)
},
)?;
self.timeindexes_to_low_in_sats.compute(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_transform(
starting_indexes.dateindex,
self.timeindexes_to_high.dateindex.mut_vec(),
|(i, high, ..)| (i, Low::new(Sats::ONE_BTC / *high)),
exit,
)
},
)?;
self.timeindexes_to_close_in_sats.compute(
indexer,
indexes,
starting_indexes,
@@ -756,12 +851,259 @@ impl Vecs {
v.compute_transform(
starting_indexes.dateindex,
self.timeindexes_to_close.dateindex.mut_vec(),
|(i, close, ..)| (i, Close::from(Sats::ONE_BTC / *close)),
|(i, close, ..)| (i, Close::new(Sats::ONE_BTC / *close)),
exit,
)
},
)?;
self.height_to_ohlc_in_sats.compute_transform(
starting_indexes.height,
self.chainindexes_to_close_in_sats.height.mut_vec(),
|(i, close, ..)| {
(
i,
OHLCSats {
open: self
.chainindexes_to_open_in_sats
.height
.double_unwrap_cached_get(i),
high: self
.chainindexes_to_high_in_sats
.height
.double_unwrap_cached_get(i),
low: self
.chainindexes_to_low_in_sats
.height
.double_unwrap_cached_get(i),
close,
},
)
},
exit,
)?;
self.dateindex_to_ohlc_in_sats.compute_transform(
starting_indexes.dateindex,
self.timeindexes_to_close_in_sats.dateindex.mut_vec(),
|(i, close, ..)| {
(
i,
OHLCSats {
open: self
.timeindexes_to_open_in_sats
.dateindex
.double_unwrap_cached_get(i),
high: self
.timeindexes_to_high_in_sats
.dateindex
.double_unwrap_cached_get(i),
low: self
.timeindexes_to_low_in_sats
.dateindex
.double_unwrap_cached_get(i),
close,
},
)
},
exit,
)?;
self.weekindex_to_ohlc_in_sats.compute_transform(
starting_indexes.weekindex,
self.timeindexes_to_close_in_sats
.weekindex
.unwrap_last()
.mut_vec(),
|(i, close, ..)| {
(
i,
OHLCSats {
open: self
.timeindexes_to_open_in_sats
.weekindex
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.timeindexes_to_high_in_sats
.weekindex
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.timeindexes_to_low_in_sats
.weekindex
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
},
exit,
)?;
self.difficultyepoch_to_ohlc_in_sats.compute_transform(
starting_indexes.difficultyepoch,
self.chainindexes_to_close_in_sats
.difficultyepoch
.unwrap_last()
.mut_vec(),
|(i, close, ..)| {
(
i,
OHLCSats {
open: self
.chainindexes_to_open_in_sats
.difficultyepoch
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.chainindexes_to_high_in_sats
.difficultyepoch
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.chainindexes_to_low_in_sats
.difficultyepoch
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
},
exit,
)?;
self.monthindex_to_ohlc_in_sats.compute_transform(
starting_indexes.monthindex,
self.timeindexes_to_close_in_sats
.monthindex
.unwrap_last()
.mut_vec(),
|(i, close, ..)| {
(
i,
OHLCSats {
open: self
.timeindexes_to_open_in_sats
.monthindex
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.timeindexes_to_high_in_sats
.monthindex
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.timeindexes_to_low_in_sats
.monthindex
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
},
exit,
)?;
self.quarterindex_to_ohlc_in_sats.compute_transform(
starting_indexes.quarterindex,
self.timeindexes_to_close_in_sats
.quarterindex
.unwrap_last()
.mut_vec(),
|(i, close, ..)| {
(
i,
OHLCSats {
open: self
.timeindexes_to_open_in_sats
.quarterindex
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.timeindexes_to_high_in_sats
.quarterindex
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.timeindexes_to_low_in_sats
.quarterindex
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
},
exit,
)?;
self.yearindex_to_ohlc_in_sats.compute_transform(
starting_indexes.yearindex,
self.timeindexes_to_close_in_sats
.yearindex
.unwrap_last()
.mut_vec(),
|(i, close, ..)| {
(
i,
OHLCSats {
open: self
.timeindexes_to_open_in_sats
.yearindex
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.timeindexes_to_high_in_sats
.yearindex
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.timeindexes_to_low_in_sats
.yearindex
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
},
exit,
)?;
// self.halvingepoch_to_ohlc
// _in_sats.compute_transform(starting_indexes.halvingepoch, other, t, exit)?;
self.decadeindex_to_ohlc_in_sats.compute_transform(
starting_indexes.decadeindex,
self.timeindexes_to_close_in_sats
.decadeindex
.unwrap_last()
.mut_vec(),
|(i, close, ..)| {
(
i,
OHLCSats {
open: self
.timeindexes_to_open_in_sats
.decadeindex
.unwrap_first()
.double_unwrap_cached_get(i),
high: self
.timeindexes_to_high_in_sats
.decadeindex
.unwrap_max()
.double_unwrap_cached_get(i),
low: self
.timeindexes_to_low_in_sats
.decadeindex
.unwrap_min()
.double_unwrap_cached_get(i),
close,
},
)
},
exit,
)?;
Ok(())
}
@@ -787,9 +1129,16 @@ impl Vecs {
self.yearindex_to_ohlc.any_vec(),
// self.halvingepoch_to_ohlc.any_vec(),
self.decadeindex_to_ohlc.any_vec(),
self.height_to_ohlc_in_sats.any_vec(),
self.dateindex_to_ohlc_in_sats.any_vec(),
self.weekindex_to_ohlc_in_sats.any_vec(),
self.difficultyepoch_to_ohlc_in_sats.any_vec(),
self.monthindex_to_ohlc_in_sats.any_vec(),
self.quarterindex_to_ohlc_in_sats.any_vec(),
self.yearindex_to_ohlc_in_sats.any_vec(),
// self.halvingepoch_to_ohlc_in_sats.any_vec(),
self.decadeindex_to_ohlc_in_sats.any_vec(),
],
self.chainindexes_to_sats_per_dollar.any_vecs(),
self.timeindexes_to_sats_per_dollar.any_vecs(),
self.timeindexes_to_close.any_vecs(),
self.timeindexes_to_high.any_vecs(),
self.timeindexes_to_low.any_vecs(),
@@ -798,6 +1147,14 @@ impl Vecs {
self.chainindexes_to_high.any_vecs(),
self.chainindexes_to_low.any_vecs(),
self.chainindexes_to_open.any_vecs(),
self.timeindexes_to_close_in_sats.any_vecs(),
self.timeindexes_to_high_in_sats.any_vecs(),
self.timeindexes_to_low_in_sats.any_vecs(),
self.timeindexes_to_open_in_sats.any_vecs(),
self.chainindexes_to_close_in_sats.any_vecs(),
self.chainindexes_to_high_in_sats.any_vecs(),
self.chainindexes_to_low_in_sats.any_vecs(),
self.chainindexes_to_open_in_sats.any_vecs(),
]
.concat()
}
+15 -10
View File
@@ -5,12 +5,12 @@ use brk_fetcher::Fetcher;
use brk_indexer::Indexer;
use brk_vec::{AnyStoredVec, Compressed};
mod base;
mod blocks;
mod grouped;
mod indexes;
mod marketprice;
mod transactions;
pub mod base;
pub mod blocks;
pub mod grouped;
pub mod indexes;
pub mod marketprice;
pub mod transactions;
use base::*;
use indexes::*;
@@ -30,7 +30,7 @@ impl Vecs {
Ok(Self {
blocks: blocks::Vecs::forced_import(path, compressed)?,
indexes: indexes::Vecs::forced_import(path, compressed)?,
transactions: transactions::Vecs::forced_import(path, compressed)?,
transactions: transactions::Vecs::forced_import(path, compressed, fetch)?,
marketprice: fetch.then(|| marketprice::Vecs::forced_import(path, compressed).unwrap()),
})
}
@@ -47,9 +47,6 @@ impl Vecs {
self.blocks
.compute(indexer, &mut self.indexes, &starting_indexes, exit)?;
self.transactions
.compute(indexer, &mut self.indexes, &starting_indexes, exit)?;
if let Some(marketprice) = self.marketprice.as_mut() {
marketprice.compute(
indexer,
@@ -60,6 +57,14 @@ impl Vecs {
)?;
}
self.transactions.compute(
indexer,
&mut self.indexes,
&starting_indexes,
&mut self.marketprice.as_mut(),
exit,
)?;
Ok(())
}
@@ -11,14 +11,17 @@ use brk_vec::{Compressed, DynamicVec, StoredIndex, Version};
use super::{
ComputedVec, Indexes,
grouped::{ComputedVecsFromHeight, ComputedVecsFromTxindex, StorableVecGeneatorOptions},
indexes,
grouped::{
ComputedValueVecsFromHeight, ComputedValueVecsFromTxindex, ComputedVecsFromHeight,
ComputedVecsFromTxindex, StorableVecGeneatorOptions,
},
indexes, marketprice,
};
#[derive(Clone)]
pub struct Vecs {
pub indexes_to_tx_count: ComputedVecsFromHeight<StoredU64>,
pub indexes_to_fee: ComputedVecsFromTxindex<Sats>,
pub indexes_to_fee: ComputedValueVecsFromTxindex,
pub indexes_to_feerate: ComputedVecsFromTxindex<Feerate>,
pub indexes_to_input_value: ComputedVecsFromTxindex<Sats>,
pub indexes_to_output_value: ComputedVecsFromTxindex<Sats>,
@@ -37,12 +40,16 @@ pub struct Vecs {
pub txindex_to_weight: ComputedVec<Txindex, Weight>,
/// Value == 0 when Coinbase
pub txinindex_to_value: ComputedVec<Txinindex, Sats>,
pub indexes_to_subsidy: ComputedVecsFromHeight<Sats>,
pub indexes_to_coinbase: ComputedVecsFromHeight<Sats>,
pub indexes_to_subsidy: ComputedValueVecsFromHeight,
pub indexes_to_coinbase: ComputedValueVecsFromHeight,
}
impl Vecs {
pub fn forced_import(path: &Path, compressed: Compressed) -> color_eyre::Result<Self> {
pub fn forced_import(
path: &Path,
compressed: Compressed,
compute_dollars: bool,
) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
Ok(Self {
@@ -142,7 +149,7 @@ impl Vecs {
.add_sum()
.add_total(),
)?,
indexes_to_fee: ComputedVecsFromTxindex::forced_import(
indexes_to_fee: ComputedValueVecsFromTxindex::forced_import(
path,
"fee",
true,
@@ -154,6 +161,7 @@ impl Vecs {
.add_percentiles()
.add_minmax()
.add_average(),
compute_dollars,
)?,
indexes_to_feerate: ComputedVecsFromTxindex::forced_import(
path,
@@ -198,7 +206,7 @@ impl Vecs {
.add_minmax()
.add_average(),
)?,
indexes_to_subsidy: ComputedVecsFromHeight::forced_import(
indexes_to_subsidy: ComputedValueVecsFromHeight::forced_import(
path,
"subsidy",
true,
@@ -210,8 +218,9 @@ impl Vecs {
.add_total()
.add_minmax()
.add_average(),
compute_dollars,
)?,
indexes_to_coinbase: ComputedVecsFromHeight::forced_import(
indexes_to_coinbase: ComputedValueVecsFromHeight::forced_import(
path,
"coinbase",
true,
@@ -223,6 +232,7 @@ impl Vecs {
.add_percentiles()
.add_minmax()
.add_average(),
compute_dollars,
)?,
})
}
@@ -232,6 +242,7 @@ impl Vecs {
indexer: &mut Indexer,
indexes: &mut indexes::Vecs,
starting_indexes: &Indexes,
marketprices: &mut Option<&mut marketprice::Vecs>,
exit: &Exit,
) -> color_eyre::Result<()> {
self.indexes_to_tx_count.compute_all(
@@ -295,10 +306,7 @@ impl Vecs {
|txindex| {
let v = indexer_vecs
.txindex_to_txversion
.cached_get(txindex)
.unwrap()
.unwrap()
.into_inner();
.double_unwrap_cached_get(txindex);
v == txversion
},
exit,
@@ -326,10 +334,7 @@ impl Vecs {
let total_size = indexer_vecs
.txindex_to_total_size
.mut_vec()
.cached_get(txindex)
.unwrap()
.unwrap()
.into_inner();
.double_unwrap_cached_get(txindex);
// This is the exact definition of a weight unit, as defined by BIP-141 (quote above).
let wu = base_size * 3 + total_size;
@@ -357,12 +362,12 @@ impl Vecs {
|(txinindex, txoutindex, slf, other)| {
let value = if txoutindex == Txoutindex::COINBASE {
Sats::ZERO
} else if let Ok(Some(value)) = indexer_vecs
} else if let Some(value) = indexer_vecs
.txoutindex_to_value
.mut_vec()
.cached_get(txoutindex)
.unwrap_cached_get(txoutindex)
{
*value
value
} else {
dbg!(txinindex, txoutindex, slf.len(), other.len());
panic!()
@@ -409,6 +414,7 @@ impl Vecs {
self.indexes_to_fee.compute_all(
indexer,
indexes,
marketprices,
starting_indexes,
exit,
|vec, _, _, starting_indexes, exit| {
@@ -429,11 +435,8 @@ impl Vecs {
if input_value.is_zero() {
(txindex, input_value)
} else {
let output_value = txindex_to_output_value
.cached_get(txindex)
.unwrap()
.unwrap()
.into_inner();
let output_value =
txindex_to_output_value.double_unwrap_cached_get(txindex);
(txindex, input_value.checked_sub(output_value).unwrap())
}
},
@@ -450,15 +453,12 @@ impl Vecs {
|vec, _, _, starting_indexes, exit| {
vec.compute_transform(
starting_indexes.txindex,
self.indexes_to_fee.txindex.as_mut().unwrap().mut_vec(),
self.indexes_to_fee.sats.txindex.as_mut().unwrap().mut_vec(),
|(txindex, fee, ..)| {
let vsize = self
.txindex_to_vsize
.mut_vec()
.cached_get(txindex)
.unwrap()
.unwrap()
.into_inner();
.double_unwrap_cached_get(txindex);
(txindex, Feerate::from((fee, vsize)))
},
@@ -486,6 +486,7 @@ impl Vecs {
self.indexes_to_subsidy.compute_all(
indexer,
indexes,
marketprices,
starting_indexes,
exit,
|vec, indexer, indexes, starting_indexes, exit| {
@@ -496,29 +497,18 @@ impl Vecs {
|(height, txindex, ..)| {
let first_txoutindex = indexer_vecs
.txindex_to_first_txoutindex
.cached_get(txindex)
.unwrap()
.unwrap()
.into_inner()
.to_usize()
.unwrap();
.double_unwrap_cached_get(txindex)
.unwrap_to_usize();
let last_txoutindex = indexes
.txindex_to_last_txoutindex
.mut_vec()
.cached_get(txindex)
.unwrap()
.unwrap()
.into_inner()
.to_usize()
.unwrap();
.double_unwrap_cached_get(txindex)
.unwrap_to_usize();
let mut sats = Sats::ZERO;
(first_txoutindex..=last_txoutindex).for_each(|txoutindex| {
sats += indexer_vecs
.txoutindex_to_value
.cached_get_(txoutindex)
.unwrap()
.unwrap()
.into_inner();
.double_unwrap_cached_get(Txoutindex::from(txoutindex));
});
(height, sats)
},
@@ -530,24 +520,26 @@ impl Vecs {
self.indexes_to_coinbase.compute_all(
indexer,
indexes,
marketprices,
starting_indexes,
exit,
|vec, _, _, starting_indexes, exit| {
vec.compute_transform(
starting_indexes.height,
self.indexes_to_subsidy.height.as_mut().unwrap().mut_vec(),
self.indexes_to_subsidy
.sats
.height
.as_mut()
.unwrap()
.mut_vec(),
|(height, subsidy, ..)| {
let fees = self
.indexes_to_fee
.sats
.height
.sum
.as_mut()
.unwrap()
.unwrap_sum()
.mut_vec()
.cached_get(height)
.unwrap()
.unwrap()
.into_inner();
.double_unwrap_cached_get(height);
(height, subsidy.checked_sub(fees).unwrap())
},
exit,
+48 -4
View File
@@ -1,20 +1,49 @@
use std::ops::Mul;
use std::ops::{Add, Div, Mul};
use serde::Serialize;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
use super::Sats;
#[derive(Debug, Default, Clone, Copy)]
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
PartialOrd,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
)]
pub struct Bitcoin(f64);
impl Add for Bitcoin {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::from(Sats::from(self) + Sats::from(rhs))
}
}
impl Mul for Bitcoin {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0 * rhs.0)
Self::from(Sats::from(self) * Sats::from(rhs))
}
}
impl Div<usize> for Bitcoin {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self::from(Sats::from(self) / rhs)
}
}
impl From<Sats> for Bitcoin {
fn from(value: Sats) -> Self {
Self(u64::from(value) as f64 / (u64::from(Sats::ONE_BTC) as f64))
Self(f64::from(value) / (f64::from(Sats::ONE_BTC)))
}
}
@@ -29,3 +58,18 @@ impl From<Bitcoin> for f64 {
value.0
}
}
impl From<usize> for Bitcoin {
fn from(value: usize) -> Self {
Self(value as f64)
}
}
impl Eq for Bitcoin {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for Bitcoin {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.partial_cmp(&other.0).unwrap()
}
}
+29 -1
View File
@@ -1,3 +1,5 @@
use std::ops::{Add, Div};
use serde::Serialize;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
@@ -22,7 +24,7 @@ pub struct Cents(u64);
impl From<Dollars> for Cents {
fn from(value: Dollars) -> Self {
Self((*value * 100.0).floor() as u64)
Self((*value * 100.0).round() as u64)
}
}
@@ -31,3 +33,29 @@ impl From<Cents> for f64 {
value.0 as f64
}
}
impl From<u64> for Cents {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<Cents> for u64 {
fn from(value: Cents) -> Self {
value.0
}
}
impl Add for Cents {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Div<usize> for Cents {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs as u64)
}
}
+13 -4
View File
@@ -1,10 +1,10 @@
use std::ops::{Add, Div};
use std::ops::{Add, Div, Mul};
use derive_deref::Deref;
use serde::Serialize;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
use super::Cents;
use super::{Bitcoin, Cents, Sats};
#[derive(
Debug,
@@ -49,14 +49,14 @@ impl From<usize> for Dollars {
impl Add for Dollars {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
Self::from(Cents::from(self) + Cents::from(rhs))
}
}
impl Div<usize> for Dollars {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs as f64)
Self::from(Cents::from(self) / rhs)
}
}
@@ -68,3 +68,12 @@ impl Ord for Dollars {
self.0.partial_cmp(&other.0).unwrap()
}
}
impl Mul<Bitcoin> for Dollars {
type Output = Dollars;
fn mul(self, rhs: Bitcoin) -> Self::Output {
Self::from(Cents::from(
u64::from(Sats::from(rhs)) * u64::from(Cents::from(self)) / u64::from(Sats::ONE_BTC),
))
}
}
+171 -98
View File
@@ -99,6 +99,51 @@ impl From<&OHLCCents> for OHLCDollars {
}
}
#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
#[repr(C)]
pub struct OHLCSats {
pub open: Open<Sats>,
pub high: High<Sats>,
pub low: Low<Sats>,
pub close: Close<Sats>,
}
impl Serialize for OHLCSats {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut tup = serializer.serialize_tuple(4)?;
tup.serialize_element(&self.open)?;
tup.serialize_element(&self.high)?;
tup.serialize_element(&self.low)?;
tup.serialize_element(&self.close)?;
tup.end()
}
}
impl From<(Open<Sats>, High<Sats>, Low<Sats>, Close<Sats>)> for OHLCSats {
fn from(value: (Open<Sats>, High<Sats>, Low<Sats>, Close<Sats>)) -> Self {
Self {
open: value.0,
high: value.1,
low: value.2,
close: value.3,
}
}
}
impl From<Close<Sats>> for OHLCSats {
fn from(value: Close<Sats>) -> Self {
Self {
open: Open::from(value),
high: High::from(value),
low: Low::from(value),
close: value,
}
}
}
#[derive(
Debug,
Default,
@@ -117,12 +162,40 @@ impl From<&OHLCCents> for OHLCDollars {
)]
#[repr(C)]
pub struct Open<T>(T);
impl<T> From<T> for Open<T> {
fn from(value: T) -> Self {
impl<T> Open<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T> From<usize> for Open<T>
where
T: From<usize>,
{
fn from(value: usize) -> Self {
Self(T::from(value))
}
}
impl<T> From<f64> for Open<T>
where
T: From<f64>,
{
fn from(value: f64) -> Self {
Self(T::from(value))
}
}
impl<T> From<Open<T>> for f64
where
f64: From<T>,
{
fn from(value: Open<T>) -> Self {
Self::from(value.0)
}
}
impl<T> From<Close<T>> for Open<T>
where
T: Copy,
@@ -138,24 +211,6 @@ impl From<Open<Cents>> for Open<Dollars> {
}
}
impl From<usize> for Open<Dollars> {
fn from(value: usize) -> Self {
Self(Dollars::from(value))
}
}
impl From<f64> for Open<Dollars> {
fn from(value: f64) -> Self {
Self(Dollars::from(value))
}
}
impl From<Open<Dollars>> for f64 {
fn from(value: Open<Dollars>) -> Self {
Self::from(value.0)
}
}
impl<T> Add for Open<T>
where
T: Add<Output = T>,
@@ -194,12 +249,40 @@ where
)]
#[repr(C)]
pub struct High<T>(T);
impl<T> From<T> for High<T> {
fn from(value: T) -> Self {
impl<T> High<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T> From<usize> for High<T>
where
T: From<usize>,
{
fn from(value: usize) -> Self {
Self(T::from(value))
}
}
impl<T> From<f64> for High<T>
where
T: From<f64>,
{
fn from(value: f64) -> Self {
Self(T::from(value))
}
}
impl<T> From<High<T>> for f64
where
f64: From<T>,
{
fn from(value: High<T>) -> Self {
Self::from(value.0)
}
}
impl<T> From<Close<T>> for High<T>
where
T: Copy,
@@ -215,24 +298,6 @@ impl From<High<Cents>> for High<Dollars> {
}
}
impl From<usize> for High<Dollars> {
fn from(value: usize) -> Self {
Self(Dollars::from(value))
}
}
impl From<f64> for High<Dollars> {
fn from(value: f64) -> Self {
Self(Dollars::from(value))
}
}
impl From<High<Dollars>> for f64 {
fn from(value: High<Dollars>) -> Self {
Self::from(value.0)
}
}
impl<T> Add for High<T>
where
T: Add<Output = T>,
@@ -271,12 +336,40 @@ where
)]
#[repr(C)]
pub struct Low<T>(T);
impl<T> From<T> for Low<T> {
fn from(value: T) -> Self {
impl<T> Low<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T> From<usize> for Low<T>
where
T: From<usize>,
{
fn from(value: usize) -> Self {
Self(T::from(value))
}
}
impl<T> From<f64> for Low<T>
where
T: From<f64>,
{
fn from(value: f64) -> Self {
Self(T::from(value))
}
}
impl<T> From<Low<T>> for f64
where
f64: From<T>,
{
fn from(value: Low<T>) -> Self {
Self::from(value.0)
}
}
impl<T> From<Close<T>> for Low<T>
where
T: Copy,
@@ -292,24 +385,6 @@ impl From<Low<Cents>> for Low<Dollars> {
}
}
impl From<usize> for Low<Dollars> {
fn from(value: usize) -> Self {
Self(Dollars::from(value))
}
}
impl From<f64> for Low<Dollars> {
fn from(value: f64) -> Self {
Self(Dollars::from(value))
}
}
impl From<Low<Dollars>> for f64 {
fn from(value: Low<Dollars>) -> Self {
Self::from(value.0)
}
}
impl<T> Add for Low<T>
where
T: Add<Output = T>,
@@ -348,54 +423,52 @@ where
)]
#[repr(C)]
pub struct Close<T>(T);
impl<T> From<T> for Close<T> {
fn from(value: T) -> Self {
impl<T> Close<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T> From<usize> for Close<T>
where
T: From<usize>,
{
fn from(value: usize) -> Self {
Self(T::from(value))
}
}
impl<T> From<f64> for Close<T>
where
T: From<f64>,
{
fn from(value: f64) -> Self {
Self(T::from(value))
}
}
impl<T> From<Close<T>> for f64
where
f64: From<T>,
{
fn from(value: Close<T>) -> Self {
Self::from(value.0)
}
}
// impl<A, B> From<Close<A>> for Close<B>
// where
// B: From<A>,
// {
// fn from(value: Close<A>) -> Self {
// Self(B::from(*value))
impl From<Close<Cents>> for Close<Dollars> {
fn from(value: Close<Cents>) -> Self {
Self(Dollars::from(*value))
}
}
impl From<usize> for Close<Dollars> {
fn from(value: usize) -> Self {
Self(Dollars::from(value))
}
}
impl From<usize> for Close<Sats> {
fn from(value: usize) -> Self {
Self(Sats::from(value))
}
}
impl From<f64> for Close<Dollars> {
fn from(value: f64) -> Self {
Self(Dollars::from(value))
}
}
impl From<f64> for Close<Sats> {
fn from(value: f64) -> Self {
Self(Sats::from(value))
}
}
impl From<Close<Dollars>> for f64 {
fn from(value: Close<Dollars>) -> Self {
Self::from(value.0)
}
}
impl From<Close<Sats>> for f64 {
fn from(value: Close<Sats>) -> Self {
Self::from(value.0)
}
}
impl<T> Add for Close<T>
where
T: Add<Output = T>,
+12 -6
View File
@@ -9,7 +9,7 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::CheckedSub;
use super::{Bitcoin, Dollars, Height};
use super::{Bitcoin, Cents, Dollars, Height};
#[derive(
Debug,
@@ -30,6 +30,7 @@ pub struct Sats(u64);
impl Sats {
pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(u64::MAX);
pub const ONE_BTC: Self = Self(100_000_000);
pub fn is_zero(&self) -> bool {
@@ -39,8 +40,8 @@ impl Sats {
impl Add for Sats {
type Output = Self;
fn add(self, rhs: Sats) -> Self::Output {
Sats::from(self.0 + rhs.0)
fn add(self, rhs: Self) -> Self::Output {
Self::from(self.0 + rhs.0)
}
}
@@ -93,7 +94,12 @@ impl Sum for Sats {
impl Div<Dollars> for Sats {
type Output = Self;
fn div(self, rhs: Dollars) -> Self::Output {
Self((self.0 as f64 / f64::from(rhs)) as u64)
let raw_cents = u64::from(Cents::from(rhs));
if raw_cents != 0 {
Self(self.0 * 100 / raw_cents)
} else {
Self::MAX
}
}
}
@@ -118,7 +124,7 @@ impl From<usize> for Sats {
impl From<f64> for Sats {
fn from(value: f64) -> Self {
Self(value as u64)
Self(value.round() as u64)
}
}
@@ -141,7 +147,7 @@ impl From<Sats> for Amount {
impl From<Bitcoin> for Sats {
fn from(value: Bitcoin) -> Self {
Self((f64::from(value) * (u64::from(Sats::ONE_BTC) as f64)) as u64)
Self((f64::from(value) * (Sats::ONE_BTC.0 as f64)).round() as u64)
}
}
+7
View File
@@ -64,3 +64,10 @@ impl Div<usize> for Weight {
Self::from(self.0 as usize / rhs)
}
}
impl Div<Weight> for Weight {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0)
}
}
+4 -4
View File
@@ -223,10 +223,10 @@ impl Binance {
Ok((
timestamp,
OHLCCents::from((
Open::from(get_cents(1)),
High::from(get_cents(2)),
Low::from(get_cents(3)),
Close::from(get_cents(4)),
Open::new(get_cents(1)),
High::new(get_cents(2)),
Low::new(get_cents(3)),
Close::new(get_cents(4)),
)),
))
}
+4 -4
View File
@@ -141,10 +141,10 @@ impl Kibo {
};
Ok(OHLCCents::from((
Open::from(get_value("open")?),
High::from(get_value("high")?),
Low::from(get_value("low")?),
Close::from(get_value("close")?),
Open::new(get_value("open")?),
High::new(get_value("high")?),
Low::new(get_value("low")?),
Close::new(get_value("close")?),
)))
}
}
+4 -4
View File
@@ -114,10 +114,10 @@ impl Kraken {
Ok((
timestamp,
OHLCCents::from((
Open::from(get_cents(1)),
High::from(get_cents(2)),
Low::from(get_cents(3)),
Close::from(get_cents(4)),
Open::new(get_cents(1)),
High::new(get_cents(2)),
Low::new(get_cents(3)),
Close::new(get_cents(4)),
)),
))
}
+2 -2
View File
@@ -60,9 +60,9 @@ impl Fetcher {
self.binance
.get_from_1mn(timestamp, previous_timestamp)
.unwrap_or_else(|_| {
self.kibo.get_from_height(height).unwrap_or_else(|_| {
self.kibo.get_from_height(height).unwrap_or_else(|e| {
let date = Date::from(timestamp);
eprintln!("{e}");
panic!(
"
Can't find the price for: height: {height} - date: {date}
+4 -4
View File
@@ -46,12 +46,12 @@ where
self.inner.get(index)
}
#[inline]
pub fn cached_get(&mut self, index: I) -> Result<Option<Value<'_, T>>> {
self.inner.cached_get(index)
pub fn unwrap_cached_get(&mut self, index: I) -> Option<T> {
self.inner.unwrap_cached_get(index)
}
#[inline]
pub fn cached_get_(&mut self, index: usize) -> Result<Option<Value<'_, T>>> {
self.inner.cached_get_(index)
pub fn double_unwrap_cached_get(&mut self, index: I) -> T {
self.inner.double_unwrap_cached_get(index)
}
pub fn iter_from<F>(&mut self, index: I, f: F) -> Result<()>
+12 -6
View File
@@ -41,23 +41,29 @@ The element returned by the iterator is a tuple which includes the:
- Block: `Block` (from `bitcoin-rust`)
- Block's Hash: `BlockHash` (also from `bitcoin-rust`)
## Example
`src/main.rs`
Tested with Bitcoin Core `v25.0..=v28.1`
## Requirements
Even though it reads *blkXXXXX.dat* files, it **needs** `bitcoind` to run with the RPC server to filter out block forks.
Even though it reads *blkXXXXX.dat* files, it **needs** `bitcoind` (with no particular parameters) to run with the RPC server to filter out forks.
Peak memory should be around 500MB.
## Comparaison
XOR-ed blocks are supported.
| | [brk_parser](https://crates.io/crates/brk_parser) | [bitcoin-explorer (deprecated)](https://crates.io/crates/bitcoin-explorer) | [blocks_iterator](https://crates.io/crates/blocks_iterator) |
## Disclaimer
A state of the local chain is saved in `{bitcoindir}/blocks/blk_index_to_blk_recap.json` to allow for faster starts (see benchmark below) but doesn't yet support locking. Thus, it is highly recommended to run one instance of `brk_parser` at a time.
## Benchmark
| | [brk_parser](https://crates.io/crates/brk_parser) | [bitcoin-explorer (deprecated)](https://crates.io/crates/bitcoin-explorer) | [blocks_iterator](https://crates.io/crates/blocks_iterator)* |
| --- | --- | --- | --- |
| Runs **with** `bitcoind` | Yes ✅ | No ❌ | Yes ✅ |
| Runs **without** `bitcoind` | No ❌ | Yes ✅ | Yes ✅ |
| `0..=855_000` | 4mn 10s | 4mn 45s | > 2h |
| `800_000..=855_000` | 0mn 52s (4mn 10s if first run) | 0mn 55s | > 2h |
\* `blocks_iterator` is with the default config (and thus with `skip_prevout = false` which does a lot more than just iterate over blocks) so it isn't an apples to apples comparaison and the numbers are misleading. You should expect much closer times. Will update the benchmark with `skip_prevout = true` as soon as possible.
*Benchmarked on a Macbook Pro M3 Pro*
+1 -1
View File
@@ -21,7 +21,7 @@ color-eyre = { workspace = true }
jiff = { workspace = true }
log = { workspace = true }
minreq = { workspace = true }
oxc = { version = "0.63.0", features = ["codegen", "minifier"] }
oxc = { version = "0.65.0", features = ["codegen", "minifier"] }
serde = { workspace = true }
tokio = { version = "1.44.2", features = ["full"] }
tower-http = { version = "0.6.2", features = ["compression-full", "trace"] }
+1 -1
View File
@@ -32,7 +32,7 @@ impl DTS for Query<'static> {
let indexes = Index::all();
let mut contents = "//
// File auto-generated, any modification will be overwritten
// File auto-generated, any modifications will be overwritten
//\n\n"
.to_string();
+9 -1
View File
@@ -17,7 +17,15 @@ pub trait DynamicVec: Send + Sync {
}
#[inline]
fn cached_get(&mut self, index: Self::I) -> Result<Option<Value<Self::T>>> {
self.get_(index.to_usize()?)
self.cached_get_(index.to_usize()?)
}
#[inline]
fn unwrap_cached_get(&mut self, index: Self::I) -> Option<Self::T> {
self.cached_get(index).unwrap().map(Value::into_inner)
}
#[inline]
fn double_unwrap_cached_get(&mut self, index: Self::I) -> Self::T {
self.unwrap_cached_get(index).unwrap()
}
#[inline]
fn get_(&self, index: usize) -> Result<Option<Value<Self::T>>> {
+7 -1
View File
@@ -18,6 +18,7 @@ where
+ Send
+ Sync,
{
fn unwrap_to_usize(self) -> usize;
fn to_usize(self) -> Result<usize>;
fn to_string<'a>() -> &'a str;
}
@@ -37,7 +38,12 @@ where
+ Send
+ Sync,
{
#[inline(always)]
#[inline]
fn unwrap_to_usize(self) -> usize {
self.to_usize().unwrap()
}
#[inline]
fn to_usize(self) -> Result<usize> {
self.try_into().map_err(|_| Error::FailedKeyTryIntoUsize)
}
+31 -12
View File
@@ -37,9 +37,8 @@
line-height: 1.5;
-webkit-text-size-adjust: 100%;
tab-size: 4;
font-family: "Geist mono", ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
font-family: "Geist mono", ui-monospace, SFMono-Regular, Menlo, Monaco,
Consolas, "Liberation Mono", "Courier New", monospace;
font-feature-settings: "ss03";
-webkit-tap-highlight-color: transparent;
}
@@ -134,6 +133,7 @@
letter-spacing: inherit;
color: inherit;
background: transparent;
text-transform: inherit;
}
button,
@@ -590,7 +590,7 @@
> fieldset {
width: 100%;
display: flex;
gap: 1rem;
gap: 1.25rem;
> label {
pointer-events: auto;
@@ -626,6 +626,7 @@
}
select {
cursor: pointer;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
@@ -884,7 +885,7 @@
bottom: 0;
left: 0;
right: 0;
z-index: 10;
z-index: 20;
pointer-events: none;
}
.shadow-left {
@@ -979,7 +980,6 @@
display: flex;
flex-direction: column;
min-height: 0;
z-index: 20;
flex: 1;
margin-top: 2rem;
margin-bottom: 1rem;
@@ -1051,12 +1051,32 @@
margin-left: var(--negative-main-padding);
fieldset {
padding-left: var(--main-padding);
padding-top: 0.5rem;
padding: 0.5rem;
z-index: 10;
position: absolute;
left: 0;
top: 0;
&[data-position="nw"] {
padding-left: var(--main-padding);
top: 0;
left: 0;
}
&[data-position="ne"] {
top: 0;
right: 0;
}
&[data-position="se"] {
bottom: 0;
right: 0;
}
&[data-position="sw"] {
padding-left: var(--main-padding);
bottom: 0;
left: 0;
}
gap: 0;
}
}
@@ -1587,9 +1607,8 @@
</main>
<aside id="aside">
<div id="charts" hidden></div>
<div id="table" hidden></div>
<div id="simulation" hidden></div>
<div id="live-price" hidden></div>
<div id="moscow-time" hidden></div>
</aside>
<div id="share-div" hidden>
<div id="share-content-div">
@@ -1,6 +1,6 @@
// @ts-check
/** @import {IChartApi, ISeriesApi, SeriesDefinition, SingleValueData as _SingleValueData, CandlestickData as _CandlestickData, BaselineData, SeriesType} from './v5.0.5-treeshaked/types' */
/** @import {IChartApi, ISeriesApi, SeriesDefinition, SingleValueData as _SingleValueData, CandlestickData as _CandlestickData, BaselineData, SeriesType, IPaneApi, LineSeriesOptions} from './v5.0.5-treeshaked/types' */
/**
* @typedef {[number, number, number, number]} OHLCTuple
@@ -32,6 +32,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
* @param {Colors} args.colors
* @param {Index} args.index
* @param {Utilities} args.utils
* @param {Elements} args.elements
* @param {DeepPartial<ChartOptions>} [args.options]
*/
function createLightweightChart({
@@ -40,14 +41,16 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
colors,
index,
utils,
elements,
options: _options = {},
}) {
console.log(elements.style.fontFamily);
/** @satisfies {DeepPartial<ChartOptions>} */
const options = {
autoSize: true,
layout: {
fontFamily: "Geist mono",
fontSize: 13,
fontFamily: elements.style.fontFamily,
// fontSize: 13,
background: { color: "transparent" },
attributionLogo: false,
colorSpace: "display-p3",
@@ -98,6 +101,16 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
index === /** @satisfies {Height} */ (0) ||
index === /** @satisfies {Difficultyepoch} */ (3) ||
index === /** @satisfies {Halvingepoch} */ (8),
minBarSpacing:
index === /** @satisfies {Monthindex} */ (4)
? 1
: index === /** @satisfies {Quarterindex} */ (5)
? 3
: index === /** @satisfies {Yearindex} */ (6)
? 12
: index === /** @satisfies {Decadeindex} */ (7)
? 120
: undefined,
},
crosshair: {
horzLine: {
@@ -124,6 +137,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
* @param {Signals} args.signals
* @param {Colors} args.colors
* @param {Utilities} args.utils
* @param {Elements} args.elements
* @param {VecsResources} args.vecsResources
* @param {Owner | null} [args.owner]
* @param {true} [args.fitContentOnResize]
@@ -134,6 +148,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
signals,
colors,
utils,
elements,
id,
vecsResources,
owner: _owner,
@@ -180,9 +195,13 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
* @param {VecResource} valuesResource
*/
function createSetFetchedDataEffect(series, valuesResource) {
const fetchedKey = vecsResources.defaultFetchedKey;
signals.runWithOwner(owner, () =>
signals.createEffect(
() => [timeResource?.fetched(), valuesResource.fetched()],
() => [
timeResource?.fetched[fetchedKey](),
valuesResource.fetched[fetchedKey](),
],
([indexes, _ohlcs]) => {
if (!ichart) throw Error("IChart should be initialized");
@@ -271,6 +290,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
signals,
colors,
utils,
elements,
});
if (fitContentOnResize) {
@@ -291,6 +311,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
* @param {Accessor<CandlestickData[]>} [args.data]
* @param {number} [args.paneIndex]
* @param {boolean} [args.defaultActive]
* @param {boolean} [args.inverse]
*/
addCandlestickSeries({
vecId,
@@ -299,13 +320,14 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
paneIndex: _paneIndex,
defaultActive,
data,
inverse,
}) {
const paneIndex = _paneIndex ?? 0;
if (!ichart || !timeResource) throw Error("Chart not fully set");
const green = colors.green();
const red = colors.red();
const green = inverse ? colors.red() : colors.green();
const red = inverse ? colors.green() : colors.red();
const series = ichart.addSeries(
/** @type {SeriesDefinition<'Candlestick'>} */ (lc.CandlestickSeries),
{
@@ -351,14 +373,11 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
utils,
});
createPriceScaleSelectorIfNeeded({
ichart,
this.addPriceScaleSelectorIfNeeded({
paneIndex,
seriesType: "Candlestick",
signals,
id,
id: `${id}-${paneIndex}`,
unit,
utils,
});
return series;
@@ -372,6 +391,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
* @param {Color} [args.color]
* @param {number} [args.paneIndex]
* @param {boolean} [args.defaultActive]
* @param {DeepPartial<LineStyleOptions & SeriesOptionsCommon>} [args.options]
*/
addLineSeries({
vecId,
@@ -381,12 +401,13 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
paneIndex: _paneIndex,
defaultActive,
data,
options,
}) {
if (!ichart || !timeResource) throw Error("Chart not fully set");
const paneIndex = _paneIndex ?? 0;
color ||= colors.orange;
color ||= unit === "USD" ? colors.dollars : colors.bitcoin;
const series = ichart.addSeries(
/** @type {SeriesDefinition<'Line'>} */ (lc.LineSeries),
@@ -395,6 +416,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
visible: defaultActive !== false,
priceLineVisible: false,
color: color(),
...options,
},
paneIndex,
);
@@ -434,14 +456,11 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
utils,
});
createPriceScaleSelectorIfNeeded({
ichart,
this.addPriceScaleSelectorIfNeeded({
paneIndex,
signals,
seriesType: "Line",
id,
id: `${id}-${paneIndex}`,
unit,
utils,
});
return series;
@@ -520,18 +539,99 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
utils,
});
createPriceScaleSelectorIfNeeded({
ichart,
this.addPriceScaleSelectorIfNeeded({
paneIndex,
signals,
seriesType: "Baseline",
id,
id: `${id}-${paneIndex}`,
unit,
utils,
});
return series;
},
/**
* @param {Object} args
* @param {Unit} args.unit
* @param {string} args.id
* @param {SeriesType} args.seriesType
* @param {number} args.paneIndex
*/
addPriceScaleSelectorIfNeeded({ unit, paneIndex, id, seriesType }) {
id = `${id}-scale`;
this.addFieldsetIfNeeded({
id,
paneIndex,
position: "sw",
createChild({ owner, pane }) {
const { field, selected } = utils.dom.createHorizontalChoiceField({
choices: /** @type {const} */ (["lin", "log"]),
id: utils.stringToId(`${id} ${unit}`),
defaultValue:
unit === "USD" && seriesType !== "Baseline" ? "log" : "lin",
key: `${id}-price-scale-${paneIndex}`,
signals,
});
signals.runWithOwner(owner, () => {
signals.createEffect(selected, (selected) => {
try {
pane.priceScale("right").applyOptions({
mode: selected === "lin" ? 0 : 1,
});
} catch {}
});
});
return field;
},
});
},
/**
* @param {Object} args
* @param {string} args.id
* @param {number} args.paneIndex
* @param {"nw" | "ne" | "se" | "sw"} args.position
* @param {number} [args.timeout]
* @param {(args: {owner: Owner | null, pane: IPaneApi<Time>}) => HTMLElement} args.createChild
*/
addFieldsetIfNeeded({ paneIndex, id, position, createChild }) {
const owner = signals.getOwner();
setTimeout(
() => {
const parent = ichart
?.panes()
.at(paneIndex)
?.getHTMLElement()
.children?.item(1)?.firstChild;
if (!parent) throw Error("Parent should exist");
if (
Array.from(parent.childNodes).filter(
(element) =>
/** @type {HTMLElement} */ (element).dataset.position ===
position,
).length
) {
return;
}
const fieldset = window.document.createElement("fieldset");
fieldset.dataset.size = "xs";
fieldset.dataset.position = position;
fieldset.id = `${id}-${paneIndex}`;
const pane = ichart?.panes().at(paneIndex);
if (!pane) throw Error("Expect pane");
pane
.getHTMLElement()
.children?.item(1)
?.firstChild?.appendChild(fieldset);
fieldset.append(createChild({ owner, pane }));
},
paneIndex ? 50 : 0,
);
},
/**
*
* @param {Object} args
@@ -894,106 +994,3 @@ function createPaneHeightObserver({ ichart, paneIndex, signals, utils }) {
callback();
}
/**
* @param {Object} args
* @param {IChartApi} args.ichart
* @param {Unit} args.unit
* @param {string} args.id
* @param {SeriesType} args.seriesType
* @param {number} args.paneIndex
* @param {Signals} args.signals
* @param {Utilities} args.utils
*/
function createPriceScaleSelectorIfNeeded({
ichart,
unit,
paneIndex,
id,
seriesType,
signals,
utils,
}) {
const owner = signals.getOwner();
setTimeout(
() => {
const parent = ichart
?.panes()
.at(paneIndex)
?.getHTMLElement()
.children?.item(1)?.firstChild;
if (!parent) throw Error("Parent should exist");
const tagName = "fieldset";
if (parent.lastChild?.nodeName.toLowerCase() === tagName) {
return;
}
console.log(id);
const choices = /**@type {const} */ (["lin", "log"]);
/** @typedef {(typeof choices)[number]} Choices */
const serializedValue = signals.createSignal(
/** @satisfies {Choices} */ (
unit === "USD" && seriesType !== "Baseline" ? "log" : "lin"
),
{
save: {
...utils.serde.string,
keyPrefix: "",
key: `${id}-price-scale-${paneIndex}`,
},
},
);
const field = utils.dom.createHorizontalChoiceField({
title: unit,
selected: serializedValue(),
choices: choices,
id: `${id}-${unit.replace(" ", "-")}`,
signals,
});
field.addEventListener("change", (event) => {
// @ts-ignore
const value = event.target.value;
serializedValue.set(value);
});
const element = window.document.createElement(tagName);
element.dataset.size = "xs";
element.id = `${id}-price-scale-${paneIndex}`;
element.append(field);
const mode = signals.createMemo(() => {
switch (serializedValue()) {
case "lin":
return 0;
case "log":
return 1;
}
});
const pane = ichart?.panes().at(paneIndex);
if (!pane) throw Error("Expect pane");
signals.runWithOwner(owner, () => {
signals.createEffect(mode, (mode) => {
try {
pane.priceScale("right").applyOptions({
mode,
});
} catch {}
});
});
pane.getHTMLElement().children?.item(1)?.firstChild?.appendChild(element);
},
paneIndex ? 10 : 0,
);
}
@@ -21,11 +21,11 @@ const importSignals = import("./v0.2.4-treeshaked/script.js").then(
(compute, effect) => {
let dispose = /** @type {VoidFunction | null} */ (null);
// @ts-ignore
_signals.createEffect(compute, (v) => {
_signals.createEffect(compute, (v, oldV) => {
dispose?.();
signals.createRoot((_dispose) => {
dispose = _dispose;
effect(v);
return effect(v, oldV);
});
signals.onCleanup(() => dispose?.());
});
+221 -110
View File
@@ -38,98 +38,223 @@ export function init({
id: "charts",
utils,
vecsResources,
elements,
});
const index_ = createIndexSelector({ elements, signals, utils });
const index = createIndexSelector({ elements, signals, utils });
let firstRun = true;
signals.createEffect(selected, (option) => {
headingElement.innerHTML = option.title;
signals.createEffect(index_, (index) => {
chart.reset({ owner: signals.getOwner() });
signals.createEffect(index, (index) => {
const { field: topUnitField, selected: topUnit } =
utils.dom.createHorizontalChoiceField({
defaultValue: "USD",
keyPrefix: "charts",
key: "unit-0",
choices: /** @type {const} */ ([
/** @satisfies {Unit} */ ("USD"),
/** @satisfies {Unit} */ ("Sats"),
]),
signals,
sorted: true,
});
const TIMERANGE_LS_KEY = `chart-timerange-${index}`;
signals.createEffect(topUnit, (topUnit) => {
const { field: seriesTypeField, selected: topSeriesType } =
utils.dom.createHorizontalChoiceField({
defaultValue: "Line",
keyPrefix: "charts",
key: "seriestype-0",
choices: /** @type {const} */ (["Candles", "Line"]),
signals,
});
const from = signals.createSignal(/** @type {number | null} */ (null), {
save: {
...utils.serde.optNumber,
keyPrefix: TIMERANGE_LS_KEY,
key: "from",
serializeParam: firstRun,
},
});
const to = signals.createSignal(/** @type {number | null} */ (null), {
save: {
...utils.serde.optNumber,
keyPrefix: TIMERANGE_LS_KEY,
key: "to",
serializeParam: firstRun,
},
});
chart.create({
index,
timeScaleSetCallback: (unknownTimeScaleCallback) => {
const from_ = from();
const to_ = to();
if (from_ !== null && to_ !== null) {
chart.inner()?.timeScale().setVisibleLogicalRange({
from: from_,
to: to_,
});
} else {
unknownTimeScaleCallback();
}
},
});
const candles = chart.addCandlestickSeries({
vecId: "ohlc",
name: "Price",
unit: "USD",
});
signals.createEffect(webSockets.kraken1dCandle.latest, (latest) => {
if (!latest) return;
const last = /** @type { CandlestickData | undefined} */ (
candles.data().at(-1)
);
if (!last) return;
candles?.update({ ...last, close: latest.close });
});
[
{ blueprints: option.top, paneIndex: 0 },
{ blueprints: option.bottom, paneIndex: 1 },
].forEach(({ blueprints, paneIndex }) => {
blueprints?.forEach((blueprint) => {
const indexes = /** @type {readonly number[]} */ (
vecIdToIndexes[blueprint.key]
signals.createEffect(topSeriesType, (topSeriesType) => {
const bottomUnits = /** @type {readonly Unit[]} */ (
Object.keys(option.bottom)
);
if (indexes.includes(index)) {
chart.addLineSeries({
vecId: blueprint.key,
color: blueprint.color,
name: blueprint.title,
unit: option.unit,
defaultActive: blueprint.defaultActive,
paneIndex,
const { field: bottomUnitField, selected: bottomUnit } =
utils.dom.createHorizontalChoiceField({
defaultValue: bottomUnits.at(0) || "",
keyPrefix: "charts",
key: "unit-1",
choices: bottomUnits,
signals,
sorted: true,
});
}
signals.createEffect(bottomUnit, (bottomUnit) => {
chart.reset({ owner: signals.getOwner() });
chart.addFieldsetIfNeeded({
id: "charts-unit-0",
paneIndex: 0,
position: "nw",
createChild() {
return topUnitField;
},
});
if (bottomUnits.length) {
chart.addFieldsetIfNeeded({
id: "charts-unit-1",
paneIndex: 1,
position: "nw",
createChild() {
return bottomUnitField;
},
});
}
chart.addFieldsetIfNeeded({
id: "charts-seriestype-0",
paneIndex: 0,
position: "ne",
createChild() {
return seriesTypeField;
},
});
const TIMERANGE_LS_KEY = `chart-timerange-${index}`;
const from = signals.createSignal(
/** @type {number | null} */ (null),
{
save: {
...utils.serde.optNumber,
keyPrefix: TIMERANGE_LS_KEY,
key: "from",
serializeParam: firstRun,
},
},
);
const to = signals.createSignal(
/** @type {number | null} */ (null),
{
save: {
...utils.serde.optNumber,
keyPrefix: TIMERANGE_LS_KEY,
key: "to",
serializeParam: firstRun,
},
},
);
chart.create({
index,
timeScaleSetCallback: (unknownTimeScaleCallback) => {
const from_ = from();
const to_ = to();
if (from_ !== null && to_ !== null) {
chart.inner()?.timeScale().setVisibleLogicalRange({
from: from_,
to: to_,
});
} else {
unknownTimeScaleCallback();
}
},
});
switch (topUnit) {
case "USD": {
switch (topSeriesType) {
case "Candles": {
const candles = chart.addCandlestickSeries({
vecId: "ohlc",
name: "Price",
unit: topUnit,
});
break;
}
case "Line": {
const line = chart.addLineSeries({
vecId: "close",
name: "Price",
unit: topUnit,
color: colors.default,
options: {
priceLineVisible: true,
},
});
}
}
// signals.createEffect(webSockets.kraken1dCandle.latest, (latest) => {
// if (!latest) return;
// const last = /** @type { CandlestickData | undefined} */ (
// candles.data().at(-1)
// );
// if (!last) return;
// candles?.update({ ...last, close: latest.close });
// });
break;
}
case "Sats": {
switch (topSeriesType) {
case "Candles": {
const candles = chart.addCandlestickSeries({
vecId: "ohlc-in-sats",
name: "Price",
unit: topUnit,
inverse: true,
});
break;
}
case "Line": {
const line = chart.addLineSeries({
vecId: "close-in-sats",
name: "Price",
unit: topUnit,
color: colors.default,
options: {
priceLineVisible: true,
},
});
}
}
break;
}
}
[
{ blueprints: option.top, paneIndex: 0 },
{ blueprints: option.bottom, paneIndex: 1 },
].forEach(({ blueprints, paneIndex }) => {
const unit = paneIndex ? bottomUnit : topUnit;
console.log({ unit });
blueprints[unit]?.forEach((blueprint) => {
const indexes = /** @type {readonly number[]} */ (
vecIdToIndexes[blueprint.key]
);
if (indexes.includes(index)) {
chart.addLineSeries({
vecId: blueprint.key,
color: blueprint.color,
name: blueprint.title,
unit,
defaultActive: blueprint.defaultActive,
paneIndex,
});
}
});
});
chart
.inner()
?.timeScale()
.subscribeVisibleLogicalRangeChange(
utils.debounce((t) => {
from.set(t.from);
to.set(t.to);
}),
);
firstRun = false;
});
});
});
chart
.inner()
?.timeScale()
.subscribeVisibleLogicalRangeChange(
utils.debounce((t) => {
from.set(t.from);
to.set(t.to);
}),
);
firstRun = false;
});
});
}
@@ -141,48 +266,34 @@ export function init({
* @param {Utilities} args.utils
*/
function createIndexSelector({ elements, signals, utils }) {
const indexChoices = /**@type {const} */ ([
"timestamp",
"date",
"week",
// "difficulty epoch",
"month",
"quarter",
"year",
// "halving epoch",
"decade",
]);
/** @typedef {(typeof indexChoices)[number]} SerializedIndex */
const serializedIndex = /** @type {Signal<SerializedIndex>} */ (
signals.createSignal("date", {
save: {
keyPrefix: "charts",
key: "index",
...utils.serde.string,
},
})
);
const indexesField = utils.dom.createHorizontalChoiceField({
const { field, selected } = utils.dom.createHorizontalChoiceField({
title: "Index",
selected: serializedIndex(),
choices: indexChoices,
defaultValue: "date",
keyPrefix: "charts",
key: "index",
choices: /**@type {const} */ ([
"timestamp",
"date",
"week",
// "difficulty epoch",
"month",
"quarter",
"year",
// "halving epoch",
"decade",
]),
id: "index",
signals,
});
indexesField.addEventListener("change", (event) => {
// @ts-ignore
const value = event.target.value;
serializedIndex.set(value);
});
const fieldset = window.document.createElement("fieldset");
fieldset.append(indexesField);
fieldset.append(field);
fieldset.dataset.size = "sm";
elements.charts.append(fieldset);
const index = signals.createMemo(
/** @returns {ChartableIndex} */ () => {
switch (serializedIndex()) {
switch (selected()) {
case "timestamp":
return /** @satisfies {Height} */ (0);
case "date":
+302 -219
View File
@@ -1,15 +1,46 @@
// @ts-check
/**
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, Unit, AnySeriesBlueprint, ChartableIndex } from "./options"
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, ChartableIndex } from "./options"
* @import {Valued, SingleValueData, CandlestickData, ChartData, OHLCTuple} from "../packages/lightweight-charts/wrapper"
* @import * as _ from "../packages/ufuzzy/v1.0.14/types"
* @import { createChart as CreateClassicChart, LineStyleOptions, DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, ISeriesApi, Time, LineData, LogicalRange, SeriesType, BaselineStyleOptions, SeriesOptionsCommon, BaselineData, CandlestickStyleOptions } from "../packages/lightweight-charts/v5.0.5-treeshaked/types"
* @import { SignalOptions } from "../packages/solid-signals/v0.2.4-treeshaked/types/core/core"
* @import { getOwner as GetOwner, onCleanup as OnCleanup, Owner } from "../packages/solid-signals/v0.2.4-treeshaked/types/core/owner"
* @import { createSignal as CreateSignal, createEffect as CreateEffect, Accessor, Setter, createMemo as CreateMemo } from "../packages/solid-signals/v0.2.4-treeshaked/types/signals";
* @import {Signal, Signals} from "../packages/solid-signals/types";
* @import {Addressindex, Dateindex, Decadeindex, Difficultyepoch, Index, Halvingepoch, Height, Monthindex, P2PK33index, P2PK65index, P2PKHindex, P2SHindex, P2TRindex, P2WPKHindex, P2WSHindex, Txindex, Txinindex, Txoutindex, VecId, Weekindex, Yearindex, VecIdToIndexes, Quarterindex} from "./vecid-to-indexes"
* @import { getOwner as GetOwner, onCleanup as OnCleanup, Owner } from "../packages/solid-signals/v0.2.4-treeshaked/types/core/owner"
* @import { createEffect as CreateEffect, Accessor, Setter, createMemo as CreateMemo } from "../packages/solid-signals/v0.2.4-treeshaked/types/signals";
* @import {Addressindex, Dateindex, Decadeindex, Difficultyepoch, Index, Halvingepoch, Height, Monthindex, P2PK33index, P2PK65index, P2PKHindex, P2SHindex, P2TRindex, P2WPKHindex, P2WSHindex, Txindex, Txinindex, Txoutindex, VecId, Weekindex, Yearindex, VecIdToIndexes, Quarterindex, Emptyindex, Multisigindex, Opreturnindex, Pushonlyindex, Unknownindex} from "./vecid-to-indexes"
*/
/**
* @typedef {"" |
* "BTC" |
* "Cents" |
* "Coinblocks" |
* "Count" |
* "Date" |
* "Difficulty" |
* "ExaHash / Second" |
* "Gigabytes" |
* "Hash" |
* "Index" |
* "mb" |
* "%" |
* "Ratio" |
* "Sats" |
* "Seconds" |
* "Timestamp" |
* "tx" |
* "Type" |
* "USD / (PetaHash / Second)" |
* "USD" |
* "Version" |
* "WU" |
* "Bool" |
* "Locktime" |
* "sat/vB" |
* "vB"
* } Unit
*/
function initPackages() {
@@ -113,22 +144,6 @@ function createUtils() {
if (!element) throw `Element with id = "${id}" should exist`;
return element;
},
/**
* @param {string} name
* @param {Elements} elements
*/
queryOrCreateMetaElement(name, elements) {
let meta = /** @type {HTMLMetaElement | null} */ (
window.document.querySelector(`meta[name="${name}"]`)
);
if (!meta) {
meta = window.document.createElement("meta");
meta.name = name;
elements.head.appendChild(meta);
}
return meta;
},
/**
* @param {HTMLElement} element
*/
@@ -206,14 +221,14 @@ function createUtils() {
},
/**
* @param {Object} arg
* @param {string} arg.text
* @param {string | HTMLElement} arg.inside
* @param {string} arg.title
* @param {VoidFunction} arg.onClick
* @param {(event: MouseEvent) => void} arg.onClick
*/
createButtonElement({ text, onClick, title }) {
createButtonElement({ inside: text, onClick, title }) {
const button = window.document.createElement("button");
button.innerHTML = text;
button.append(text);
button.title = title;
@@ -322,40 +337,71 @@ function createUtils() {
this.importStyle(href).addEventListener("load", callback);
},
/**
* @template {Readonly<string[]>} T
* @param {Object} args
* @param {string | Accessor<string>} args.title
* @param {string} args.id
* @param {Readonly<string[]>} args.choices
* @param {string} args.selected
* @param {{createEffect: CreateEffect}} args.signals
* @param {string | Accessor<string>} [args.title]
* @param {T[number]} args.defaultValue
* @param {string} [args.id]
* @param {T} args.choices
* @param {string} [args.keyPrefix]
* @param {string} args.key
* @param {boolean} [args.sorted]
* @param {{createEffect: CreateEffect, createSignal: Signals["createSignal"]}} args.signals
*/
createHorizontalChoiceField({ title, id, choices, selected, signals }) {
createHorizontalChoiceField({
title,
id,
choices: unsortedChoices,
defaultValue,
keyPrefix,
key,
signals,
sorted,
}) {
const choices = sorted
? /** @type {T} */ (/** @type {any} */ (unsortedChoices.toSorted()))
: unsortedChoices;
/** @type {Signal<T[number]>} */
const selected = signals.createSignal(defaultValue, {
save: {
...serde.string,
keyPrefix: keyPrefix ?? "",
key,
},
});
if (!choices.includes(selected())) {
selected.set(() => defaultValue);
}
const field = window.document.createElement("div");
field.classList.add("field");
const legend = window.document.createElement("legend");
if (typeof title === "string") {
legend.innerHTML = title;
} else {
signals.createEffect(title, (title) => {
if (title) {
const legend = window.document.createElement("legend");
if (typeof title === "string") {
legend.innerHTML = title;
});
}
field.append(legend);
} else {
signals.createEffect(title, (title) => {
legend.innerHTML = title;
});
}
field.append(legend);
const hr = window.document.createElement("hr");
field.append(hr);
const hr = window.document.createElement("hr");
field.append(hr);
}
const div = window.document.createElement("div");
field.append(div);
choices.forEach((choice) => {
const inputValue = choice.toLowerCase();
const inputValue = choice;
const { label } = this.createLabeledInput({
inputId: `${id}-${choice.toLowerCase()}`,
inputName: id,
inputId: `${id ?? key}-${choice.toLowerCase()}`,
inputName: id ?? key,
inputValue,
inputChecked: inputValue === selected,
inputChecked: inputValue === selected(),
labelTitle: choice,
type: "radio",
});
@@ -365,129 +411,13 @@ function createUtils() {
div.append(label);
});
return field;
},
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {string} args.placeholder
* @param {Signal<number | null>} args.signal
* @param {number} args.min
* @param {number} args.step
* @param {number} [args.max]
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputNumberElement({
id,
title,
signal,
min,
max,
step,
placeholder,
signals,
}) {
const input = window.document.createElement("input");
if (!id || !title || !placeholder) throw Error("input attribute missing");
input.id = id;
input.title = title;
input.placeholder = placeholder;
input.type = "number";
input.min = String(min);
if (max) {
input.max = String(max);
}
input.step = String(step);
let stateValue = /** @type {string | null} */ (null);
signals.createEffect(
() => {
const value = signal();
return value ? String(value) : "";
},
(value) => {
if (stateValue !== value) {
input.value = value;
stateValue = value;
}
},
);
input.addEventListener("input", () => {
const valueSer = input.value;
stateValue = valueSer;
const value = Number(valueSer);
if (value >= min && (max ? value <= max : true)) {
signal.set(value);
}
field.addEventListener("change", (event) => {
// @ts-ignore
const value = event.target.value;
selected.set(value);
});
return { input, signal };
},
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {Signal<number | null>} args.signal
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputDollar({ id, title, signal, signals }) {
return this.createInputNumberElement({
id,
placeholder: "USD",
min: 0,
title,
signal,
signals,
step: 1,
});
},
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {Signal<Date | null>} args.signal
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputDate({ id, title, signal, signals }) {
const input = window.document.createElement("input");
input.id = id;
input.title = title;
input.type = "date";
const min = "2011-01-01";
const minDate = new Date(min);
const maxDate = new Date();
const max = date.toString(maxDate);
input.min = min;
input.max = max;
let stateValue = /** @type {string | null} */ (null);
signals.createEffect(
() => {
const dateSignal = signal();
return dateSignal ? date.toString(dateSignal) : "";
},
(value) => {
if (stateValue !== value) {
input.value = value;
stateValue = value;
}
},
);
input.addEventListener("change", () => {
const value = input.value;
const date = new Date(value);
if (date >= minDate && date <= maxDate) {
stateValue = value;
signal.set(value ? date : null);
}
});
return { input, signal };
return { field, selected };
},
/**
* @param {Object} args
@@ -508,46 +438,64 @@ function createUtils() {
};
},
/**
* @param {Object} param0
* @param {string} param0.name
* @param {string} param0.value
* @template {string} Name
* @template {string} Value
* @template {Value | {name: Name; value: Value}} T
* @param {T} arg
*/
createOption({ name, value }) {
createOption(arg) {
const option = window.document.createElement("option");
option.value = value;
option.innerText = name;
if (typeof arg === "object") {
option.value = arg.value;
option.innerText = arg.name;
} else {
option.value = arg;
option.innerText = arg;
}
return option;
},
/**
* @template {{name: string; value: string}} T
* @param {Object} param0
* @param {string} param0.id
* @param {(({name: string; value: string} & T) | {name: string; list: ({name: string; value: string} & T)[]})[]} param0.list
* @param {Signal<T>} param0.signal
* @template {string} Name
* @template {string} Value
* @template {Value | {name: Name; value: Value}} T
* @param {Object} args
* @param {string} [args.id]
* @param {boolean} [args.deep]
* @param {readonly ((T) | {name: string; list: T[]})[]} args.list
* @param {Signal<T>} args.signal
*/
createSelect({ id, list, signal }) {
createSelect({ id, list, signal, deep = false }) {
const select = window.document.createElement("select");
select.name = id;
select.id = id;
if (id) {
select.name = id;
select.id = id;
}
/** @type {Record<string, VoidFunction>} */
const setters = {};
list.forEach((anyOption, index) => {
if ("list" in anyOption) {
if (typeof anyOption === "object" && "list" in anyOption) {
const { name, list } = anyOption;
const optGroup = window.document.createElement("optgroup");
optGroup.label = name;
select.append(optGroup);
list.forEach((option) => {
optGroup.append(this.createOption(option));
setters[option.value] = () => signal.set(() => option);
const key = /** @type {string} */ (
typeof option === "object" ? option.value : option
);
setters[key] = () => signal.set(() => option);
});
} else {
select.append(this.createOption(anyOption));
setters[anyOption.value] = () => signal.set(() => anyOption);
const key = /** @type {string} */ (
typeof anyOption === "object" ? anyOption.value : anyOption
);
setters[key] = () => signal.set(() => anyOption);
}
if (index !== list.length - 1) {
if (deep && index !== list.length - 1) {
select.append(window.document.createElement("hr"));
}
});
@@ -560,34 +508,13 @@ function createUtils() {
}
});
select.value = signal().value;
const initialSignal = signal();
const initialValue =
typeof initialSignal === "object" ? initialSignal.value : initialSignal;
select.value = String(initialValue);
return { select, signal };
},
/**
* @param {Object} param0
* @param {Signal<any>} param0.signal
* @param {HTMLInputElement} [param0.input]
* @param {HTMLSelectElement} [param0.select]
*/
createResetableInput({ input, select, signal }) {
const div = window.document.createElement("div");
const element = input || select;
if (!element) throw "createResetableField element missing";
div.append(element);
const button = this.createButtonElement({
onClick: signal.reset,
text: "Reset",
title: "Reset field",
});
button.type = "reset";
div.append(button);
return div;
},
/**
* @param {Object} args
* @param {string} args.title
@@ -749,6 +676,79 @@ function createUtils() {
});
}
/**
* @param {VecId} id
*/
function vecidToUnit(id) {
/** @type {Unit} */
let unit;
if (id.includes("index") || id.includes("height") || id.includes("epoch")) {
unit = "Index";
} else if (id.includes("type")) {
unit = "Type";
} else if (id === "locktime") {
unit = "Locktime";
} else if (id.startsWith("is-")) {
unit = "Bool";
} else if (
id.includes("hash") ||
id.includes("address") ||
id.includes("txid")
) {
unit = "Hash";
} else if (id.includes("interval")) {
unit = "Seconds";
} else if (id.includes("feerate")) {
unit = "sat/vB";
} else if (id.includes("in-cents")) {
unit = "Cents";
} else if (id.includes("in-usd")) {
unit = "USD";
} else if (id.includes("in-btc")) {
unit = "BTC";
} else if (
id.includes("in-sats") ||
id.startsWith("sats-") ||
id.includes("input-value") ||
id.includes("output-value") ||
id.includes("fee") ||
id.includes("coinbase") ||
id.includes("subsidy")
) {
unit = "Sats";
} else if (
id.includes("open") ||
id.includes("high") ||
id.includes("low") ||
id.includes("close") ||
id.includes("ohlc")
) {
unit = "USD";
} else if (id.includes("count") || id.match(/v[1-3]/g)) {
unit = "Count";
} else if (id.includes("date")) {
unit = "Date";
} else if (id.includes("timestamp")) {
unit = "Timestamp";
} else if (id.includes("difficulty")) {
unit = "Difficulty";
} else if (id.includes("-size")) {
unit = "mb";
} else if (id.includes("weight")) {
unit = "WU";
} else if (id.includes("vbytes") || id.includes("vsize")) {
unit = "vB";
} else if (id.includes("version")) {
unit = "Version";
} else if (id === "value") {
unit = "Sats";
} else {
console.log();
throw Error(`Unit not set for "${id}"`);
}
return unit;
}
const locale = {
numberToUSFormat,
/** @param {number} value */
@@ -857,6 +857,20 @@ function createUtils() {
return v;
},
},
vecIds: {
/**
* @param {VecId[]} v
*/
serialize(v) {
return v.join(",");
},
/**
* @param {string} v
*/
deserialize(v) {
return /** @type {VecId[]} */ (v.split(","));
},
},
number: {
/**
* @param {number} v
@@ -1261,6 +1275,7 @@ function createUtils() {
runWhenIdle,
getNumberOfDaysBetweenTwoDates,
stringToId,
vecidToUnit,
};
}
/** @typedef {ReturnType<typeof createUtils>} Utilities */
@@ -1272,6 +1287,24 @@ function createUtils() {
function createVecsResources(signals, utils) {
const owner = signals.getOwner();
const defaultFrom = -10_000;
const defaultTo = undefined;
/**
* Defaults
* - from: -10_000
* - to: undefined
*
* @param {Object} [args]
* @param {number} [args.from]
* @param {number} [args.to]
*/
function genFetchedKey(args) {
return `${args?.from}-${args?.to}`;
}
const defaultFetchedKey = genFetchedKey({ from: defaultFrom, to: defaultTo });
/**
* @template {number | OHLCTuple} [T=number]
* @param {Index} index
@@ -1281,16 +1314,29 @@ function createVecsResources(signals, utils) {
return signals.runWithOwner(owner, () => {
/** @typedef {T extends number ? SingleValueData : CandlestickData} Value */
const fetched = signals.createSignal(/** @type {T[] | null} */ (null));
let loading = false;
let at = /** @type {Date | null} */ (null);
const from = -10_000;
return {
url: utils.api.genUrl(index, id, from),
fetched,
async fetch() {
url: utils.api.genUrl(index, id, defaultFrom),
fetched: /** @type {Record<string, Signal<T[] | null>>} */ ({}),
/**
* Defaults
* - from: -10_000
* - to: undefined
*
* @param {Object} [args]
* @param {number} [args.from]
* @param {number} [args.to]
*/
async fetch(args) {
const from = args?.from ?? defaultFrom;
const to = args?.to ?? defaultTo;
const fetchedKey = genFetchedKey({ from, to });
this.fetched[fetchedKey] ??= signals.createSignal(
/** @type {T[] | null} */ (null),
);
const fetched = this.fetched[fetchedKey];
if (loading) return fetched();
if (at) {
const diff = new Date().getTime() - at.getTime();
@@ -1306,6 +1352,7 @@ function createVecsResources(signals, utils) {
index,
id,
from,
to,
)
);
at = new Date();
@@ -1337,6 +1384,8 @@ function createVecsResources(signals, utils) {
map.set(key, /** @type {any} */ (vec));
return vec;
},
genFetchedKey,
defaultFetchedKey,
};
return vecs;
@@ -1398,6 +1447,7 @@ function getElements() {
selectors: getElementById("frame-selectors"),
style: getComputedStyle(window.document.documentElement),
charts: getElementById("charts"),
table: getElementById("table"),
simulation: getElementById("simulation"),
};
}
@@ -1883,6 +1933,12 @@ function main() {
optionsPromise.then(async ({ initOptions }) => {
const vecIdToIndexes = createVecIdToIndexes();
if (env.localhost) {
Object.keys(vecIdToIndexes).forEach((id) => {
utils.vecidToUnit(/** @type {VecId} */ (id));
});
}
function initDark() {
const preferredColorSchemeMatchMedia = window.matchMedia(
"(prefers-color-scheme: dark)",
@@ -1966,7 +2022,8 @@ function main() {
undefined
);
let firstTimeLoadingChart = true;
let firstTimeLoadingSim = true;
let firstTimeLoadingTable = true;
let firstTimeLoadingSimulation = true;
signals.createEffect(options.selected, (option) => {
if (previousElement) {
@@ -1999,7 +2056,9 @@ function main() {
colors,
elements,
lightweightCharts,
selected: /** @type {any} */ (lastChartOption),
selected: /** @type {Accessor<ChartOption>} */ (
lastChartOption
),
signals,
utils,
webSockets,
@@ -2015,15 +2074,39 @@ function main() {
break;
}
case "table": {
element = elements.table;
if (firstTimeLoadingTable) {
const tableScript = import("./table.js");
utils.dom.importStyleAndThen("/styles/table.css", () =>
tableScript.then(({ init }) =>
signals.runWithOwner(owner, () =>
init({
colors,
elements,
signals,
utils,
vecsResources,
option,
vecIdToIndexes,
}),
),
),
);
}
firstTimeLoadingTable = false;
break;
}
case "simulation": {
element = elements.simulation;
lastSimulationOption.set(option);
if (firstTimeLoadingSim) {
if (firstTimeLoadingSimulation) {
const lightweightCharts = packages.lightweightCharts();
const simulationScript = import("./simulation.js");
utils.dom.importStyleAndThen(
"/styles/simulation.css",
() =>
@@ -2043,7 +2126,7 @@ function main() {
),
);
}
firstTimeLoadingSim = false;
firstTimeLoadingSimulation = false;
break;
}
File diff suppressed because it is too large Load Diff
+174 -17
View File
@@ -30,6 +30,155 @@ export function init({
const simulationElement = elements.simulation;
const dom = {
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {string} args.placeholder
* @param {Signal<number | null>} args.signal
* @param {number} args.min
* @param {number} args.step
* @param {number} [args.max]
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputNumberElement({
id,
title,
signal,
min,
max,
step,
placeholder,
signals,
}) {
const input = window.document.createElement("input");
if (!id || !title || !placeholder) throw Error("input attribute missing");
input.id = id;
input.title = title;
input.placeholder = placeholder;
input.type = "number";
input.min = String(min);
if (max) {
input.max = String(max);
}
input.step = String(step);
let stateValue = /** @type {string | null} */ (null);
signals.createEffect(
() => {
const value = signal();
return value ? String(value) : "";
},
(value) => {
if (stateValue !== value) {
input.value = value;
stateValue = value;
}
},
);
input.addEventListener("input", () => {
const valueSer = input.value;
stateValue = valueSer;
const value = Number(valueSer);
if (value >= min && (max ? value <= max : true)) {
signal.set(value);
}
});
return { input, signal };
},
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {Signal<number | null>} args.signal
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputDollar({ id, title, signal, signals }) {
return this.createInputNumberElement({
id,
placeholder: "USD",
min: 0,
title,
signal,
signals,
step: 1,
});
},
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {Signal<Date | null>} args.signal
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputDate({ id, title, signal, signals }) {
const input = window.document.createElement("input");
input.id = id;
input.title = title;
input.type = "date";
const min = "2011-01-01";
const minDate = new Date(min);
const maxDate = new Date();
const max = utils.date.toString(maxDate);
input.min = min;
input.max = max;
let stateValue = /** @type {string | null} */ (null);
signals.createEffect(
() => {
const dateSignal = signal();
return dateSignal ? utils.date.toString(dateSignal) : "";
},
(value) => {
if (stateValue !== value) {
input.value = value;
stateValue = value;
}
},
);
input.addEventListener("change", () => {
const value = input.value;
const date = new Date(value);
if (date >= minDate && date <= maxDate) {
stateValue = value;
signal.set(value ? date : null);
}
});
return { input, signal };
},
/**
* @param {Object} param0
* @param {Signal<any>} param0.signal
* @param {HTMLInputElement} [param0.input]
* @param {HTMLSelectElement} [param0.select]
*/
createResetableInput({ input, select, signal }) {
const div = window.document.createElement("div");
const element = input || select;
if (!element) throw "createResetableField element missing";
div.append(element);
const button = utils.dom.createButtonElement({
onClick: signal.reset,
inside: "Reset",
title: "Reset field",
});
button.type = "reset";
div.append(button);
return div;
},
};
const parametersElement = window.document.createElement("div");
simulationElement.append(parametersElement);
const resultsElement = window.document.createElement("div");
@@ -276,8 +425,8 @@ export function init({
}),
description:
"The amount of dollars you have ready on the exchange on day one.",
input: utils.dom.createResetableInput(
utils.dom.createInputDollar({
input: dom.createResetableInput(
dom.createInputDollar({
id: "simulation-dollars-initial",
title: "Initial Dollar Amount",
signal: settings.dollars.initial.amount,
@@ -296,11 +445,12 @@ export function init({
}),
description:
"The frequency at which you'll top up your account at the exchange.",
input: utils.dom.createResetableInput(
input: dom.createResetableInput(
utils.dom.createSelect({
id: "top-up-frequency",
list: frequencies.list,
signal: settings.dollars.topUp.frenquency,
deep: true,
}),
),
}),
@@ -315,8 +465,8 @@ export function init({
}),
description:
"The recurrent amount of dollars you'll be transfering to said exchange.",
input: utils.dom.createResetableInput(
utils.dom.createInputDollar({
input: dom.createResetableInput(
dom.createInputDollar({
id: "simulation-dollars-top-up-amount",
title: "Top Up Dollar Amount",
signal: settings.dollars.topUp.amount,
@@ -335,8 +485,8 @@ export function init({
}),
description:
"The amount, if available, of dollars that will be used to buy Bitcoin on day one.",
input: utils.dom.createResetableInput(
utils.dom.createInputDollar({
input: dom.createResetableInput(
dom.createInputDollar({
id: "simulation-bitcoin-initial-investment",
title: "Initial Swap Amount",
signal: settings.bitcoin.investment.initial,
@@ -354,11 +504,12 @@ export function init({
text: "Investment Frequency",
}),
description: "The frequency at which you'll be buying Bitcoin.",
input: utils.dom.createResetableInput(
input: dom.createResetableInput(
utils.dom.createSelect({
id: "investment-frequency",
list: frequencies.list,
signal: settings.bitcoin.investment.frequency,
deep: true,
}),
),
}),
@@ -373,8 +524,8 @@ export function init({
}),
description:
"The recurrent amount, if available, of dollars that will be used to buy Bitcoin.",
input: utils.dom.createResetableInput(
utils.dom.createInputDollar({
input: dom.createResetableInput(
dom.createInputDollar({
id: "simulation-bitcoin-recurrent-investment",
title: "Bitcoin Recurrent Investment",
signal: settings.bitcoin.investment.recurrent,
@@ -392,8 +543,8 @@ export function init({
text: "Start",
}),
description: "The first day of the simulation.",
input: utils.dom.createResetableInput(
utils.dom.createInputDate({
input: dom.createResetableInput(
dom.createInputDate({
id: "simulation-inverval-start",
title: "First Simulation Date",
signal: settings.interval.start,
@@ -411,8 +562,8 @@ export function init({
text: "End",
}),
description: "The last day of the simulation.",
input: utils.dom.createResetableInput(
utils.dom.createInputDate({
input: dom.createResetableInput(
dom.createInputDate({
id: "simulation-inverval-end",
title: "Last Simulation Day",
signal: settings.interval.end,
@@ -430,8 +581,8 @@ export function init({
text: "Exchange",
}),
description: "The amount of trading fees (in %) at the exchange.",
input: utils.dom.createResetableInput(
utils.dom.createInputNumberElement({
input: dom.createResetableInput(
dom.createInputNumberElement({
id: "simulation-fees",
title: "Exchange Fees",
signal: settings.fees.percentage,
@@ -555,6 +706,7 @@ export function init({
fitContentOnResize: true,
vecsResources,
utils,
elements,
config: [
{
unit: "USD",
@@ -597,6 +749,7 @@ export function init({
id: `bitcoin`,
fitContentOnResize: true,
vecsResources,
elements,
utils,
config: [
{
@@ -621,6 +774,7 @@ export function init({
fitContentOnResize: true,
vecsResources,
utils,
elements,
config: [
{
unit: "USD",
@@ -650,6 +804,8 @@ export function init({
id: `return-ratio`,
fitContentOnResize: true,
utils,
elements,
config: [
{
unit: "USD",
@@ -672,10 +828,11 @@ export function init({
fitContentOnResize: true,
vecsResources,
utils,
elements,
owner,
config: [
{
unit: "Percentage",
unit: "%",
blueprints: [
{
title: "Profitable Days Ratio",
+534
View File
@@ -0,0 +1,534 @@
// @ts-check
/**
* @param {Object} args
* @param {VecIdToIndexes} args.vecIdToIndexes
* @param {Option} args.option
* @param {Utilities} args.utils
* @param {Signals} args.signals
* @param {VecsResources} args.vecsResources
*/
function createTable({
utils,
vecIdToIndexes,
signals,
option,
vecsResources,
}) {
const indexToVecIds = createIndexToVecIds(vecIdToIndexes);
const serializedIndexes = createSerializedIndexes();
/** @type {SerializedIndex} */
const defaultSerializedIndex = "height";
const serializedIndex = /** @type {Signal<SerializedIndex>} */ (
signals.createSignal(
/** @type {SerializedIndex} */ (defaultSerializedIndex),
{
save: {
...utils.serde.string,
keyPrefix: "table",
key: "index",
},
},
)
);
const index = signals.createMemo(() =>
serializedIndexToIndex(serializedIndex()),
);
const table = window.document.createElement("table");
const obj = {
element: table,
/** @type {VoidFunction | undefined} */
addRandomCol: undefined,
};
signals.createEffect(index, (index, prevIndex) => {
if (prevIndex !== undefined) {
utils.url.resetParams(option);
}
const possibleVecIds = indexToVecIds[index];
const columns = signals.createSignal(/** @type {VecId[]} */ ([]), {
equals: false,
save: {
...utils.serde.vecIds,
keyPrefix: `table-${serializedIndex()}`,
key: `columns`,
},
});
columns.set((l) => l.filter((id) => possibleVecIds.includes(id)));
signals.createEffect(columns, (columns) => {
console.log(columns);
});
table.innerHTML = "";
const thead = window.document.createElement("thead");
table.append(thead);
const trHead = window.document.createElement("tr");
thead.append(trHead);
const tbody = window.document.createElement("tbody");
table.append(tbody);
const rowElements = signals.createSignal(
/** @type {HTMLTableRowElement[]} */ ([]),
);
/**
* @param {Object} args
* @param {HTMLSelectElement} args.select
* @param {Unit} [args.unit]
* @param {(event: MouseEvent) => void} [args.onLeft]
* @param {(event: MouseEvent) => void} [args.onRight]
* @param {(event: MouseEvent) => void} [args.onRemove]
*/
function addThCol({ select, onLeft, onRight, onRemove, unit: _unit }) {
const th = window.document.createElement("th");
th.scope = "col";
trHead.append(th);
const div = window.document.createElement("div");
div.append(select);
// const top = window.document.createElement("div");
// div.append(top);
// top.append(select);
// top.append(
// utils.dom.createAnchorElement({
// href: "",
// blank: true,
// }),
// );
const bottom = window.document.createElement("div");
const unit = window.document.createElement("span");
if (_unit) {
unit.innerHTML = _unit;
}
const moveLeft = utils.dom.createButtonElement({
inside: "←",
title: "Move column to the left",
onClick: onLeft || (() => {}),
});
const moveRight = utils.dom.createButtonElement({
inside: "→",
title: "Move column to the right",
onClick: onRight || (() => {}),
});
const remove = utils.dom.createButtonElement({
inside: "×",
title: "Remove column",
onClick: onRemove || (() => {}),
});
bottom.append(unit);
bottom.append(moveLeft);
bottom.append(moveRight);
bottom.append(remove);
div.append(bottom);
th.append(div);
return {
element: th,
/**
* @param {Unit} _unit
*/
setUnit(_unit) {
unit.innerHTML = _unit;
},
};
}
addThCol({
...utils.dom.createSelect({
list: serializedIndexes,
signal: serializedIndex,
}),
unit: "Index",
});
let from = 0;
let to = 0;
vecsResources
.getOrCreate(index, serializedIndex())
.fetch()
.then((vec) => {
if (!vec) return;
from = /** @type {number} */ (vec[0]);
to = /** @type {number} */ (vec.at(-1)) + 1;
const trs = /** @type {HTMLTableRowElement[]} */ ([]);
for (let i = vec.length - 1; i >= 0; i--) {
const value = vec[i];
const tr = window.document.createElement("tr");
trs.push(tr);
tbody.append(tr);
const th = window.document.createElement("th");
th.innerHTML = serializeValue({
value,
unit: "Index",
});
th.scope = "row";
tr.append(th);
}
rowElements.set(() => trs);
});
const owner = signals.getOwner();
/**
* @param {VecId} vecId
* @param {number} [_colIndex]
*/
function addCol(vecId, _colIndex = columns().length) {
signals.runWithOwner(owner, () => {
/** @type {VoidFunction | undefined} */
let dispose;
signals.createRoot((_dispose) => {
dispose = _dispose;
const vecIdOption = signals.createSignal({
name: vecId,
value: vecId,
});
const { select } = utils.dom.createSelect({
list: possibleVecIds.map((vecId) => ({
name: vecId,
value: vecId,
})),
signal: vecIdOption,
});
if (_colIndex === columns().length) {
columns.set((l) => {
l.push(vecId);
return l;
});
}
const colIndex = signals.createSignal(_colIndex);
/**
* @param {boolean} right
* @returns {(event: MouseEvent) => void}
*/
function createMoveColumnFunction(right) {
return () => {
const oldColIndex = colIndex();
const newColIndex = oldColIndex + (right ? 1 : -1);
const currentTh = /** @type {HTMLTableCellElement} */ (
trHead.childNodes[oldColIndex + 1]
);
const oterTh = /** @type {HTMLTableCellElement} */ (
trHead.childNodes[newColIndex + 1]
);
if (right) {
oterTh.after(currentTh);
} else {
oterTh.before(currentTh);
}
columns.set((l) => {
[l[oldColIndex], l[newColIndex]] = [
l[newColIndex],
l[oldColIndex],
];
return l;
});
const rows = rowElements();
for (let i = 0; i < rows.length; i++) {
const element = rows[i].childNodes[oldColIndex + 1];
const sibling = rows[i].childNodes[newColIndex + 1];
const temp = element.textContent;
element.textContent = sibling.textContent;
sibling.textContent = temp;
}
};
}
const th = addThCol({
select,
unit: utils.vecidToUnit(vecId),
onLeft: createMoveColumnFunction(false),
onRight: createMoveColumnFunction(true),
onRemove: () => {
const ci = colIndex();
trHead.childNodes[ci + 1].remove();
columns.set((l) => {
l.splice(ci, 1);
return l;
});
const rows = rowElements();
for (let i = 0; i < rows.length; i++) {
rows[i].childNodes[ci + 1].remove();
}
dispose?.();
},
});
signals.createEffect(columns, () => {
colIndex.set(Array.from(trHead.children).indexOf(th.element) - 1);
});
console.log(colIndex());
signals.createEffect(rowElements, (rowElements) => {
if (!rowElements.length) return;
for (let i = 0; i < rowElements.length; i++) {
const td = window.document.createElement("td");
rowElements[i].append(td);
}
signals.createEffect(
() => vecIdOption().name,
(vecId, prevVecId) => {
const unit = utils.vecidToUnit(vecId);
th.setUnit(unit);
const vec = vecsResources.getOrCreate(index, vecId);
vec.fetch({ from, to });
const fetchedKey = vecsResources.genFetchedKey({ from, to });
columns.set((l) => {
const i = l.indexOf(prevVecId ?? vecId);
if (i === -1) {
l.push(vecId);
} else {
l[i] = vecId;
}
return l;
});
signals.createEffect(vec.fetched[fetchedKey], (vec) => {
if (!vec) return;
const thIndex = colIndex() + 1;
for (let i = 0; i < rowElements.length; i++) {
const iRev = vec.length - 1 - i;
const value = vec[iRev];
// @ts-ignore
rowElements[i].childNodes[thIndex].innerHTML =
serializeValue({
value,
unit,
});
}
});
return () => vecId;
},
);
});
});
signals.onCleanup(() => {
dispose?.();
});
});
}
columns().forEach((vecId, colIndex) => addCol(vecId, colIndex));
obj.addRandomCol = function () {
const vecId =
possibleVecIds[Math.floor(Math.random() * possibleVecIds.length)];
addCol(vecId);
};
return () => index;
});
return obj;
}
/**
* @param {Object} args
* @param {Colors} args.colors
* @param {Signals} args.signals
* @param {Utilities} args.utils
* @param {Option} args.option
* @param {Elements} args.elements
* @param {VecsResources} args.vecsResources
* @param {VecIdToIndexes} args.vecIdToIndexes
*/
export function init({
colors,
elements,
signals,
option,
utils,
vecsResources,
vecIdToIndexes,
}) {
const parent = elements.table;
const { headerElement } = utils.dom.createHeader({
title: "Table",
});
parent.append(headerElement);
const div = window.document.createElement("div");
parent.append(div);
const table = createTable({
signals,
utils,
vecIdToIndexes,
vecsResources,
option,
});
div.append(table.element);
const span = window.document.createElement("span");
span.innerHTML = "Add column";
div.append(
utils.dom.createButtonElement({
onClick: () => {
table.addRandomCol?.();
},
inside: span,
title: "Click or tap to add a column to the table",
}),
);
}
function createSerializedIndexes() {
return /** @type {const} */ ([
/** @satisfies {VecId} */ ("height"),
/** @satisfies {VecId} */ ("dateindex"),
/** @satisfies {VecId} */ ("weekindex"),
/** @satisfies {VecId} */ ("difficultyepoch"),
/** @satisfies {VecId} */ ("monthindex"),
/** @satisfies {VecId} */ ("quarterindex"),
/** @satisfies {VecId} */ ("yearindex"),
/** @satisfies {VecId} */ ("decadeindex"),
/** @satisfies {VecId} */ ("halvingepoch"),
/** @satisfies {VecId} */ ("addressindex"),
/** @satisfies {VecId} */ ("p2pk33index"),
/** @satisfies {VecId} */ ("p2pk65index"),
/** @satisfies {VecId} */ ("p2pkhindex"),
/** @satisfies {VecId} */ ("p2shindex"),
/** @satisfies {VecId} */ ("p2trindex"),
/** @satisfies {VecId} */ ("p2wpkhindex"),
/** @satisfies {VecId} */ ("p2wshindex"),
/** @satisfies {VecId} */ ("txindex"),
/** @satisfies {VecId} */ ("txinindex"),
/** @satisfies {VecId} */ ("txoutindex"),
/** @satisfies {VecId} */ ("emptyindex"),
/** @satisfies {VecId} */ ("multisigindex"),
/** @satisfies {VecId} */ ("opreturnindex"),
/** @satisfies {VecId} */ ("pushonlyindex"),
/** @satisfies {VecId} */ ("unknownindex"),
]);
}
/** @typedef {ReturnType<typeof createSerializedIndexes>} SerializedIndexes */
/** @typedef {SerializedIndexes[number]} SerializedIndex */
/**
* @param {SerializedIndex} serializedIndex
* @returns {Index}
*/
function serializedIndexToIndex(serializedIndex) {
switch (serializedIndex) {
case "height":
return /** @satisfies {Height} */ (0);
case "dateindex":
return /** @satisfies {Dateindex} */ (1);
case "weekindex":
return /** @satisfies {Weekindex} */ (2);
case "difficultyepoch":
return /** @satisfies {Difficultyepoch} */ (3);
case "monthindex":
return /** @satisfies {Monthindex} */ (4);
case "quarterindex":
return /** @satisfies {Quarterindex} */ (5);
case "yearindex":
return /** @satisfies {Yearindex} */ (6);
case "decadeindex":
return /** @satisfies {Decadeindex} */ (7);
case "halvingepoch":
return /** @satisfies {Halvingepoch} */ (8);
case "addressindex":
return /** @satisfies {Addressindex} */ (9);
case "p2pk33index":
return /** @satisfies {P2PK33index} */ (10);
case "p2pk65index":
return /** @satisfies {P2PK65index} */ (11);
case "p2pkhindex":
return /** @satisfies {P2PKHindex} */ (12);
case "p2shindex":
return /** @satisfies {P2SHindex} */ (13);
case "p2trindex":
return /** @satisfies {P2TRindex} */ (14);
case "p2wpkhindex":
return /** @satisfies {P2WPKHindex} */ (15);
case "p2wshindex":
return /** @satisfies {P2WSHindex} */ (16);
case "txindex":
return /** @satisfies {Txindex} */ (17);
case "txinindex":
return /** @satisfies {Txinindex} */ (18);
case "txoutindex":
return /** @satisfies {Txoutindex} */ (19);
case "emptyindex":
return /** @satisfies {Emptyindex} */ (20);
case "multisigindex":
return /** @satisfies {Multisigindex} */ (21);
case "opreturnindex":
return /** @satisfies {Opreturnindex} */ (22);
case "pushonlyindex":
return /** @satisfies {Pushonlyindex} */ (23);
case "unknownindex":
return /** @satisfies {Unknownindex} */ (24);
}
}
/**
* @param {VecIdToIndexes} vecIdToIndexes
*/
function createIndexToVecIds(vecIdToIndexes) {
const indexToVecIds = Object.entries(vecIdToIndexes).reduce(
(arr, [_id, indexes]) => {
const id = /** @type {VecId} */ (_id);
indexes.forEach((i) => {
arr[i] ??= [];
arr[i].push(id);
});
return arr;
},
/** @type {VecId[][]} */ (new Array(24)),
);
indexToVecIds.forEach((arr) => {
arr.sort();
});
return indexToVecIds;
}
/**
* @param {Object} args
* @param {number | OHLCTuple} args.value
* @param {Unit} args.unit
*/
function serializeValue({ value, unit }) {
if (typeof value !== "number") {
return String(value);
} else if (value !== 18446744073709552000) {
if (unit === "USD" || unit === "Difficulty" || unit === "sat/vB") {
return value.toLocaleString("en-us", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
} else if (unit === "BTC") {
return value.toLocaleString("en-us", {
minimumFractionDigits: 8,
maximumFractionDigits: 8,
});
} else {
return value.toLocaleString("en-us");
}
} else {
return "";
}
}
@@ -1,5 +1,5 @@
//
// File auto-generated, any modification will be overwritten
// File auto-generated, any modifications will be overwritten
//
/** @typedef {0} Height */
@@ -58,7 +58,7 @@ export function createVecIdToIndexes() {
const Unknownindex = /** @satisfies {Unknownindex} */ (24);
return /** @type {const} */ ({
addressindex: [Txoutindex],
addressindex: [Addressindex, Txoutindex],
addresstype: [Addressindex],
addresstypeindex: [Addressindex],
"base-size": [Txindex],
@@ -78,12 +78,33 @@ export function createVecIdToIndexes() {
blockhash: [Height],
close: [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"close-in-cents": [Dateindex, Height],
"close-in-sats": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
coinbase: [Height],
"coinbase-10p": [Dateindex],
"coinbase-25p": [Dateindex],
"coinbase-75p": [Dateindex],
"coinbase-90p": [Dateindex],
"coinbase-average": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"coinbase-in-btc": [Height],
"coinbase-in-btc-10p": [Dateindex],
"coinbase-in-btc-25p": [Dateindex],
"coinbase-in-btc-75p": [Dateindex],
"coinbase-in-btc-90p": [Dateindex],
"coinbase-in-btc-average": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"coinbase-in-btc-max": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"coinbase-in-btc-median": [Dateindex],
"coinbase-in-btc-min": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"coinbase-in-btc-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"coinbase-in-usd": [Height],
"coinbase-in-usd-10p": [Dateindex],
"coinbase-in-usd-25p": [Dateindex],
"coinbase-in-usd-75p": [Dateindex],
"coinbase-in-usd-90p": [Dateindex],
"coinbase-in-usd-average": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"coinbase-in-usd-max": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"coinbase-in-usd-median": [Dateindex],
"coinbase-in-usd-min": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"coinbase-in-usd-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"coinbase-max": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"coinbase-median": [Dateindex],
"coinbase-min": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
@@ -93,12 +114,33 @@ export function createVecIdToIndexes() {
decadeindex: [Yearindex, Decadeindex],
difficulty: [Height],
difficultyepoch: [Height, Difficultyepoch],
emptyindex: [Emptyindex],
fee: [Txindex],
"fee-10p": [Height],
"fee-25p": [Height],
"fee-75p": [Height],
"fee-90p": [Height],
"fee-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"fee-in-btc": [Txindex],
"fee-in-btc-10p": [Height],
"fee-in-btc-25p": [Height],
"fee-in-btc-75p": [Height],
"fee-in-btc-90p": [Height],
"fee-in-btc-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"fee-in-btc-max": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"fee-in-btc-median": [Height],
"fee-in-btc-min": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"fee-in-btc-sum": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"fee-in-usd": [Txindex],
"fee-in-usd-10p": [Height],
"fee-in-usd-25p": [Height],
"fee-in-usd-75p": [Height],
"fee-in-usd-90p": [Height],
"fee-in-usd-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"fee-in-usd-max": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"fee-in-usd-median": [Height],
"fee-in-usd-min": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"fee-in-usd-sum": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"fee-max": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"fee-median": [Height],
"fee-min": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
@@ -138,6 +180,7 @@ export function createVecIdToIndexes() {
height: [Addressindex, Height, P2PK33index, P2PK65index, P2PKHindex, P2SHindex, P2TRindex, P2WPKHindex, P2WSHindex, Txindex, Txinindex, Txoutindex, Emptyindex, Multisigindex, Opreturnindex, Pushonlyindex, Unknownindex],
high: [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"high-in-cents": [Dateindex, Height],
"high-in-sats": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"input-count": [Txindex],
"input-count-10p": [Height],
"input-count-25p": [Height],
@@ -164,11 +207,16 @@ export function createVecIdToIndexes() {
locktime: [Txindex],
low: [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"low-in-cents": [Dateindex, Height],
"low-in-sats": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
monthindex: [Dateindex, Monthindex],
multisigindex: [Multisigindex],
ohlc: [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"ohlc-in-cents": [Dateindex, Height],
"ohlc-in-sats": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
open: [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"open-in-cents": [Dateindex, Height],
"open-in-sats": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
opreturnindex: [Opreturnindex],
"output-count": [Txindex],
"output-count-10p": [Height],
"output-count-25p": [Height],
@@ -183,21 +231,48 @@ export function createVecIdToIndexes() {
"output-value-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"output-value-sum": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
p2pk33addressbytes: [P2PK33index],
p2pk33index: [P2PK33index],
p2pk65addressbytes: [P2PK65index],
p2pk65index: [P2PK65index],
p2pkhaddressbytes: [P2PKHindex],
p2pkhindex: [P2PKHindex],
p2shaddressbytes: [P2SHindex],
p2shindex: [P2SHindex],
p2traddressbytes: [P2TRindex],
p2trindex: [P2TRindex],
p2wpkhaddressbytes: [P2WPKHindex],
p2wpkhindex: [P2WPKHindex],
p2wshaddressbytes: [P2WSHindex],
p2wshindex: [P2WSHindex],
pushonlyindex: [Pushonlyindex],
quarterindex: [Monthindex, Quarterindex],
"real-date": [Height],
"sats-per-dollar": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
subsidy: [Height],
"subsidy-10p": [Dateindex],
"subsidy-25p": [Dateindex],
"subsidy-75p": [Dateindex],
"subsidy-90p": [Dateindex],
"subsidy-average": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"subsidy-in-btc": [Height],
"subsidy-in-btc-10p": [Dateindex],
"subsidy-in-btc-25p": [Dateindex],
"subsidy-in-btc-75p": [Dateindex],
"subsidy-in-btc-90p": [Dateindex],
"subsidy-in-btc-average": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"subsidy-in-btc-max": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"subsidy-in-btc-median": [Dateindex],
"subsidy-in-btc-min": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"subsidy-in-btc-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"subsidy-in-usd": [Height],
"subsidy-in-usd-10p": [Dateindex],
"subsidy-in-usd-25p": [Dateindex],
"subsidy-in-usd-75p": [Dateindex],
"subsidy-in-usd-90p": [Dateindex],
"subsidy-in-usd-average": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"subsidy-in-usd-max": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"subsidy-in-usd-median": [Dateindex],
"subsidy-in-usd-min": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"subsidy-in-usd-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"subsidy-max": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"subsidy-median": [Dateindex],
"subsidy-min": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
@@ -208,13 +283,19 @@ export function createVecIdToIndexes() {
"total-block-vbytes": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-block-weight": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-coinbase": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-coinbase-in-btc": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-coinbase-in-usd": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-fee": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-fee-in-btc": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-fee-in-usd": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-input-count": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-input-value": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-output-count": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-output-value": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-size": [Height, Txindex],
"total-subsidy": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-subsidy-in-btc": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-subsidy-in-usd": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-tx-count": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-tx-v1": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-tx-v2": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
@@ -252,8 +333,11 @@ export function createVecIdToIndexes() {
"tx-weight-median": [Height],
"tx-weight-min": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
txid: [Txindex],
txoutindex: [Txinindex],
txindex: [Txindex],
txinindex: [Txinindex],
txoutindex: [Txinindex, Txoutindex],
txversion: [Txindex],
unknownindex: [Unknownindex],
value: [Txinindex, Txoutindex],
vbytes: [Height],
vsize: [Txindex],
+4
View File
@@ -27,4 +27,8 @@
.chart {
flex: 1;
}
> * {
z-index: 30;
}
}
+142
View File
@@ -0,0 +1,142 @@
#table {
width: 100%;
display: flex;
flex-direction: column;
gap: 2rem;
padding: var(--main-padding);
> div {
display: flex;
font-size: var(--font-size-sm);
margin-left: var(--negative-main-padding);
margin-right: var(--negative-main-padding);
table {
z-index: 10;
border-top-width: 1px;
border-style: dashed !important;
/* width: 100%; */
line-height: var(--line-height-sm);
text-transform: uppercase;
table-layout: auto;
border-collapse: separate;
border-spacing: 0;
/* border: 3px solid purple; */
/* min-height: 100%; */
}
th {
font-weight: 600;
}
th,
td {
/* border-top: 1px; */
border-right: 1px;
border-bottom: 1px;
border-color: var(--off-color);
border-style: dashed !important;
padding: 0.25rem 1rem;
}
td {
text-transform: lowercase;
}
a {
margin: -0.2rem 0;
font-size: 1.2rem;
}
th:first-child {
padding-left: var(--main-padding);
}
th[scope="col"] {
position: sticky;
top: 0;
background-color: var(--background-color);
> div {
display: flex;
flex-direction: column;
padding-top: 0.275rem;
> div {
display: flex;
gap: 0.25rem;
text-transform: lowercase;
color: var(--off-color);
text-align: left;
&:first-child {
gap: 0.5rem;
}
&:last-child {
gap: 1rem;
}
> span {
width: 100%;
}
> button {
padding: 0 0.25rem;
margin: 0 -0.25rem;
font-size: 1rem;
line-height: 0;
}
}
}
&:first-child {
button {
display: none;
}
}
&:nth-child(2) {
button:nth-of-type(1) {
display: none;
}
}
&:last-child {
button:nth-of-type(2) {
display: none;
}
}
}
/* select {
width: 100%;
} */
tbody {
text-align: right;
}
> button {
padding: 1rem;
min-width: 10rem;
display: flex;
flex-direction: column;
flex: 1;
position: relative;
border-top-width: 1px;
width: 100%;
/* border-right-width: 1px; */
border-bottom-width: 1px;
border-style: dashed !important;
> span {
text-align: left;
position: sticky;
top: 2rem;
left: 0;
right: 0;
}
}
}
}