Compare commits

...

31 Commits

Author SHA1 Message Date
nym21 ee30d1d36d release: v0.0.29 2025-04-22 19:34:55 +02:00
nym21 0d9415db9d kibo: fix add ts-ignore 2025-04-22 19:33:57 +02:00
nym21 8020e1126f kibo: database: part 2 2025-04-22 19:31:30 +02:00
nym21 3439422057 kibo: database: part 1 2025-04-21 23:17:37 +02:00
nym21 68d2bf736f release: v0.0.28 2025-04-19 11:46:05 +02:00
nym21 d78c39fd8c computer + kibo: part 14 - fixes 2025-04-19 11:45:26 +02:00
nym21 b1dcad86b4 release: v0.0.27 2025-04-18 19:41:13 +02:00
nym21 9b6124074d dist: another fix 2025-04-18 19:40:09 +02:00
nym21 02cbaa1e80 release: v0.0.26 2025-04-18 19:00:19 +02:00
nym21 a12f1321c7 dist: fix format tag version 2025-04-18 18:59:53 +02:00
nym21 8b67f592ac release: v0.0.25 2025-04-18 18:48:10 +02:00
nym21 319d17b337 dist: fix ubuntu version 2025-04-18 18:45:27 +02:00
nym21 476eaa85da github: add manual trigger 2025-04-18 18:37:51 +02:00
nym21 d26099855c dist: update ubuntu version 2025-04-18 18:36:18 +02:00
nym21 e47456da17 release: v0.0.24 2025-04-18 18:22:23 +02:00
nym21 a464d5d0b6 crates: upgrade 2025-04-18 18:20:40 +02:00
nym21 1cfb7b5615 computer + kibo: part 13 2025-04-18 18:08:11 +02:00
nym21 ac7c2f3d03 kibo: cleanup 2025-04-17 17:06:44 +02:00
nym21 638d9e6e01 computer: part 12 2025-04-17 15:22:34 +02:00
nym21 8b9df2a396 parser: readme 2025-04-15 14:40:56 +02:00
nym21 d7fe911bde parser: readme 2025-04-15 11:56:50 +02:00
nym21 0acc3d511b parser: readme 2025-04-15 11:27:48 +02:00
nym21 4cf465f419 computer: unwrap 2025-04-15 11:18:31 +02:00
nym21 b686d317a9 release: v0.0.23 2025-04-14 22:22:19 +02:00
nym21 dcef541852 release: v0.0.22 2025-04-14 21:57:15 +02:00
nym21 abdd733f11 deps: upgrade 2025-04-14 21:56:47 +02:00
nym21 942431e882 computer + kibo: part 11 2025-04-14 16:27:22 +02:00
nym21 1c75ea046c computer + kibo: part 10 2025-04-11 19:21:35 +02:00
nym21 f32b6daa51 release: v0.0.21 2025-04-11 12:01:30 +02:00
nym21 3736d6ba5e computer + kibo: part 9 2025-04-11 12:00:26 +02:00
nym21 9788b01f35 readmes: update discord link yet again 2025-04-10 22:57:09 +02:00
68 changed files with 5380 additions and 2259 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
+107 -105
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.20"
version = "0.0.29"
dependencies = [
"brk_cli",
"brk_computer",
@@ -391,7 +391,7 @@ dependencies = [
[[package]]
name = "brk_cli"
version = "0.0.20"
version = "0.0.29"
dependencies = [
"brk_computer",
"brk_core",
@@ -412,7 +412,7 @@ dependencies = [
[[package]]
name = "brk_computer"
version = "0.0.20"
version = "0.0.29"
dependencies = [
"brk_core",
"brk_exit",
@@ -427,7 +427,7 @@ dependencies = [
[[package]]
name = "brk_core"
version = "0.0.20"
version = "0.0.29"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -444,7 +444,7 @@ dependencies = [
[[package]]
name = "brk_exit"
version = "0.0.20"
version = "0.0.29"
dependencies = [
"brk_logger",
"ctrlc",
@@ -453,7 +453,7 @@ dependencies = [
[[package]]
name = "brk_fetcher"
version = "0.0.20"
version = "0.0.29"
dependencies = [
"brk_core",
"brk_logger",
@@ -466,7 +466,7 @@ dependencies = [
[[package]]
name = "brk_indexer"
version = "0.0.20"
version = "0.0.29"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -485,7 +485,7 @@ dependencies = [
[[package]]
name = "brk_logger"
version = "0.0.20"
version = "0.0.29"
dependencies = [
"color-eyre",
"env_logger",
@@ -495,7 +495,7 @@ dependencies = [
[[package]]
name = "brk_parser"
version = "0.0.20"
version = "0.0.29"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -510,7 +510,7 @@ dependencies = [
[[package]]
name = "brk_query"
version = "0.0.20"
version = "0.0.29"
dependencies = [
"brk_computer",
"brk_indexer",
@@ -526,7 +526,7 @@ dependencies = [
[[package]]
name = "brk_server"
version = "0.0.20"
version = "0.0.29"
dependencies = [
"axum",
"brk_computer",
@@ -552,7 +552,7 @@ dependencies = [
[[package]]
name = "brk_vec"
version = "0.0.20"
version = "0.0.29"
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",
@@ -648,9 +648,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.18"
version = "1.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
dependencies = [
"jobserver",
"libc",
@@ -694,9 +694,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.35"
version = "4.5.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
dependencies = [
"clap_builder",
"clap_derive",
@@ -704,9 +704,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.35"
version = "4.5.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
dependencies = [
"anstream",
"anstyle",
@@ -851,9 +851,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.14"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
@@ -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",
@@ -1129,7 +1129,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
dependencies = [
"crc32fast",
"miniz_oxide 0.8.7",
"miniz_oxide 0.8.8",
]
[[package]]
@@ -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.5"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260"
checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6"
dependencies = [
"jiff-static",
"jiff-tzdb-platform",
@@ -1485,9 +1493,9 @@ dependencies = [
[[package]]
name = "jiff-static"
version = "0.2.5"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c"
checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254"
dependencies = [
"proc-macro2",
"quote",
@@ -1549,15 +1557,15 @@ 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"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "lock_api"
@@ -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",
@@ -1670,18 +1672,18 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.8.7"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]
[[package]]
name = "minreq"
version = "2.13.3"
version = "2.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567496f13503d6cae8c9f961f34536850275f396307d7a6b981eef1464032f53"
checksum = "f0d2aaba477837b46ec1289588180fabfccf0c3b1d1a0c6b1866240cd6cd5ce9"
dependencies = [
"log",
"rustls",
@@ -1789,9 +1791,9 @@ checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564"
[[package]]
name = "oxc"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c274a11ab2471eea5f970d943ecb7b64dc9fa21d89ac5c968fc0f48ca9971196"
checksum = "339134dd7e4bb36c2d0e97601fe4193b37649f729e94e7c26667feecd439b27e"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1832,9 +1834,9 @@ dependencies = [
[[package]]
name = "oxc_allocator"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fef6db9c542af8b1b0889c4fdbc4dfd0a13c5bec0f94ef39bb826084465d6b1e"
checksum = "56c3dd872f5f819775c28163977399a1dd4f2177f3745dcb1bfd4214bae27e43"
dependencies = [
"allocator-api2",
"bumpalo",
@@ -1846,9 +1848,9 @@ dependencies = [
[[package]]
name = "oxc_ast"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c269cf713bed18d74e957045d2b1cf57827333a66aad2b2c136c9f8b79940bd2"
checksum = "a1bebb401e36d73f35400ee9609ab6372b457543ce7bb1ef17c922235f24ba7b"
dependencies = [
"bitflags",
"cow-utils",
@@ -1863,9 +1865,9 @@ dependencies = [
[[package]]
name = "oxc_ast_macros"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1d8afe9d7e4aed37d6e8906250db4872f514c15a244268ce3da09ccb4017900"
checksum = "3b997c2a787321814d6e92f212486b4d590af4dbfff5c2265d3b0386e016eafe"
dependencies = [
"proc-macro2",
"quote",
@@ -1874,9 +1876,9 @@ dependencies = [
[[package]]
name = "oxc_ast_visit"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d360f012f970b3e79f8f9863abb83b373014eb9d1a23fecd7ce33e555cc3151"
checksum = "e1ee363bd1ff2000ae2a1052bb4d68e827e62f76e0139449ad1301ad9adc6ac4"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1886,9 +1888,9 @@ dependencies = [
[[package]]
name = "oxc_cfg"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17a48085dd0ca7e8f3ad946a131d8229af783be65cbf4965ff6379e432f6b625"
checksum = "77cb606168f768c747885ae48272287a028de5374db865c29d6d307d3b601a97"
dependencies = [
"bitflags",
"itertools",
@@ -1901,9 +1903,9 @@ dependencies = [
[[package]]
name = "oxc_codegen"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1e6d4e1c953205c62effeb61c1da684dd1668feaf21b4fe15c2b603526355f6"
checksum = "4afef88d94fe9eee073332e3f908bd429baa989300d83152dab10dad99bd2a66"
dependencies = [
"bitflags",
"cow-utils",
@@ -1922,15 +1924,15 @@ dependencies = [
[[package]]
name = "oxc_data_structures"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e2866696e6bb90151c5687a961c3ff1fa2726436869d42eea14cf2d5c663590"
checksum = "235fe6251a3f06813e77b45f3cd9c7ec5d0a23741c33f6c308b5af393d5d2409"
[[package]]
name = "oxc_diagnostics"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d7800336d376a2baeafb9d2bcdc9fbef254863c33810e6d1cc4a431cd0048ef"
checksum = "2cd0f62519773d942baf1a17474e53b535182182f2cb41319ad1c2782e34310d"
dependencies = [
"cow-utils",
"oxc-miette",
@@ -1938,9 +1940,9 @@ dependencies = [
[[package]]
name = "oxc_ecmascript"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0edf650adfacf84768cdfeca874d8f5ffebfd108ee8a44959c0eca82b04321c6"
checksum = "5d073fdd2a3e1adb63731faab3c3861d0f9244cc15e88453d78460a4b2a604ca"
dependencies = [
"cow-utils",
"num-bigint",
@@ -1952,9 +1954,9 @@ dependencies = [
[[package]]
name = "oxc_estree"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd77f6588d373c1b753588b784de380a7418f8e46776e25c4564739994b9a5a6"
checksum = "59df015cfd58d084b27d049281835094556a4469db3e2ac45b0676336e0aeed8"
[[package]]
name = "oxc_index"
@@ -1964,9 +1966,9 @@ checksum = "2fa07b0cfa997730afed43705766ef27792873fdf5215b1391949fec678d2392"
[[package]]
name = "oxc_mangler"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02f00ca9e544230f01d37de50ba710136763a13804303bcf70cf22d8155f9db5"
checksum = "7dfc3363726f4ade77ba4d9e56cc38cc306436feee7b72e1c69436fb30c02b9b"
dependencies = [
"fixedbitset",
"itertools",
@@ -1981,9 +1983,9 @@ dependencies = [
[[package]]
name = "oxc_minifier"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8670c4452b9f78012b9ede921e40f91686ad9a1af1c40aab8ee3a5f4ef8380ef"
checksum = "f095580f12a6277d4cb532310ba95cc6d1f719b6c59341fb392b71e37d1207e6"
dependencies = [
"cow-utils",
"oxc_allocator",
@@ -2003,9 +2005,9 @@ dependencies = [
[[package]]
name = "oxc_parser"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8eaebe1ac01073b2a43a8848ae7ec24fdad3813855d72bb1942908632654f94"
checksum = "8d54fd003e5347dcb5ccdcc96cb283bf32a648ff291912b5a6e5e718b3359935"
dependencies = [
"bitflags",
"cow-utils",
@@ -2026,9 +2028,9 @@ dependencies = [
[[package]]
name = "oxc_regular_expression"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9ad8833ffba010e9f4cced1482297282728b0dc1c410cd716f206d214250f3f"
checksum = "5e6e8058355e43a2388112eddf73031d8749b40255ad6f3a824e35fe0c6addec"
dependencies = [
"oxc_allocator",
"oxc_ast_macros",
@@ -2042,9 +2044,9 @@ dependencies = [
[[package]]
name = "oxc_semantic"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0523c9521e3ee63d6e0b463941e030b482977d7c323ee802eebacac8e5227bc8"
checksum = "3f9f297db174c99b43e1b268b0960dcc588767093575cc39bb5517196a7a21d3"
dependencies = [
"itertools",
"oxc_allocator",
@@ -2078,9 +2080,9 @@ dependencies = [
[[package]]
name = "oxc_span"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bcfa55b4e3de93d39528b4ac616c00918dc73b6f6828403b3c56a7dde7af23b"
checksum = "69575b94d8fdac097784abf48a5e43ee38375aa377f8e9bb8bc80f2ef3d2269c"
dependencies = [
"compact_str",
"oxc-miette",
@@ -2091,9 +2093,9 @@ dependencies = [
[[package]]
name = "oxc_syntax"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bc0d1f63b254791867280db5e582d7411c112f0798eee50bfdb8cdd2ef80c93"
checksum = "32c4940ed3e38ce51fb6bc3e880edb815182bdcdc19c6dc110857625aac9c9a7"
dependencies = [
"bitflags",
"cow-utils",
@@ -2112,9 +2114,9 @@ dependencies = [
[[package]]
name = "oxc_traverse"
version = "0.62.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a1f5183b3db89b3b3c7621741d5e2b9c13996dec5be4ad5e7adf86fdcfd24f0"
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",
@@ -2574,9 +2578,9 @@ dependencies = [
[[package]]
name = "self_cell"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
[[package]]
name = "seq-macro"
@@ -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",
@@ -3404,9 +3408,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.4"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
dependencies = [
"memchr",
]
@@ -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",
]
+13 -5
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.20"
package.version = "0.0.29"
package.repository = "https://github.com/bitcoinresearchkit/brk"
[profile.release]
@@ -31,13 +31,13 @@ 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.35", 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.5"
fjall = "2.9.0"
jiff = "0.2.10"
log = { version = "0.4.27" }
minreq = { version = "2.13.3", features = ["https", "serde_json"] }
minreq = { version = "2.13.4", features = ["https", "serde_json"] }
rayon = "1.10.0"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = { version = "1.0.140", features = ["float_roundtrip"] }
@@ -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" }
+2 -1
View File
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk">
<img src="https://deps.rs/crate/brk/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
@@ -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`
+16 -4
View File
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk_cli">
<img src="https://deps.rs/crate/brk_cli/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
@@ -59,16 +59,28 @@ To be determined
- Unix based operating system (Mac OS or Linux)
- Ubuntu users need to install `open-ssl` via `sudo apt install libssl-dev pkg-config`
## Install
## Download
### Binaries
You can find a pre-built binary for your operating system on the releases page ([link](https://github.com/bitcoinresearchkit/brk/releases/latest)).
### Cargo
```bash
# Install
cargo install brk # or `cargo install brk_cli`, the result is the same
# Update
cargo install brk # or `cargo install-update -a` if you have `cargo-update` installed
```
## Update
### Source
```bash
cargo install brk # or `cargo install-update -a` if you have `cargo-update` installed
git clone https://github.com/bitcoinresearchkit/brk.git
cd brk/crates/brk
cargo run -r
```
## Usage
+18
View File
@@ -58,7 +58,25 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
};
if config.process() {
let wait_for_synced_node = || -> color_eyre::Result<()> {
let is_synced = || -> color_eyre::Result<bool> {
let info = rpc.get_blockchain_info()?;
Ok(info.headers == info.blocks)
};
if !is_synced()? {
info!("Waiting for node to be synced...");
while !is_synced()? {
sleep(Duration::from_secs(1))
}
}
Ok(())
};
loop {
wait_for_synced_node()?;
let block_count = rpc.get_block_count()?;
info!("{} blocks found.", block_count + 1);
+1 -1
View File
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk_computer">
<img src="https://deps.rs/crate/brk_computer/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
+190 -25
View File
@@ -2,15 +2,14 @@ use core::error;
use std::{
cmp::Ordering,
fmt::Debug,
ops::{Add, Sub},
ops::Add,
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))
})?;
@@ -249,11 +276,12 @@ where
let index = max_from.min(I::from(self.len()));
let one = T::from(1);
let mut prev_index: Option<I> = None;
first_indexes.iter_from(index, |(i, v, ..)| {
first_indexes.iter_from(index, |(index, v, ..)| {
if let Some(prev_index) = prev_index.take() {
self.forced_push_at(prev_index, v.checked_sub(one).unwrap(), exit)?;
let value = v.checked_sub(one).unwrap();
self.forced_push_at(prev_index, value, exit)?;
}
prev_index.replace(i);
prev_index.replace(index);
Ok(())
})?;
if let Some(prev_index) = prev_index {
@@ -276,7 +304,65 @@ where
) -> Result<()>
where
T: From<T2>,
T2: StoredType + Copy + Add<usize, Output = T2> + CheckedSub<T2> + TryInto<T> + Default,
T2: StoredType
+ StoredIndex
+ Copy
+ Add<usize, Output = T2>
+ CheckedSub<T2>
+ TryInto<T>
+ Default,
<T2 as TryInto<T>>::Error: error::Error + 'static,
{
let opt: Option<Box<dyn FnMut(T2) -> bool>> = None;
self.compute_filtered_count_from_indexes_(max_from, first_indexes, last_indexes, opt, exit)
}
pub fn compute_filtered_count_from_indexes<T2, F>(
&mut self,
max_from: I,
first_indexes: &mut StoredVec<I, T2>,
last_indexes: &mut StoredVec<I, T2>,
filter: F,
exit: &Exit,
) -> Result<()>
where
T: From<T2>,
T2: StoredType
+ StoredIndex
+ Copy
+ Add<usize, Output = T2>
+ CheckedSub<T2>
+ TryInto<T>
+ Default,
<T2 as TryInto<T>>::Error: error::Error + 'static,
F: FnMut(T2) -> bool,
{
self.compute_filtered_count_from_indexes_(
max_from,
first_indexes,
last_indexes,
Some(Box::new(filter)),
exit,
)
}
fn compute_filtered_count_from_indexes_<T2>(
&mut self,
max_from: I,
first_indexes: &mut StoredVec<I, T2>,
last_indexes: &mut StoredVec<I, T2>,
mut filter: Option<Box<dyn FnMut(T2) -> bool + '_>>,
exit: &Exit,
) -> Result<()>
where
T: From<T2>,
T2: StoredType
+ StoredIndex
+ Copy
+ Add<usize, Output = T2>
+ CheckedSub<T2>
+ TryInto<T>
+ Default,
<T2 as TryInto<T>>::Error: error::Error + 'static,
{
self.validate_computed_version_or_reset_file(
@@ -285,11 +371,14 @@ 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();
let count = (*last_index + 1_usize)
.checked_sub(first_index)
.unwrap_or_default();
self.forced_push_at(i, count.into(), exit)
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 {
range.count()
};
self.forced_push_at(i, T::from(T2::from(count)), exit)
})?;
self.safe_flush(exit)
@@ -315,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,
)
})?;
@@ -328,22 +417,98 @@ where
max_from: I,
first_indexes: &mut StoredVec<I, T2>,
last_indexes: &mut StoredVec<I, T2>,
source: &mut StoredVec<T2, T>,
exit: &Exit,
) -> Result<()>
where
T: From<T2>,
T2: StoredType + Copy + Add<usize, Output = T2> + Sub<T2, Output = T2> + TryInto<T>,
<T2 as TryInto<T>>::Error: error::Error + 'static,
T: From<usize> + Add<T, Output = T>,
T2: StoredIndex + StoredType,
{
self.validate_computed_version_or_reset_file(
Version::ZERO + self.version() + first_indexes.version() + last_indexes.version(),
)?;
let index = max_from.min(I::from(self.len()));
first_indexes.iter_from(index, |(index, first_index, ..)| {
let last_index = last_indexes.cached_get(index)?.unwrap();
let count = *last_index + 1_usize - first_index;
self.forced_push_at(index, count.into(), exit)
first_indexes.iter_from(index, |(i, first_index, ..)| {
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.double_unwrap_cached_get(T2::from(i));
});
self.forced_push_at(i, sum, exit)
})?;
self.safe_flush(exit)
}
}
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)
+56 -43
View File
@@ -1,6 +1,6 @@
use std::{fs, path::Path};
use brk_core::{CheckedSub, StoredU32, StoredU64, StoredUsize, Timestamp, Weight};
use brk_core::{CheckedSub, Height, StoredU32, StoredU64, StoredUsize, Timestamp, Weight};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_parser::bitcoin;
@@ -8,15 +8,18 @@ use brk_vec::{Compressed, Version};
use super::{
Indexes,
base::ComputedVec,
grouped::{ComputedVecsFromHeight, StorableVecGeneatorOptions},
indexes,
};
#[derive(Clone)]
pub struct Vecs {
pub height_to_interval: ComputedVec<Height, Timestamp>,
pub indexes_to_block_interval: ComputedVecsFromHeight<Timestamp>,
pub indexes_to_block_count: ComputedVecsFromHeight<StoredU32>,
pub indexes_to_block_weight: ComputedVecsFromHeight<Weight>,
pub height_to_vbytes: ComputedVec<Height, StoredU64>,
pub indexes_to_block_vbytes: ComputedVecsFromHeight<StoredU64>,
pub indexes_to_block_size: ComputedVecsFromHeight<StoredUsize>,
}
@@ -26,10 +29,15 @@ impl Vecs {
fs::create_dir_all(path)?;
Ok(Self {
height_to_interval: ComputedVec::forced_import(
&path.join("height_to_interval"),
Version::ZERO,
compressed,
)?,
indexes_to_block_interval: ComputedVecsFromHeight::forced_import(
path,
"block_interval",
true,
false,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default()
@@ -61,10 +69,15 @@ impl Vecs {
compressed,
StorableVecGeneatorOptions::default().add_sum().add_total(),
)?,
height_to_vbytes: ComputedVec::forced_import(
&path.join("height_to_vbytes"),
Version::ZERO,
compressed,
)?,
indexes_to_block_vbytes: ComputedVecsFromHeight::forced_import(
path,
"block_vbytes",
true,
false,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum().add_total(),
@@ -79,29 +92,26 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> color_eyre::Result<()> {
self.indexes_to_block_interval.compute_all(
indexer,
self.height_to_interval.compute_transform(
starting_indexes.height,
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.double_unwrap_cached_get(prev_h);
timestamp
.checked_sub(prev_timestamp)
.unwrap_or(Timestamp::ZERO)
});
(height, interval)
},
exit,
)?;
self.indexes_to_block_interval.compute_rest(
indexes,
starting_indexes,
exit,
|v, indexer, _, starting_indexes, exit| {
let indexer_vecs = indexer.mut_vecs();
v.compute_transform(
starting_indexes.height,
indexer_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.get(prev_h).unwrap().unwrap();
timestamp
.checked_sub(prev_timestamp)
.unwrap_or(Timestamp::ZERO)
});
(height, interval)
},
exit,
)
},
Some(self.height_to_interval.mut_vec()),
)?;
self.indexes_to_block_count.compute_all(
@@ -110,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_block_weight.mut_vec(),
|(h, ..)| (h, StoredU32::from(1_u32)),
indexer.mut_vecs().height_to_weight.mut_vec(),
|h| (h, StoredU32::from(1_u32)),
exit,
)
},
@@ -123,34 +133,33 @@ impl Vecs {
indexes,
starting_indexes,
exit,
Some(indexer.mut_vecs().height_to_block_weight.mut_vec()),
Some(indexer.mut_vecs().height_to_weight.mut_vec()),
)?;
self.indexes_to_block_size.compute_rest(
indexes,
starting_indexes,
exit,
Some(indexer.mut_vecs().height_to_block_size.mut_vec()),
Some(indexer.mut_vecs().height_to_total_size.mut_vec()),
)?;
self.indexes_to_block_vbytes.compute_all(
indexer,
self.height_to_vbytes.compute_transform(
starting_indexes.height,
indexer.mut_vecs().height_to_weight.mut_vec(),
|(h, w, ..)| {
(
h,
StoredU64::from(bitcoin::Weight::from(w).to_vbytes_floor()),
)
},
exit,
)?;
self.indexes_to_block_vbytes.compute_rest(
indexes,
starting_indexes,
exit,
|v, indexer, _, starting_indexes, exit| {
v.compute_transform(
starting_indexes.height,
indexer.mut_vecs().height_to_block_weight.mut_vec(),
|(h, w, ..)| {
(
h,
StoredU64::from(bitcoin::Weight::from(w).to_vbytes_floor()),
)
},
exit,
)
},
Some(self.height_to_vbytes.mut_vec()),
)?;
Ok(())
@@ -158,6 +167,10 @@ impl Vecs {
pub fn as_any_vecs(&self) -> Vec<&dyn brk_vec::AnyStoredVec> {
[
vec![
self.height_to_interval.any_vec(),
self.height_to_vbytes.any_vec(),
],
self.indexes_to_block_interval.any_vecs(),
self.indexes_to_block_count.any_vecs(),
self.indexes_to_block_weight.any_vecs(),
@@ -4,6 +4,7 @@ use brk_exit::Exit;
use brk_vec::{
Compressed, DynamicVec, GenericVec, Result, StoredIndex, StoredType, StoredVec, Version,
};
use color_eyre::eyre::ContextCompat;
use crate::storage::vecs::base::ComputedVec;
@@ -15,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,
@@ -37,6 +40,7 @@ where
pub fn forced_import(
path: &Path,
name: &str,
version: Version,
compressed: Compressed,
options: StorableVecGeneatorOptions,
) -> color_eyre::Result<Self> {
@@ -66,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()
}),
};
@@ -127,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)?;
@@ -157,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, ..)| {
let last_index = *last_indexes.cached_get(i)?.unwrap();
first_indexes.iter_from(index, |(i, first_index, first_indexes)| {
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)?;
}
@@ -199,7 +243,27 @@ where
values.sort_unstable();
if let Some(max) = self.max.as_mut() {
max.forced_push_at(i, values.last().unwrap().clone(), exit)?;
max.forced_push_at(
i,
values
.last()
.context("expect some")
.inspect_err(|_| {
dbg!(
&values,
max.path(),
first_indexes.path(),
first_index,
last_indexes.path(),
last_index,
source.len(),
source.path()
);
})
.unwrap()
.clone(),
exit,
)?;
}
if let Some(_90p) = self._90p.as_mut() {
@@ -229,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)?;
}
}
@@ -284,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()
@@ -299,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)?;
}
@@ -318,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)?;
}
@@ -360,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)?;
}
@@ -377,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() {
@@ -384,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)?;
}
}
@@ -436,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)?,
})
}
@@ -18,7 +18,6 @@ where
T: ComputedType + PartialOrd,
{
pub txindex: Option<ComputedVec<Txindex, T>>,
pub txindex_extra: ComputedVecBuilder<Txindex, T>,
pub height: ComputedVecBuilder<Height, T>,
pub dateindex: ComputedVecBuilder<Dateindex, T>,
pub weekindex: ComputedVecBuilder<Weekindex, T>,
@@ -30,6 +29,8 @@ where
pub decadeindex: ComputedVecBuilder<Decadeindex, T>,
}
const VERSION: Version = Version::ZERO;
impl<T> ComputedVecsFromTxindex<T>
where
T: ComputedType + Ord + From<f64>,
@@ -43,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}")),
@@ -52,30 +55,29 @@ where
.unwrap()
});
let txindex_extra = ComputedVecBuilder::forced_import(
path,
name,
compressed,
StorableVecGeneatorOptions::default(),
)?;
let height = ComputedVecBuilder::forced_import(path, name, compressed, options)?;
let dateindex = 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,
txindex_extra,
height,
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)?,
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,
)?,
})
}
@@ -119,9 +121,6 @@ where
) -> color_eyre::Result<()> {
let txindex = txindex.unwrap_or_else(|| self.txindex.as_mut().unwrap().mut_vec());
self.txindex_extra
.extend(starting_indexes.txindex, txindex, exit)?;
self.height.compute(
starting_indexes.height,
txindex,
@@ -192,7 +191,6 @@ where
pub fn any_vecs(&self) -> Vec<&dyn AnyStoredVec> {
[
self.txindex.as_ref().map_or(vec![], |v| vec![v.any_vec()]),
self.txindex_extra.any_vecs(),
self.height.any_vecs(),
self.dateindex.any_vecs(),
self.weekindex.any_vecs(),
@@ -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()
}
}
+281 -102
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,8 +14,7 @@ use super::ComputedVec;
#[derive(Clone)]
pub struct Vecs {
// pub height_to_last_addressindex: StorableVec<Height, Addressindex>,
// pub height_to_last_txoutindex: StorableVec<Height, Txoutindex>,
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>,
@@ -29,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>,
@@ -47,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>,
@@ -309,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,
)?,
})
}
@@ -320,15 +416,15 @@ impl Vecs {
) -> color_eyre::Result<Indexes> {
let indexer_vecs = indexer.mut_vecs();
let height_count = indexer_vecs.height_to_block_size.len();
let height_count = indexer_vecs.height_to_total_size.len();
let txindexes_count = indexer_vecs.txindex_to_txid.len();
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,
)?;
@@ -342,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,
)?;
@@ -363,10 +455,12 @@ impl Vecs {
exit,
)?;
let decremented_starting_height = starting_indexes.height.decremented().unwrap_or_default();
let starting_dateindex = self
.height_to_dateindex
.cached_get(starting_indexes.height.decremented().unwrap_or_default())?
.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(starting_indexes.height.decremented().unwrap_or_default())?
.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(starting_indexes.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(starting_indexes.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;
@@ -19,38 +19,55 @@ use super::{
#[derive(Clone)]
pub struct Vecs {
// pub dateindex_to_close: ComputedVec<Dateindex, Close<Dollars>>,
pub dateindex_to_close_in_cents: ComputedVec<Dateindex, Close<Cents>>,
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)?;
@@ -71,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,
@@ -101,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,
@@ -149,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(),
)?,
@@ -184,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(),
)?,
@@ -196,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,
)?,
})
}
@@ -243,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)
@@ -455,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,
},
)
@@ -503,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,
},
)
@@ -547,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,
},
)
@@ -595,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,
},
)
@@ -639,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,
},
)
@@ -690,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,
},
)
@@ -733,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,
@@ -742,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,
@@ -757,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(())
}
@@ -788,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(),
@@ -799,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(())
}
@@ -1,91 +1,89 @@
use std::{fs, path::Path};
use brk_core::{Sats, StoredU8, StoredU32, StoredU64, Txindex, Txinindex, Txoutindex};
use brk_core::{
CheckedSub, Feerate, Sats, StoredU32, StoredU64, StoredUsize, TxVersion, Txindex, Txinindex,
Txoutindex, Weight,
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_vec::{Compressed, DynamicVec, Version};
use brk_parser::bitcoin;
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 height_to_fee: ComputedVec<Txindex, Sats>,
// pub height_to_inputcount: ComputedVec<Height, u32>,
// pub height_to_maxfeerate: ComputedVec<Height, Feerate>,
// pub height_to_medianfeerate: ComputedVec<Height, Feerate>,
// pub height_to_minfeerate: ComputedVec<Height, Feerate>,
// pub height_to_outputcount: ComputedVec<Height, u32>,
// pub height_to_subsidy: ComputedVec<Height, u32>,
// pub height_to_totalfees: ComputedVec<Height, Sats>,
// pub txindex_to_fee: ComputedVec<Txindex, Sats>,
// pub txindex_to_feerate: ComputedVec<Txindex, Feerate>,
// pub txindex_to_input_sum: ComputedVec<Txindex, Sats>,
// pub txindex_to_output_sum: ComputedVec<Txindex, Sats>,
// pub txindex_to_output_value: ComputedVecsFromTxindex<Sats>,
pub txindex_to_v1: ComputedVec<Txindex, StoredU8>,
pub txindex_to_v2: ComputedVec<Txindex, StoredU8>,
pub txindex_to_v3: ComputedVec<Txindex, StoredU8>,
pub indexes_to_tx_count: ComputedVecsFromHeight<StoredU64>,
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>,
// pub txindex_to_is_v1: ComputedVec<Txindex, bool>,
pub indexes_to_tx_v1: ComputedVecsFromHeight<StoredU32>,
// pub txindex_to_is_v2: ComputedVec<Txindex, bool>,
pub indexes_to_tx_v2: ComputedVecsFromHeight<StoredU32>,
// pub txindex_to_is_v3: ComputedVec<Txindex, bool>,
pub indexes_to_tx_v3: ComputedVecsFromHeight<StoredU32>,
// pub txinindex_to_value: ComputedVec<Txinindex, Sats>,
pub height_to_tx_count: ComputedVecsFromHeight<StoredU64>,
pub indexes_to_tx_vsize: ComputedVecsFromTxindex<StoredUsize>,
pub indexes_to_tx_weight: ComputedVecsFromTxindex<Weight>,
pub txindex_to_input_count: ComputedVecsFromTxindex<StoredU64>,
pub txindex_to_is_coinbase: ComputedVec<Txindex, bool>,
pub txindex_to_output_count: ComputedVecsFromTxindex<StoredU64>,
pub txindex_to_vsize: ComputedVec<Txindex, StoredUsize>,
pub txindex_to_weight: ComputedVec<Txindex, Weight>,
/// Value == 0 when Coinbase
pub txinindex_to_value: ComputedVec<Txinindex, 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 {
height_to_tx_count: ComputedVecsFromHeight::forced_import(
indexes_to_tx_count: ComputedVecsFromHeight::forced_import(
path,
"tx_count",
true,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum().add_total(),
StorableVecGeneatorOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
.add_sum()
.add_total(),
)?,
// height_to_fee: StorableVec::forced_import(&path.join("height_to_fee"), Version::ZERO)?,
// height_to_input_count: StorableVec::forced_import(
// &path.join("height_to_input_count"),
// Version::ZERO,
// )?,
// height_to_maxfeerate: StorableVec::forced_import(&path.join("height_to_maxfeerate"), Version::ZERO)?,
// height_to_medianfeerate: StorableVec::forced_import(&path.join("height_to_medianfeerate"), Version::ZERO)?,
// height_to_minfeerate: StorableVec::forced_import(&path.join("height_to_minfeerate"), Version::ZERO)?,
// height_to_output_count: StorableVec::forced_import(
// &path.join("height_to_output_count"),
// Version::ZERO,
// )?,
// height_to_subsidy: StorableVec::forced_import(&path.join("height_to_subsidy"), Version::ZERO)?,
// height_to_totalfees: StorableVec::forced_import(&path.join("height_to_totalfees"), Version::ZERO)?,
// height_to_txcount: StorableVec::forced_import(&path.join("height_to_txcount"), Version::ZERO)?,
// txindex_to_fee: StorableVec::forced_import(
// &path.join("txindex_to_fee"),
// Version::ZERO,
// )?,
txindex_to_is_coinbase: ComputedVec::forced_import(
&path.join("txindex_to_is_coinbase"),
Version::ZERO,
compressed,
)?,
// txindex_to_feerate: StorableVec::forced_import(&path.join("txindex_to_feerate"), Version::ZERO)?,
txindex_to_input_count: ComputedVecsFromTxindex::forced_import(
path,
"input_count",
true,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum().add_total(),
StorableVecGeneatorOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
.add_sum()
.add_total(),
)?,
txindex_to_output_count: ComputedVecsFromTxindex::forced_import(
path,
@@ -93,35 +91,18 @@ impl Vecs {
true,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum().add_total(),
StorableVecGeneatorOptions::default()
.add_average()
.add_minmax()
.add_percentiles()
.add_sum()
.add_total(),
)?,
// txindex_to_output_value: ComputedVecsFromTxindex::forced_import(
// path,
// "output_value",
// Version::ZERO,
// compressed,
// StorableVecGeneatorOptions::default().add_sum().add_total(),
// )?,
txinindex_to_value: ComputedVec::forced_import(
&path.join("txinindex_to_value"),
Version::ZERO,
compressed,
)?,
txindex_to_v1: ComputedVec::forced_import(
&path.join("txindex_to_v1"),
Version::ZERO,
compressed,
)?,
txindex_to_v2: ComputedVec::forced_import(
&path.join("txindex_to_v2"),
Version::ZERO,
compressed,
)?,
txindex_to_v3: ComputedVec::forced_import(
&path.join("txindex_to_v3"),
Version::ZERO,
compressed,
)?,
indexes_to_tx_v1: ComputedVecsFromHeight::forced_import(
path,
"tx_v1",
@@ -146,6 +127,113 @@ impl Vecs {
compressed,
StorableVecGeneatorOptions::default().add_sum().add_total(),
)?,
indexes_to_input_value: ComputedVecsFromTxindex::forced_import(
path,
"input_value",
true,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default()
.add_average()
.add_sum()
.add_total(),
)?,
indexes_to_output_value: ComputedVecsFromTxindex::forced_import(
path,
"output_value",
true,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default()
.add_average()
.add_sum()
.add_total(),
)?,
indexes_to_fee: ComputedValueVecsFromTxindex::forced_import(
path,
"fee",
true,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default()
.add_sum()
.add_total()
.add_percentiles()
.add_minmax()
.add_average(),
compute_dollars,
)?,
indexes_to_feerate: ComputedVecsFromTxindex::forced_import(
path,
"feerate",
true,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default()
.add_percentiles()
.add_minmax()
.add_average(),
)?,
txindex_to_weight: ComputedVec::forced_import(
&path.join("txindex_to_weight"),
Version::ZERO,
compressed,
)?,
txindex_to_vsize: ComputedVec::forced_import(
&path.join("txindex_to_vsize"),
Version::ZERO,
compressed,
)?,
indexes_to_tx_vsize: ComputedVecsFromTxindex::forced_import(
path,
"tx_vsize",
false,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default()
.add_percentiles()
.add_minmax()
.add_average(),
)?,
indexes_to_tx_weight: ComputedVecsFromTxindex::forced_import(
path,
"tx_weight",
false,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default()
.add_percentiles()
.add_minmax()
.add_average(),
)?,
indexes_to_subsidy: ComputedValueVecsFromHeight::forced_import(
path,
"subsidy",
true,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default()
.add_percentiles()
.add_sum()
.add_total()
.add_minmax()
.add_average(),
compute_dollars,
)?,
indexes_to_coinbase: ComputedValueVecsFromHeight::forced_import(
path,
"coinbase",
true,
Version::ZERO,
compressed,
StorableVecGeneatorOptions::default()
.add_sum()
.add_total()
.add_percentiles()
.add_minmax()
.add_average(),
compute_dollars,
)?,
})
}
@@ -154,9 +242,10 @@ impl Vecs {
indexer: &mut Indexer,
indexes: &mut indexes::Vecs,
starting_indexes: &Indexes,
marketprices: &mut Option<&mut marketprice::Vecs>,
exit: &Exit,
) -> color_eyre::Result<()> {
self.height_to_tx_count.compute_all(
self.indexes_to_tx_count.compute_all(
indexer,
indexes,
starting_indexes,
@@ -201,13 +290,33 @@ impl Vecs {
},
)?;
// self.txindex_to_output_value.compute_rest(
// indexer,
// indexes,
// starting_indexes,
// exit,
// Some(indexer.mut_vecs().txoutindex_to_value.mut_vec()),
// )?;
let mut compute_indexes_to_tx_vany =
|indexes_to_tx_vany: &mut ComputedVecsFromHeight<StoredU32>, txversion| {
indexes_to_tx_vany.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, indexer, indexes, starting_indexes, exit| {
let indexer_vecs = indexer.mut_vecs();
vec.compute_filtered_count_from_indexes(
starting_indexes.height,
indexer_vecs.height_to_first_txindex.mut_vec(),
indexes.height_to_last_txindex.mut_vec(),
|txindex| {
let v = indexer_vecs
.txindex_to_txversion
.double_unwrap_cached_get(txindex);
v == txversion
},
exit,
)
},
)
};
compute_indexes_to_tx_vany(&mut self.indexes_to_tx_v1, TxVersion::ONE)?;
compute_indexes_to_tx_vany(&mut self.indexes_to_tx_v2, TxVersion::TWO)?;
compute_indexes_to_tx_vany(&mut self.indexes_to_tx_v3, TxVersion::THREE)?;
let indexer_vecs = indexer.mut_vecs();
@@ -218,50 +327,34 @@ impl Vecs {
exit,
)?;
self.txindex_to_v1.compute_transform(
self.txindex_to_weight.compute_transform(
starting_indexes.txindex,
indexer_vecs.txindex_to_txversion.mut_vec(),
|(i, v, ..)| (i, StoredU8::from(v)),
indexer_vecs.txindex_to_base_size.mut_vec(),
|(txindex, base_size, ..)| {
let total_size = indexer_vecs
.txindex_to_total_size
.mut_vec()
.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;
let weight = Weight::from(bitcoin::Weight::from_wu_usize(wu));
(txindex, weight)
},
exit,
)?;
// self.indexes_to_tx_v1.compute_all(
// indexer,
// indexes,
// starting_indexes,
// exit,
// |vec, indexer, indexes, indexes, exit| {
// vec.compute_transform(
// starting_indexes.height,
// indexer.mut_vecs().txindex_to_txversion.mut_vec(),
// || {},
// exit,
// )?;
// },
// )?;
self.txindex_to_v2.compute_transform(
self.txindex_to_vsize.compute_transform(
starting_indexes.txindex,
indexer_vecs.txindex_to_txversion.mut_vec(),
|(i, v, ..)| (i, StoredU8::from(v)),
self.txindex_to_weight.mut_vec(),
|(txindex, weight, ..)| {
let vbytes =
StoredUsize::from(bitcoin::Weight::from(weight).to_vbytes_ceil() as usize);
(txindex, vbytes)
},
exit,
)?;
// self.indexes_to_tx_v1.compute_rest(
// starting_indexes.txindex,
// indexer_vecs.txindex_to_txversion.mut_vec(),
// |(i, v, ..)| (i, StoredU8::from(v)),
// exit,
// )?;
self.txindex_to_v3.compute_transform(
starting_indexes.txindex,
indexer_vecs.txindex_to_txversion.mut_vec(),
|(i, v, ..)| (i, StoredU8::from(v)),
exit,
)?;
// self.indexes_to_tx_v1.compute_rest(
// starting_indexes.txindex,
// indexer_vecs.txindex_to_txversion.mut_vec(),
// |(i, v, ..)| (i, StoredU8::from(v)),
// exit,
// )?;
self.txinindex_to_value.compute_transform(
starting_indexes.txinindex,
@@ -269,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!()
@@ -284,10 +377,175 @@ impl Vecs {
exit,
)?;
// self.txindex_to_fee.compute_transform(
// &mut self.vecs.txindex_to_height,
// &mut indexer.vecs().height_to_first_txindex,
// )?;
self.indexes_to_output_value.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, indexer, indexes, starting_indexes, exit| {
let indexer_vecs = indexer.mut_vecs();
vec.compute_sum_from_indexes(
starting_indexes.txindex,
indexer_vecs.txindex_to_first_txoutindex.mut_vec(),
indexes.txindex_to_last_txoutindex.mut_vec(),
indexer_vecs.txoutindex_to_value.mut_vec(),
exit,
)
},
)?;
self.indexes_to_input_value.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, indexer, indexes, starting_indexes, exit| {
let indexer_vecs = indexer.mut_vecs();
vec.compute_sum_from_indexes(
starting_indexes.txindex,
indexer_vecs.txindex_to_first_txinindex.mut_vec(),
indexes.txindex_to_last_txinindex.mut_vec(),
self.txinindex_to_value.mut_vec(),
exit,
)
},
)?;
self.indexes_to_fee.compute_all(
indexer,
indexes,
marketprices,
starting_indexes,
exit,
|vec, _, _, starting_indexes, exit| {
let txindex_to_output_value = self
.indexes_to_output_value
.txindex
.as_mut()
.unwrap()
.mut_vec();
vec.compute_transform(
starting_indexes.txindex,
self.indexes_to_input_value
.txindex
.as_mut()
.unwrap()
.mut_vec(),
|(txindex, input_value, ..)| {
if input_value.is_zero() {
(txindex, input_value)
} else {
let output_value =
txindex_to_output_value.double_unwrap_cached_get(txindex);
(txindex, input_value.checked_sub(output_value).unwrap())
}
},
exit,
)
},
)?;
self.indexes_to_feerate.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, _, starting_indexes, exit| {
vec.compute_transform(
starting_indexes.txindex,
self.indexes_to_fee.sats.txindex.as_mut().unwrap().mut_vec(),
|(txindex, fee, ..)| {
let vsize = self
.txindex_to_vsize
.mut_vec()
.double_unwrap_cached_get(txindex);
(txindex, Feerate::from((fee, vsize)))
},
exit,
)
},
)?;
self.indexes_to_tx_weight.compute_rest(
indexer,
indexes,
starting_indexes,
exit,
Some(self.txindex_to_weight.mut_vec()),
)?;
self.indexes_to_tx_vsize.compute_rest(
indexer,
indexes,
starting_indexes,
exit,
Some(self.txindex_to_vsize.mut_vec()),
)?;
self.indexes_to_subsidy.compute_all(
indexer,
indexes,
marketprices,
starting_indexes,
exit,
|vec, indexer, indexes, starting_indexes, exit| {
let indexer_vecs = indexer.mut_vecs();
vec.compute_transform(
starting_indexes.height,
indexer_vecs.height_to_first_txindex.mut_vec(),
|(height, txindex, ..)| {
let first_txoutindex = indexer_vecs
.txindex_to_first_txoutindex
.double_unwrap_cached_get(txindex)
.unwrap_to_usize();
let last_txoutindex = indexes
.txindex_to_last_txoutindex
.mut_vec()
.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
.double_unwrap_cached_get(Txoutindex::from(txoutindex));
});
(height, sats)
},
exit,
)
},
)?;
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
.sats
.height
.as_mut()
.unwrap()
.mut_vec(),
|(height, subsidy, ..)| {
let fees = self
.indexes_to_fee
.sats
.height
.unwrap_sum()
.mut_vec()
.double_unwrap_cached_get(height);
(height, subsidy.checked_sub(fees).unwrap())
},
exit,
)
},
)?;
Ok(())
}
@@ -297,16 +555,23 @@ impl Vecs {
vec![
self.txindex_to_is_coinbase.any_vec(),
self.txinindex_to_value.any_vec(),
self.txindex_to_v1.any_vec(),
self.txindex_to_v2.any_vec(),
self.txindex_to_v3.any_vec(),
self.txindex_to_weight.any_vec(),
self.txindex_to_vsize.any_vec(),
],
self.height_to_tx_count.any_vecs(),
self.txindex_to_output_count.any_vecs(),
self.txindex_to_input_count.any_vecs(),
self.indexes_to_tx_count.any_vecs(),
self.indexes_to_coinbase.any_vecs(),
self.indexes_to_fee.any_vecs(),
self.indexes_to_feerate.any_vecs(),
self.indexes_to_input_value.any_vecs(),
self.indexes_to_output_value.any_vecs(),
self.indexes_to_subsidy.any_vecs(),
self.indexes_to_tx_v1.any_vecs(),
self.indexes_to_tx_v2.any_vecs(),
self.indexes_to_tx_v3.any_vecs(),
self.indexes_to_tx_vsize.any_vecs(),
self.indexes_to_tx_weight.any_vecs(),
self.txindex_to_input_count.any_vecs(),
self.txindex_to_output_count.any_vecs(),
]
.concat()
}
+1 -1
View File
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk_core">
<img src="https://deps.rs/crate/brk_core/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
+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),
))
}
}
+62 -1
View File
@@ -1,5 +1,66 @@
use std::ops::{Add, Div};
use serde::Serialize;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
#[derive(Debug, Clone, Copy, Serialize, FromBytes, Immutable, IntoBytes, KnownLayout)]
use super::{Sats, StoredUsize};
#[derive(
Debug,
Clone,
Copy,
Serialize,
PartialEq,
PartialOrd,
FromBytes,
Immutable,
IntoBytes,
KnownLayout,
)]
pub struct Feerate(f32);
impl From<(Sats, StoredUsize)> for Feerate {
fn from((sats, vsize): (Sats, StoredUsize)) -> Self {
Self((f64::from(sats) / f64::from(vsize)) as f32)
}
}
impl From<f64> for Feerate {
fn from(value: f64) -> Self {
Self(value as f32)
}
}
impl From<Feerate> for f64 {
fn from(value: Feerate) -> Self {
value.0 as f64
}
}
impl Add for Feerate {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Div<usize> for Feerate {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self((self.0 as f64 / rhs as f64) as f32)
}
}
impl From<usize> for Feerate {
fn from(value: usize) -> Self {
Self(value as f32)
}
}
impl Eq for Feerate {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for Feerate {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.partial_cmp(&other.0).unwrap()
}
}
+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)
}
}
+8
View File
@@ -7,6 +7,8 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::{CheckedSub, Error};
use super::StoredU32;
#[derive(
Debug,
PartialEq,
@@ -97,3 +99,9 @@ impl From<Txindex> for ByteView {
Self::new(value.as_bytes())
}
}
impl From<Txindex> for StoredU32 {
fn from(value: Txindex) -> Self {
Self::from(value.0)
}
}
+21 -1
View File
@@ -4,9 +4,29 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
use super::StoredU8;
#[derive(Debug, Deref, Clone, Copy, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize)]
#[derive(
Debug,
Deref,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Immutable,
IntoBytes,
KnownLayout,
FromBytes,
Serialize,
)]
pub struct TxVersion(i32);
impl TxVersion {
pub const ONE: Self = Self(1);
pub const TWO: Self = Self(2);
pub const THREE: Self = Self(3);
}
impl From<bitcoin::transaction::Version> for TxVersion {
fn from(value: bitcoin::transaction::Version) -> Self {
Self(value.0)
+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)
}
}
+1 -1
View File
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk_exit">
<img src="https://deps.rs/crate/brk_exit/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
+1 -1
View File
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk_fetcher">
<img src="https://deps.rs/crate/brk_fetcher/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
+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}
+7 -6
View File
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk_indexer">
<img src="https://deps.rs/crate/brk_indexer/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
@@ -58,13 +58,14 @@ Stores: `src/storage/stores/mod.rs`
## Benchmark
### Result 1 - 2025-04-10
### Result 1 - 2025-04-12
- version: `v0.0.20`
- version: `v0.0.21`
- machine: `Macbook Pro M3 Pro (36GB RAM)`
- mode: `raw`
- from: `0`
- to: `891_810`
- time: `8 hours 27 min 3s`
- peak memory: `6.5GB`
- to: `892_098`
- time: `7 hours 10 min 22s`
- peak memory: `6.1GB`
- disk usage: `270 GB`
- overhead: `36%` (`270 GB / 741 GB`)
+3 -3
View File
@@ -81,7 +81,7 @@ impl Indexer {
self.stores.as_ref().unwrap(),
rpc,
))
.unwrap_or_else(|_| {
.unwrap_or_else(|_report| {
let indexes = Indexes::default();
indexes.push_if_needed(self.vecs.as_mut().unwrap()).unwrap();
indexes
@@ -165,8 +165,8 @@ impl Indexer {
.push_if_needed(height, block.header.difficulty_float())?;
vecs.height_to_timestamp
.push_if_needed(height, Timestamp::from(block.header.time))?;
vecs.height_to_block_size.push_if_needed(height, block.total_size().into())?;
vecs.height_to_block_weight.push_if_needed(height, block.weight().into())?;
vecs.height_to_total_size.push_if_needed(height, block.total_size().into())?;
vecs.height_to_weight.push_if_needed(height, block.weight().into())?;
let inputs = block
.txdata
+3 -3
View File
@@ -98,10 +98,10 @@ where
if !self.puts.is_empty() {
unreachable!("Shouldn't reach this");
// self.puts.remove(&key);
}
// dbg!(&key);
if !self.dels.insert(key) {
if !self.dels.insert(key.clone()) {
dbg!(key, &self.meta.path());
unreachable!();
}
}
+8
View File
@@ -46,6 +46,10 @@ impl StoreMeta {
self.len() == 0
}
pub fn version(&self) -> Version {
self.version
}
pub fn export(&mut self, len: usize, height: Height) -> io::Result<()> {
self.len = len;
self.write_length()?;
@@ -61,6 +65,10 @@ impl StoreMeta {
fs::create_dir(path)
}
pub fn path(&self) -> &Path {
&self.pathbuf
}
fn path_version(&self) -> PathBuf {
Self::path_version_(&self.pathbuf)
}
+6 -2
View File
@@ -46,8 +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 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 -12
View File
@@ -40,9 +40,9 @@ pub struct Vecs {
pub height_to_first_txinindex: IndexedVec<Height, Txinindex>,
pub height_to_first_txoutindex: IndexedVec<Height, Txoutindex>,
pub height_to_first_unknownindex: IndexedVec<Height, Unknownindex>,
pub height_to_block_size: IndexedVec<Height, StoredUsize>,
pub height_to_total_size: IndexedVec<Height, StoredUsize>,
pub height_to_timestamp: IndexedVec<Height, Timestamp>,
pub height_to_block_weight: IndexedVec<Height, Weight>,
pub height_to_weight: IndexedVec<Height, Weight>,
pub multisigindex_to_height: IndexedVec<Multisigindex, Height>,
pub opreturnindex_to_height: IndexedVec<Opreturnindex, Height>,
pub p2pk33index_to_height: IndexedVec<P2PK33index, Height>,
@@ -188,8 +188,8 @@ impl Vecs {
Version::ZERO,
compressed,
)?,
height_to_block_size: IndexedVec::forced_import(
&path.join("height_to_block_size"),
height_to_total_size: IndexedVec::forced_import(
&path.join("height_to_total_size"),
Version::ZERO,
compressed,
)?,
@@ -198,8 +198,8 @@ impl Vecs {
Version::ZERO,
compressed,
)?,
height_to_block_weight: IndexedVec::forced_import(
&path.join("height_to_block_weight"),
height_to_weight: IndexedVec::forced_import(
&path.join("height_to_weight"),
Version::ZERO,
compressed,
)?,
@@ -431,11 +431,11 @@ impl Vecs {
.truncate_if_needed(height, saved_height)?;
self.height_to_difficulty
.truncate_if_needed(height, saved_height)?;
self.height_to_block_size
self.height_to_total_size
.truncate_if_needed(height, saved_height)?;
self.height_to_timestamp
.truncate_if_needed(height, saved_height)?;
self.height_to_block_weight
self.height_to_weight
.truncate_if_needed(height, saved_height)?;
self.addressindex_to_addresstype
@@ -631,9 +631,9 @@ impl Vecs {
self.height_to_first_p2trindex.any_vec(),
self.height_to_first_p2wpkhindex.any_vec(),
self.height_to_first_p2wshindex.any_vec(),
self.height_to_block_size.any_vec(),
self.height_to_total_size.any_vec(),
self.height_to_timestamp.any_vec(),
self.height_to_block_weight.any_vec(),
self.height_to_weight.any_vec(),
self.p2pk33index_to_p2pk33addressbytes.any_vec(),
self.p2pk65index_to_p2pk65addressbytes.any_vec(),
self.p2pkhindex_to_p2pkhaddressbytes.any_vec(),
@@ -693,9 +693,9 @@ impl Vecs {
&mut self.height_to_first_p2trindex,
&mut self.height_to_first_p2wpkhindex,
&mut self.height_to_first_p2wshindex,
&mut self.height_to_block_size,
&mut self.height_to_total_size,
&mut self.height_to_timestamp,
&mut self.height_to_block_weight,
&mut self.height_to_weight,
&mut self.p2pk33index_to_p2pk33addressbytes,
&mut self.p2pk65index_to_p2pk65addressbytes,
&mut self.p2pkhindex_to_p2pkhaddressbytes,
+1 -1
View File
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk_logger">
<img src="https://deps.rs/crate/brk_logger/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
+13 -7
View File
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk_parser">
<img src="https://deps.rs/crate/brk_parser/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
@@ -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
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk_query">
<img src="https://deps.rs/crate/brk_query/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
+1 -1
View File
@@ -21,7 +21,7 @@ color-eyre = { workspace = true }
jiff = { workspace = true }
log = { workspace = true }
minreq = { workspace = true }
oxc = { version = "0.62.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
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk_server">
<img src="https://deps.rs/crate/brk_server/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
+3 -3
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();
@@ -67,7 +67,7 @@ impl DTS for Query<'static> {
.collect::<Vec<_>>()
.join("\n");
contents += "\n\n return {\n";
contents += "\n\n return /** @type {const} */ ({\n";
self.vec_trees
.id_to_index_to_vec
@@ -89,7 +89,7 @@ impl DTS for Query<'static> {
);
});
contents += " }\n";
contents += " });\n";
contents.push('}');
contents += "\n/** @typedef {ReturnType<typeof createVecIdToIndexes>} VecIdToIndexes */";
+1 -1
View File
@@ -20,7 +20,7 @@
<a href="https://deps.rs/crate/brk_vec">
<img src="https://deps.rs/crate/brk_vec/latest/status.svg" alt="Dependency status">
</a>
<a href="https://discord.gg/Cvrwpv3zEG">
<a href="https://discord.gg/HaR3wpH3nr">
<img src="https://img.shields.io/discord/1350431684562124850?label=discord" alt="Discord" />
</a>
<a href="https://primal.net/p/nprofile1qqsfw5dacngjlahye34krvgz7u0yghhjgk7gxzl5ptm9v6n2y3sn03sqxu2e6">
+8 -8
View File
@@ -122,6 +122,14 @@ where
StoredVec::Compressed(v) => v.mut_pushed(),
}
}
#[inline]
fn path(&self) -> &Path {
match self {
StoredVec::Raw(v) => v.path(),
StoredVec::Compressed(v) => v.path(),
}
}
}
impl<I, T> GenericVec<I, T> for StoredVec<I, T>
@@ -160,14 +168,6 @@ where
}
}
#[inline]
fn path(&self) -> &Path {
match self {
StoredVec::Raw(v) => v.path(),
StoredVec::Compressed(v) => v.path(),
}
}
#[inline]
fn version(&self) -> Version {
match self {
+12 -2
View File
@@ -1,4 +1,4 @@
use std::sync::Arc;
use std::{path::Path, sync::Arc};
use arc_swap::{ArcSwap, Guard};
use memmap2::Mmap;
@@ -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>>> {
@@ -113,4 +121,6 @@ pub trait DynamicVec: Send + Sync {
Err(Error::IndexTooLow)
}
}
fn path(&self) -> &Path;
}
+1 -4
View File
@@ -61,8 +61,7 @@ where
#[inline]
fn reset(&mut self) -> Result<()> {
self.file_write_all(&[])?;
Ok(())
self.file_truncate_and_write_all(0, &[])
}
fn new_mmap(file: File) -> Result<Arc<Mmap>> {
@@ -174,8 +173,6 @@ where
Ok(self.collect_range_axum_json(from, to)?.into_response())
}
fn path(&self) -> &Path;
#[inline]
fn path_vec(&self) -> PathBuf {
Self::path_vec_(self.path())
+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)
}
+16 -7
View File
@@ -278,6 +278,11 @@ where
fn mut_pushed(&mut self) -> &mut Vec<T> {
self.inner.mut_pushed()
}
#[inline]
fn path(&self) -> &Path {
self.inner.path()
}
}
impl<I, T> GenericVec<I, T> for CompressedVec<I, T>
@@ -441,6 +446,15 @@ where
Ok(())
}
fn reset(&mut self) -> Result<()> {
let mut pages_meta = (**self.pages_meta.load()).clone();
pages_meta.truncate(0);
pages_meta.write()?;
self.pages_meta.store(Arc::new(pages_meta));
self.reset_caches();
self.file_truncate_and_write_all(0, &[])
}
fn truncate_if_needed(&mut self, index: I) -> Result<()> {
let index = index.to_usize()?;
@@ -453,12 +467,12 @@ where
return Ok(());
}
let mut pages_meta = (**self.pages_meta.load()).clone();
let page_index = Self::index_to_page_index(index);
let guard = self.guard().as_ref().unwrap();
let mut pages_meta = (**self.pages_meta.load()).clone();
let values = self.decode_page(page_index, guard)?;
let mut buf = vec![];
@@ -490,11 +504,6 @@ where
Ok(())
}
#[inline]
fn path(&self) -> &Path {
self.inner.path()
}
#[inline]
fn version(&self) -> Version {
self.inner.version()
+5 -5
View File
@@ -114,6 +114,11 @@ where
fn mut_pushed(&mut self) -> &mut Vec<T> {
&mut self.pushed
}
#[inline]
fn path(&self) -> &Path {
self.pathbuf.as_path()
}
}
impl<I, T> GenericVec<I, T> for RawVec<I, T>
@@ -211,11 +216,6 @@ where
Ok(())
}
#[inline]
fn path(&self) -> &Path {
self.pathbuf.as_path()
}
#[inline]
fn version(&self) -> Version {
self.version
+33 -13
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;
@@ -877,13 +878,14 @@
background-image: linear-gradient(
to bottom,
transparent,
var(--background-color) 90%,
var(--background-color)
);
height: 24rem;
height: 21rem;
bottom: 0;
left: 0;
right: 0;
z-index: 10;
z-index: 20;
pointer-events: none;
}
.shadow-left {
@@ -978,7 +980,6 @@
display: flex;
flex-direction: column;
min-height: 0;
z-index: 20;
flex: 1;
margin-top: 2rem;
margin-bottom: 1rem;
@@ -1050,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;
}
}
@@ -1586,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",
@@ -57,9 +60,6 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
vertLines: { visible: false },
horzLines: { visible: false },
},
timeScale: {
minBarSpacing: 2.1,
},
localization: {
priceFormatter: utils.locale.numberToShortUSFormat,
locale: "en-us",
@@ -101,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: {
@@ -127,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]
@@ -137,6 +148,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
signals,
colors,
utils,
elements,
id,
vecsResources,
owner: _owner,
@@ -274,6 +286,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
signals,
colors,
utils,
elements,
});
if (fitContentOnResize) {
@@ -294,6 +307,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,
@@ -302,13 +316,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),
{
@@ -354,14 +369,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;
@@ -375,6 +387,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,
@@ -384,12 +397,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),
@@ -398,6 +412,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
visible: defaultActive !== false,
priceLineVisible: false,
color: color(),
...options,
},
paneIndex,
);
@@ -437,14 +452,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;
@@ -523,18 +535,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
@@ -897,106 +990,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 === "US Dollars" && 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?.());
});
+220 -106
View File
@@ -38,95 +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_,
signals.createEffect(topSeriesType, (topSeriesType) => {
const bottomUnits = /** @type {readonly Unit[]} */ (
Object.keys(option.bottom)
);
const { field: bottomUnitField, selected: bottomUnit } =
utils.dom.createHorizontalChoiceField({
defaultValue: bottomUnits.at(0) || "",
keyPrefix: "charts",
key: "unit-1",
choices: bottomUnits,
signals,
sorted: true,
});
} else {
unknownTimeScaleCallback();
}
},
});
const candles = chart.addCandlestickSeries({
vecId: "ohlc",
name: "Price",
unit: "US Dollars",
});
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 });
});
signals.createEffect(bottomUnit, (bottomUnit) => {
chart.reset({ owner: signals.getOwner() });
[
{ blueprints: option.top, paneIndex: 0 },
{ blueprints: option.bottom, paneIndex: 1 },
].forEach(({ blueprints, paneIndex }) => {
blueprints?.forEach((blueprint) => {
if (vecIdToIndexes[blueprint.key].includes(index)) {
chart.addLineSeries({
vecId: blueprint.key,
color: blueprint.color,
name: blueprint.title,
unit: option.unit,
defaultActive: blueprint.defaultActive,
paneIndex,
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;
});
});
}
@@ -138,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 {Index} */ () => {
switch (serializedIndex()) {
/** @returns {ChartableIndex} */ () => {
switch (selected()) {
case "timestamp":
return /** @satisfies {Height} */ (0);
case "date":
+282 -220
View File
@@ -1,15 +1,46 @@
// @ts-check
/**
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, Unit, AnySeriesBlueprint } 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: "US Dollars",
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 */
@@ -767,13 +767,13 @@ function createUtils() {
return numberToUSFormat(value, 0);
} else if (absoluteValue < 1_000_000) {
return `${numberToUSFormat(value / 1_000, 1)}K`;
} else if (absoluteValue >= 9_000_000_000_000_000) {
} else if (absoluteValue >= 900_000_000_000_000_000) {
return "Inf.";
}
const log = Math.floor(Math.log10(absoluteValue) - 6);
const suffices = ["M", "B", "T", "Q"];
const suffices = ["M", "B", "T", "P", "E"];
const letterIndex = Math.floor(log / 3);
const letter = suffices[letterIndex];
@@ -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
@@ -1216,9 +1230,10 @@ function createUtils() {
/**
* @param {Index} index
* @param {VecId} vecId
* @param {number} from
*/
genUrl(index, vecId) {
return `/api${genPath(index, vecId)}`;
genUrl(index, vecId, from) {
return `/api${genPath(index, vecId, from)}`;
},
/**
* @template {number | OHLCTuple} [T=number]
@@ -1260,6 +1275,7 @@ function createUtils() {
runWhenIdle,
getNumberOfDaysBetweenTwoDates,
stringToId,
vecidToUnit,
};
}
/** @typedef {ReturnType<typeof createUtils>} Utilities */
@@ -1284,10 +1300,21 @@ function createVecsResources(signals, utils) {
let loading = false;
let at = /** @type {Date | null} */ (null);
const from = -10_000;
return {
url: utils.api.genUrl(index, id),
url: utils.api.genUrl(index, id, from),
fetched,
async fetch() {
/**
* Defaults
* - from: -10_000
* - to: undefined
*
* @param {Object} [args]
* @param {number} [args.from]
* @param {number} [args.to]
*/
async fetch(args) {
if (loading) return fetched();
if (at) {
const diff = new Date().getTime() - at.getTime();
@@ -1302,7 +1329,8 @@ function createVecsResources(signals, utils) {
},
index,
id,
-10_000,
args?.from ?? from,
args?.to,
)
);
at = new Date();
@@ -1395,6 +1423,7 @@ function getElements() {
selectors: getElementById("frame-selectors"),
style: getComputedStyle(window.document.documentElement),
charts: getElementById("charts"),
table: getElementById("table"),
simulation: getElementById("simulation"),
};
}
@@ -1880,6 +1909,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)",
@@ -1963,7 +1998,8 @@ function main() {
undefined
);
let firstTimeLoadingChart = true;
let firstTimeLoadingSim = true;
let firstTimeLoadingTable = true;
let firstTimeLoadingSimulation = true;
signals.createEffect(options.selected, (option) => {
if (previousElement) {
@@ -1996,7 +2032,9 @@ function main() {
colors,
elements,
lightweightCharts,
selected: /** @type {any} */ (lastChartOption),
selected: /** @type {Accessor<ChartOption>} */ (
lastChartOption
),
signals,
utils,
webSockets,
@@ -2012,15 +2050,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",
() =>
@@ -2040,7 +2102,7 @@ function main() {
),
);
}
firstTimeLoadingSim = false;
firstTimeLoadingSimulation = false;
break;
}
File diff suppressed because it is too large Load Diff
+178 -21
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,9 +706,10 @@ export function init({
fitContentOnResize: true,
vecsResources,
utils,
elements,
config: [
{
unit: "US Dollars",
unit: "USD",
blueprints: [
{
title: "Bitcoin Value",
@@ -597,10 +749,11 @@ export function init({
id: `bitcoin`,
fitContentOnResize: true,
vecsResources,
elements,
utils,
config: [
{
unit: "Bitcoin",
unit: "BTC",
blueprints: [
{
title: "Bitcoin Stack",
@@ -621,9 +774,10 @@ export function init({
fitContentOnResize: true,
vecsResources,
utils,
elements,
config: [
{
unit: "US Dollars",
unit: "USD",
blueprints: [
{
title: "Bitcoin Price",
@@ -650,9 +804,11 @@ export function init({
id: `return-ratio`,
fitContentOnResize: true,
utils,
elements,
config: [
{
unit: "US Dollars",
unit: "USD",
blueprints: [
{
title: "Return Of Investment",
@@ -672,10 +828,11 @@ export function init({
fitContentOnResize: true,
vecsResources,
utils,
elements,
owner,
config: [
{
unit: "Percentage",
unit: "%",
blueprints: [
{
title: "Profitable Days Ratio",
+523
View File
@@ -0,0 +1,523 @@
// @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 strip = 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 || (() => {}),
});
strip.append(unit);
strip.append(moveLeft);
strip.append(moveRight);
strip.append(remove);
div.append(strip);
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 });
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, (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 "";
}
}
+185 -14
View File
@@ -1,5 +1,5 @@
//
// File auto-generated, any modification will be overwritten
// File auto-generated, any modifications will be overwritten
//
/** @typedef {0} Height */
@@ -57,14 +57,13 @@ export function createVecIdToIndexes() {
const Pushonlyindex = /** @satisfies {Pushonlyindex} */ (23);
const Unknownindex = /** @satisfies {Unknownindex} */ (24);
return {
addressindex: [Txoutindex],
return /** @type {const} */ ({
addressindex: [Addressindex, Txoutindex],
addresstype: [Addressindex],
addresstypeindex: [Addressindex],
"base-size": [Txindex],
"block-count": [Height],
"block-count-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"block-interval": [Height],
"block-interval-10p": [Dateindex],
"block-interval-25p": [Dateindex],
"block-interval-75p": [Dateindex],
@@ -73,20 +72,88 @@ export function createVecIdToIndexes() {
"block-interval-max": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"block-interval-median": [Dateindex],
"block-interval-min": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"block-size": [Height],
"block-size-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"block-vbytes": [Height],
"block-vbytes-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"block-weight": [Height],
"block-weight-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
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],
"coinbase-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
date: [Dateindex],
dateindex: [Dateindex, Height],
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],
"fee-sum": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
feerate: [Txindex],
"feerate-10p": [Height],
"feerate-25p": [Height],
"feerate-75p": [Height],
"feerate-90p": [Height],
"feerate-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"feerate-max": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"feerate-median": [Height],
"feerate-min": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"first-addressindex": [Height],
"first-dateindex": [Weekindex, Monthindex],
"first-emptyindex": [Height],
@@ -113,8 +180,21 @@ 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],
"input-count-75p": [Height],
"input-count-90p": [Height],
"input-count-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"input-count-max": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"input-count-median": [Height],
"input-count-min": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"input-count-sum": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"input-value": [Txindex],
"input-value-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"input-value-sum": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
interval: [Height],
"is-coinbase": [Txindex],
"is-explicitly-rbf": [Txindex],
"last-dateindex": [Weekindex, Monthindex],
@@ -127,36 +207,108 @@ 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],
"output-count-75p": [Height],
"output-count-90p": [Height],
"output-count-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"output-count-max": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"output-count-median": [Height],
"output-count-min": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"output-count-sum": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"output-value": [Txindex],
"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],
"subsidy-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
timestamp: [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch, Halvingepoch],
"total-block-count": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"total-block-size": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"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-size": [Txindex],
"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],
"total-tx-v3": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"tx-count": [Height],
"tx-count-10p": [Dateindex],
"tx-count-25p": [Dateindex],
"tx-count-75p": [Dateindex],
"tx-count-90p": [Dateindex],
"tx-count-average": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"tx-count-max": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"tx-count-median": [Dateindex],
"tx-count-min": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"tx-count-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"tx-v1": [Height],
"tx-v1-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
@@ -164,16 +316,35 @@ export function createVecIdToIndexes() {
"tx-v2-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"tx-v3": [Height],
"tx-v3-sum": [Dateindex, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"tx-vsize-10p": [Height],
"tx-vsize-25p": [Height],
"tx-vsize-75p": [Height],
"tx-vsize-90p": [Height],
"tx-vsize-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"tx-vsize-max": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"tx-vsize-median": [Height],
"tx-vsize-min": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"tx-weight-10p": [Height],
"tx-weight-25p": [Height],
"tx-weight-75p": [Height],
"tx-weight-90p": [Height],
"tx-weight-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"tx-weight-max": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"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],
v1: [Txindex],
v2: [Txindex],
v3: [Txindex],
unknownindex: [Unknownindex],
value: [Txinindex, Txoutindex],
vbytes: [Height],
vsize: [Txindex],
weekindex: [Dateindex, Weekindex],
weight: [Height, Txindex],
yearindex: [Monthindex, Yearindex],
}
});
}
/** @typedef {ReturnType<typeof createVecIdToIndexes>} VecIdToIndexes */
/** @typedef {keyof VecIdToIndexes} VecId */
+4
View File
@@ -27,4 +27,8 @@
.chart {
flex: 1;
}
> * {
z-index: 30;
}
}
+130
View File
@@ -0,0 +1,130 @@
#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;
}
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;
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;
}
}
}
}