From 9576f6e91e2567ee782cd808fc9d4e37d47dd4cc Mon Sep 17 00:00:00 2001 From: nym21 Date: Fri, 13 Dec 2024 12:30:56 +0100 Subject: [PATCH 01/63] website: fix window resize brave bug --- website/scripts/main.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/website/scripts/main.js b/website/scripts/main.js index 0dd2cdda8..9e3f3d7b7 100644 --- a/website/scripts/main.js +++ b/website/scripts/main.js @@ -1310,10 +1310,11 @@ function initFrameSelectors() { function setAsideParent() { const { clientWidth } = window.document.documentElement; - if (clientWidth >= consts.MEDIUM_WIDTH) { - elements.body.append(elements.aside); - } else { - elements.main.append(elements.aside); + const { aside, body, main } = elements; + if (clientWidth >= consts.MEDIUM_WIDTH && aside.parentElement !== body) { + body.append(aside); + } else if (aside.parentElement !== main) { + main.append(aside); } } From f6f4660cd22c4b88e78c129f53288678e0dc347d Mon Sep 17 00:00:00 2001 From: nym21 Date: Fri, 13 Dec 2024 16:08:59 +0100 Subject: [PATCH 02/63] website: fix the previous fix --- website/scripts/main.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/website/scripts/main.js b/website/scripts/main.js index 9e3f3d7b7..17edce5b5 100644 --- a/website/scripts/main.js +++ b/website/scripts/main.js @@ -1311,10 +1311,10 @@ function initFrameSelectors() { function setAsideParent() { const { clientWidth } = window.document.documentElement; const { aside, body, main } = elements; - if (clientWidth >= consts.MEDIUM_WIDTH && aside.parentElement !== body) { - body.append(aside); - } else if (aside.parentElement !== main) { - main.append(aside); + if (clientWidth >= consts.MEDIUM_WIDTH) { + aside.parentElement !== body && body.append(aside); + } else { + aside.parentElement !== main && main.append(aside); } } From 795791219e15e92e53c11e49158ad3ce6c2b168d Mon Sep 17 00:00:00 2001 From: nym21 Date: Fri, 13 Dec 2024 19:55:32 +0100 Subject: [PATCH 03/63] global: snapshot --- .gitignore | 28 +- CHANGELOG.md | 69 +- CONTRIBUTING.md | 4 +- server/Cargo.lock => Cargo.lock | 556 ++-- Cargo.toml | 39 + README.md | 3 +- crates/biter/CHANGELOG.md | 12 + {biter => crates/biter}/Cargo.lock | 79 +- {biter => crates/biter}/Cargo.toml | 10 +- {biter => crates/biter}/LICENSE.md | 0 {biter => crates/biter}/README.md | 23 +- .../biter}/src/blk_index_to_blk_recap.rs | 40 +- {biter => crates/biter}/src/blk_metadata.rs | 0 .../biter}/src/blk_metadata_and_block.rs | 0 {biter => crates/biter}/src/blk_recap.rs | 0 {biter => crates/biter}/src/lib.rs | 21 +- {biter => crates/biter}/src/main.rs | 14 +- {biter => crates/biter}/src/utils.rs | 13 +- {iterable => crates/iterable}/Cargo.lock | 0 {iterable => crates/iterable}/Cargo.toml | 0 {iterable => crates/iterable}/README.md | 0 {iterable => crates/iterable}/src/lib.rs | 0 .../struct_iterable_derive/Cargo.toml | 0 .../struct_iterable_derive/README.md | 0 .../struct_iterable_derive/src/lib.rs | 0 .../struct_iterable_internal/Cargo.toml | 0 .../struct_iterable_internal/README.md | 0 .../struct_iterable_internal/src/lib.rs | 0 parser/Cargo.lock | 2430 ----------------- parser/Cargo.toml | 32 - parser/reset.sh | 7 - parser/samply.sh | 10 - parser/src/databases/_trait.rs | 52 - parser/src/io/consts.rs | 2 - parser/src/lib.rs | 19 - parser/src/main.rs | 36 - parser/src/states/_trait.rs | 48 - parser/src/utils/log.rs | 34 - parser/run.sh => run.sh | 2 +- server/Cargo.toml | 22 - server/restart-cloudflared.sh | 4 - server/run.sh | 14 - server/src/api/structs/routes.rs | 157 -- {parser/src => src}/io/binary.rs | 0 {parser/src => src}/io/json.rs | 0 {parser/src => src}/io/mod.rs | 2 - {parser/src => src}/io/serialization.rs | 31 +- src/main.rs | 70 + {parser/src => src/parser}/actions/export.rs | 40 +- .../src => src/parser}/actions/iter_blocks.rs | 93 +- .../src => src/parser}/actions/min_height.rs | 28 +- {parser/src => src/parser}/actions/mod.rs | 0 {parser/src => src/parser}/actions/parse.rs | 29 +- .../src => src/parser}/databases/_database.rs | 97 +- src/parser/databases/_trait.rs | 59 + .../address_index_to_address_data.rs | 39 +- .../address_index_to_empty_address_data.rs | 37 +- .../databases/address_to_address_index.rs | 91 +- .../src => src/parser}/databases/metadata.rs | 8 +- {parser/src => src/parser}/databases/mod.rs | 81 +- .../parser}/databases/txid_to_tx_data.rs | 34 +- .../databases/txout_index_to_address_index.rs | 35 +- .../databases/txout_index_to_amount.rs | 35 +- .../parser}/datasets/_traits/any_dataset.rs | 6 +- .../datasets/_traits/any_dataset_group.rs | 0 .../parser}/datasets/_traits/any_datasets.rs | 0 .../datasets/_traits/min_initial_state.rs | 0 .../parser}/datasets/_traits/mod.rs | 0 .../parser}/datasets/address/all_metadata.rs | 8 +- .../parser}/datasets/address/cohort.rs | 14 +- .../datasets/address/cohort_metadata.rs | 10 +- .../parser}/datasets/address/mod.rs | 13 +- .../parser}/datasets/block_metadata.rs | 6 +- .../src => src/parser}/datasets/coindays.rs | 9 +- .../src => src/parser}/datasets/cointime.rs | 20 +- .../src => src/parser}/datasets/constant.rs | 4 +- .../parser}/datasets/date_metadata.rs | 6 +- {parser/src => src/parser}/datasets/mining.rs | 6 +- {parser/src => src/parser}/datasets/mod.rs | 126 +- {parser/src => src/parser}/datasets/price.rs | 43 +- .../parser}/datasets/subs/capitalization.rs | 16 +- .../src => src/parser}/datasets/subs/input.rs | 15 +- .../src => src/parser}/datasets/subs/mod.rs | 7 +- .../parser}/datasets/subs/output.rs | 0 .../parser}/datasets/subs/price_paid.rs | 14 +- .../src => src/parser}/datasets/subs/ratio.rs | 10 +- .../parser}/datasets/subs/realized.rs | 16 +- .../src => src/parser}/datasets/subs/recap.rs | 10 +- .../parser}/datasets/subs/supply.rs | 14 +- .../parser}/datasets/subs/unrealized.rs | 14 +- .../src => src/parser}/datasets/subs/utxo.rs | 14 +- .../parser}/datasets/transaction.rs | 9 +- .../parser}/datasets/utxo/dataset.rs | 10 +- .../src => src/parser}/datasets/utxo/mod.rs | 10 +- src/parser/mod.rs | 52 + {parser/src => src/parser}/price/binance.rs | 23 +- {parser/src => src/parser}/price/kibo.rs | 10 +- {parser/src => src/parser}/price/kraken.rs | 7 +- {parser/src => src/parser}/price/mod.rs | 0 src/parser/states/_trait.rs | 39 + .../address/cohort_durable_states.rs | 2 +- .../cohorts_states/address/cohort_id.rs | 0 .../address/cohorts_durable_states.rs | 2 +- .../address/cohorts_input_states.rs | 2 +- .../address/cohorts_one_shot_states.rs | 2 +- .../address/cohorts_output_states.rs | 2 +- .../address/cohorts_realized_states.rs | 2 +- .../states/cohorts_states/address/mod.rs | 0 .../address/split_by_address_cohort.rs | 0 .../any/capitalization_state.rs | 0 .../cohorts_states/any/durable_states.rs | 0 .../states/cohorts_states/any/input_state.rs | 0 .../parser}/states/cohorts_states/any/mod.rs | 0 .../cohorts_states/any/one_shot_states.rs | 0 .../states/cohorts_states/any/output_state.rs | 0 .../cohorts_states/any/price_paid_state.rs | 0 .../cohorts_states/any/price_to_value.rs | 0 .../cohorts_states/any/realized_state.rs | 0 .../states/cohorts_states/any/supply_state.rs | 0 .../cohorts_states/any/unrealized_state.rs | 0 .../states/cohorts_states/any/utxo_state.rs | 0 .../parser}/states/cohorts_states/mod.rs | 0 .../utxo/cohort_durable_states.rs | 2 +- .../cohorts_states/utxo/cohort_filter.rs | 0 .../cohorts_states/utxo/cohort_filters.rs | 0 .../states/cohorts_states/utxo/cohort_id.rs | 0 .../utxo/cohorts_durable_states.rs | 2 +- .../utxo/cohorts_one_shot_states.rs | 2 +- .../utxo/cohorts_sent_states.rs | 2 +- .../parser}/states/cohorts_states/utxo/mod.rs | 0 .../utxo/split_by_utxo_cohort.rs | 0 {parser/src => src/parser}/states/counters.rs | 0 .../parser}/states/date_data_vec.rs | 0 {parser/src => src/parser}/states/mod.rs | 25 +- .../server}/api/handlers/dataset.rs | 70 +- .../server}/api/handlers/extension.rs | 0 .../server}/api/handlers/fallback.rs | 2 +- .../src => src/server}/api/handlers/mod.rs | 0 .../server}/api/handlers/response.rs | 8 +- {server/src => src/server}/api/mod.rs | 10 +- .../src => src/server}/api/structs/chunk.rs | 0 .../src => src/server}/api/structs/kind.rs | 0 {server/src => src/server}/api/structs/mod.rs | 0 .../src => src/server}/api/structs/paths.rs | 2 +- src/server/api/structs/routes.rs | 159 ++ {server/src => src/server}/header_map.rs | 4 +- server/src/main.rs => src/server/mod.rs | 34 +- .../server}/website/handlers/_minify.rs | 0 .../server}/website/handlers/file.rs | 95 +- .../server}/website/handlers/minify.rs | 0 .../server}/website/handlers/mod.rs | 0 {server/src => src/server}/website/mod.rs | 2 +- {parser/src => src}/structs/address.rs | 4 +- {parser/src => src}/structs/address_data.rs | 0 .../src => src}/structs/address_liquidity.rs | 0 .../structs/address_realized_data.rs | 0 {parser/src => src}/structs/address_size.rs | 0 {parser/src => src}/structs/address_split.rs | 0 {parser/src => src}/structs/address_type.rs | 0 {parser/src => src}/structs/amount.rs | 0 {parser/src => src}/structs/any_map.rs | 15 +- {parser/src => src}/structs/array.rs | 0 {parser/src => src}/structs/bi_map.rs | 6 +- {parser/src => src}/structs/block_data.rs | 0 {parser/src => src}/structs/block_path.rs | 0 {parser/src => src}/structs/config.rs | 168 +- {parser/src => src}/structs/counter.rs | 0 {parser/src => src}/structs/date.rs | 16 +- {parser/src => src}/structs/date_data.rs | 0 {parser/src => src}/structs/date_map.rs | 6 +- .../src => src}/structs/date_map_chunk_id.rs | 4 +- .../src => src}/structs/empty_address_data.rs | 0 {parser/src => src}/structs/epoch.rs | 0 {parser/src => src}/structs/exit.rs | 6 +- {parser/src => src}/structs/generic_map.rs | 72 +- {parser/src => src}/structs/height.rs | 12 +- {parser/src => src}/structs/height_map.rs | 4 +- .../structs/height_map_chunk_id.rs | 4 +- {parser/src => src}/structs/liquidity.rs | 0 src/structs/map_path.rs | 24 + {parser/src => src}/structs/map_value.rs | 0 {parser/src => src}/structs/mod.rs | 2 + {parser/src => src}/structs/ohlc.rs | 0 .../src => src}/structs/partial_txout_data.rs | 0 {parser/src => src}/structs/price.rs | 0 {parser/src => src}/structs/ram.rs | 0 {parser/src => src}/structs/sent_data.rs | 0 .../structs/serialized_btreemap.rs | 2 +- {parser/src => src}/structs/serialized_vec.rs | 2 +- {parser/src => src}/structs/timestamp.rs | 6 +- {parser/src => src}/structs/tx_data.rs | 0 {parser/src => src}/structs/txout_index.rs | 0 {parser/src => src}/utils/consts.rs | 0 {parser/src => src}/utils/flamegraph.rs | 9 +- src/utils/log.rs | 82 + {parser/src => src}/utils/lossy.rs | 0 {parser/src => src}/utils/mod.rs | 1 - {parser/src => src}/utils/percentile.rs | 0 {parser/src => src}/utils/retry.rs | 0 {parser/src => src}/utils/rpc.rs | 10 +- {parser/src => src}/utils/time.rs | 4 +- .../fonts/satoshi/2024-09/font.var.woff2 | Bin .../website}/assets/fonts/satoshi/FFL.txt | 0 .../assets/pdfs/block/2022-report.pdf | Bin .../pdfs/braiins/building-bitcoin-in-rust.pdf | Bin .../pdfs/glassnode/cointime-economics.pdf | Bin .../assets/pdfs/multi-author/bcap_v1.0.pdf | Bin ...-bitcoin-adoption-in-the-united-states.pdf | Bin .../protection-under-first-amendment.pdf | Bin .../pdfs/satoshi-nakamoto/whitepaper.pdf | Bin .../2021-bitcoin-clean-energy-initiative.pdf | Bin .../2024-11-20_09-41-25/apple-icon-180.png | Bin .../apple-splash-1125-2436.jpg | Bin .../apple-splash-1136-640.jpg | Bin .../apple-splash-1170-2532.jpg | Bin .../apple-splash-1179-2556.jpg | Bin .../apple-splash-1242-2208.jpg | Bin .../apple-splash-1242-2688.jpg | Bin .../apple-splash-1284-2778.jpg | Bin .../apple-splash-1290-2796.jpg | Bin .../apple-splash-1334-750.jpg | Bin .../apple-splash-1488-2266.jpg | Bin .../apple-splash-1536-2048.jpg | Bin .../apple-splash-1620-2160.jpg | Bin .../apple-splash-1640-2360.jpg | Bin .../apple-splash-1668-2224.jpg | Bin .../apple-splash-1668-2388.jpg | Bin .../apple-splash-1792-828.jpg | Bin .../apple-splash-2048-1536.jpg | Bin .../apple-splash-2048-2732.jpg | Bin .../apple-splash-2160-1620.jpg | Bin .../apple-splash-2208-1242.jpg | Bin .../apple-splash-2224-1668.jpg | Bin .../apple-splash-2266-1488.jpg | Bin .../apple-splash-2360-1640.jpg | Bin .../apple-splash-2388-1668.jpg | Bin .../apple-splash-2436-1125.jpg | Bin .../apple-splash-2532-1170.jpg | Bin .../apple-splash-2556-1179.jpg | Bin .../apple-splash-2688-1242.jpg | Bin .../apple-splash-2732-2048.jpg | Bin .../apple-splash-2778-1284.jpg | Bin .../apple-splash-2796-1290.jpg | Bin .../apple-splash-640-1136.jpg | Bin .../apple-splash-750-1334.jpg | Bin .../apple-splash-828-1792.jpg | Bin .../pwa/2024-11-20_09-41-25/favicon-196.png | Bin .../assets/pwa/2024-11-20_09-41-25/index.html | 0 .../manifest-icon-192.maskable.png | Bin .../manifest-icon-512.maskable.png | Bin .../website}/assets/pwa/index.html | 0 {website => src/website}/generate-icons.sh | 0 {website => src/website}/index.html | 27 +- {website => src/website}/jsconfig.json | 0 {website => src/website}/manifest.webmanifest | 0 .../website}/misc/tailwindTo550.js | 0 .../website}/packages/lean-qr/README.md | 0 .../packages/lean-qr/v2.3.4/script.js | 0 .../packages/lean-qr/v2.3.4/types.d.ts | 0 .../packages/lightweight-charts/NOTICE.md | 0 .../packages/lightweight-charts/README.md | 0 .../packages/lightweight-charts/types.d.ts | 0 .../lightweight-charts/v4.2.0/script.js | 0 .../lightweight-charts/v4.2.0/types.d.ts | 0 .../packages/lightweight-charts/wrapper.js | 0 .../solid-signals/2024-11-02/script.js | 0 .../2024-11-02/types/core/constants.d.ts | 0 .../2024-11-02/types/core/core.d.ts | 0 .../2024-11-02/types/core/effect.d.ts | 0 .../2024-11-02/types/core/error.d.ts | 0 .../2024-11-02/types/core/flags.d.ts | 0 .../2024-11-02/types/core/index.d.ts | 0 .../2024-11-02/types/core/owner.d.ts | 0 .../2024-11-02/types/core/scheduler.d.ts | 0 .../solid-signals/2024-11-02/types/index.d.ts | 0 .../solid-signals/2024-11-02/types/map.d.ts | 0 .../2024-11-02/types/signals.d.ts | 0 .../2024-11-02/types/store/index.d.ts | 0 .../2024-11-02/types/store/store.d.ts | 0 .../solid-signals/2024-11-02/types/utils.d.ts | 0 .../solid-signals/2024-11-08/script.js | 0 .../2024-11-08/types/core/constants.d.ts | 0 .../2024-11-08/types/core/core.d.ts | 0 .../2024-11-08/types/core/effect.d.ts | 0 .../2024-11-08/types/core/error.d.ts | 0 .../2024-11-08/types/core/flags.d.ts | 0 .../2024-11-08/types/core/index.d.ts | 0 .../2024-11-08/types/core/owner.d.ts | 0 .../2024-11-08/types/core/scheduler.d.ts | 0 .../solid-signals/2024-11-08/types/index.d.ts | 0 .../solid-signals/2024-11-08/types/map.d.ts | 0 .../2024-11-08/types/signals.d.ts | 0 .../2024-11-08/types/store/index.d.ts | 0 .../2024-11-08/types/store/store.d.ts | 0 .../solid-signals/2024-11-08/types/utils.d.ts | 0 .../website}/packages/solid-signals/README.md | 0 .../packages/solid-signals/types.d.ts | 0 .../packages/solid-signals/wrapper.js | 0 .../website}/packages/ufuzzy/README.md | 0 .../packages/ufuzzy/v1.0.14/script.js | 0 .../packages/ufuzzy/v1.0.14/types.d.ts | 0 {website => src/website}/robots.txt | 0 {website => src/website}/scripts/chart.js | 0 .../website}/scripts/live-price.js | 0 {website => src/website}/scripts/main.js | 8 +- .../website}/scripts/moscow-time.js | 0 {website => src/website}/scripts/options.js | 0 .../website}/scripts/service-worker.js | 0 .../website}/scripts/simulation.js | 0 .../website}/scripts/types/self.d.ts | 0 {website => src/website}/styles/chart.css | 0 .../website}/styles/live-price.css | 0 .../website}/styles/moscow-time.css | 0 .../website}/styles/simulation.css | 0 {website => src/website}/tsconfig.json | 0 315 files changed, 1931 insertions(+), 4144 deletions(-) rename server/Cargo.lock => Cargo.lock (90%) create mode 100644 Cargo.toml create mode 100644 crates/biter/CHANGELOG.md rename {biter => crates/biter}/Cargo.lock (85%) rename {biter => crates/biter}/Cargo.toml (65%) rename {biter => crates/biter}/LICENSE.md (100%) rename {biter => crates/biter}/README.md (71%) rename {biter => crates/biter}/src/blk_index_to_blk_recap.rs (73%) rename {biter => crates/biter}/src/blk_metadata.rs (100%) rename {biter => crates/biter}/src/blk_metadata_and_block.rs (100%) rename {biter => crates/biter}/src/blk_recap.rs (100%) rename {biter => crates/biter}/src/lib.rs (96%) rename {biter => crates/biter}/src/main.rs (54%) rename {biter => crates/biter}/src/utils.rs (79%) rename {iterable => crates/iterable}/Cargo.lock (100%) rename {iterable => crates/iterable}/Cargo.toml (100%) rename {iterable => crates/iterable}/README.md (100%) rename {iterable => crates/iterable}/src/lib.rs (100%) rename {iterable => crates/iterable}/struct_iterable_derive/Cargo.toml (100%) rename {iterable => crates/iterable}/struct_iterable_derive/README.md (100%) rename {iterable => crates/iterable}/struct_iterable_derive/src/lib.rs (100%) rename {iterable => crates/iterable}/struct_iterable_internal/Cargo.toml (100%) rename {iterable => crates/iterable}/struct_iterable_internal/README.md (100%) rename {iterable => crates/iterable}/struct_iterable_internal/src/lib.rs (100%) delete mode 100644 parser/Cargo.lock delete mode 100644 parser/Cargo.toml delete mode 100755 parser/reset.sh delete mode 100755 parser/samply.sh delete mode 100644 parser/src/databases/_trait.rs delete mode 100644 parser/src/io/consts.rs delete mode 100644 parser/src/lib.rs delete mode 100644 parser/src/main.rs delete mode 100644 parser/src/states/_trait.rs delete mode 100644 parser/src/utils/log.rs rename parser/run.sh => run.sh (89%) delete mode 100644 server/Cargo.toml delete mode 100755 server/restart-cloudflared.sh delete mode 100755 server/run.sh delete mode 100644 server/src/api/structs/routes.rs rename {parser/src => src}/io/binary.rs (100%) rename {parser/src => src}/io/json.rs (100%) rename {parser/src => src}/io/mod.rs (77%) rename {parser/src => src}/io/serialization.rs (89%) create mode 100644 src/main.rs rename {parser/src => src/parser}/actions/export.rs (52%) rename {parser/src => src/parser}/actions/iter_blocks.rs (78%) rename {parser/src => src/parser}/actions/min_height.rs (84%) rename {parser/src => src/parser}/actions/mod.rs (100%) rename {parser/src => src/parser}/actions/parse.rs (98%) rename {parser/src => src/parser}/databases/_database.rs (76%) create mode 100644 src/parser/databases/_trait.rs rename {parser/src => src/parser}/databases/address_index_to_address_data.rs (86%) rename {parser/src => src/parser}/databases/address_index_to_empty_address_data.rs (84%) rename {parser/src => src/parser}/databases/address_to_address_index.rs (86%) rename {parser/src => src/parser}/databases/metadata.rs (94%) rename {parser/src => src/parser}/databases/mod.rs (78%) rename {parser/src => src/parser}/databases/txid_to_tx_data.rs (87%) rename {parser/src => src/parser}/databases/txout_index_to_address_index.rs (85%) rename {parser/src => src/parser}/databases/txout_index_to_amount.rs (84%) rename {parser/src => src/parser}/datasets/_traits/any_dataset.rs (99%) rename {parser/src => src/parser}/datasets/_traits/any_dataset_group.rs (100%) rename {parser/src => src/parser}/datasets/_traits/any_datasets.rs (100%) rename {parser/src => src/parser}/datasets/_traits/min_initial_state.rs (100%) rename {parser/src => src/parser}/datasets/_traits/mod.rs (100%) rename {parser/src => src/parser}/datasets/address/all_metadata.rs (88%) rename {parser/src => src/parser}/datasets/address/cohort.rs (96%) rename {parser/src => src/parser}/datasets/address/cohort_metadata.rs (87%) rename {parser/src => src/parser}/datasets/address/mod.rs (93%) rename {parser/src => src/parser}/datasets/block_metadata.rs (87%) rename {parser/src => src/parser}/datasets/coindays.rs (87%) rename {parser/src => src/parser}/datasets/cointime.rs (97%) rename {parser/src => src/parser}/datasets/constant.rs (90%) rename {parser/src => src/parser}/datasets/date_metadata.rs (88%) rename {parser/src => src/parser}/datasets/mining.rs (99%) rename {parser/src => src/parser}/datasets/mod.rs (75%) rename {parser/src => src/parser}/datasets/price.rs (93%) rename {parser/src => src/parser}/datasets/subs/capitalization.rs (90%) rename {parser/src => src/parser}/datasets/subs/input.rs (87%) rename {parser/src => src/parser}/datasets/subs/mod.rs (95%) rename {parser/src => src/parser}/datasets/subs/output.rs (100%) rename {parser/src => src/parser}/datasets/subs/price_paid.rs (96%) rename {parser/src => src/parser}/datasets/subs/ratio.rs (93%) rename {parser/src => src/parser}/datasets/subs/realized.rs (97%) rename {parser/src => src/parser}/datasets/subs/recap.rs (96%) rename {parser/src => src/parser}/datasets/subs/supply.rs (90%) rename {parser/src => src/parser}/datasets/subs/unrealized.rs (95%) rename {parser/src => src/parser}/datasets/subs/utxo.rs (83%) rename {parser/src => src/parser}/datasets/transaction.rs (97%) rename {parser/src => src/parser}/datasets/utxo/dataset.rs (96%) rename {parser/src => src/parser}/datasets/utxo/mod.rs (94%) create mode 100644 src/parser/mod.rs rename {parser/src => src/parser}/price/binance.rs (92%) rename {parser/src => src/parser}/price/kibo.rs (94%) rename {parser/src => src/parser}/price/kraken.rs (97%) rename {parser/src => src/parser}/price/mod.rs (100%) create mode 100644 src/parser/states/_trait.rs rename {parser/src => src/parser}/states/cohorts_states/address/cohort_durable_states.rs (97%) rename {parser/src => src/parser}/states/cohorts_states/address/cohort_id.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/address/cohorts_durable_states.rs (99%) rename {parser/src => src/parser}/states/cohorts_states/address/cohorts_input_states.rs (97%) rename {parser/src => src/parser}/states/cohorts_states/address/cohorts_one_shot_states.rs (81%) rename {parser/src => src/parser}/states/cohorts_states/address/cohorts_output_states.rs (97%) rename {parser/src => src/parser}/states/cohorts_states/address/cohorts_realized_states.rs (98%) rename {parser/src => src/parser}/states/cohorts_states/address/mod.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/address/split_by_address_cohort.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/capitalization_state.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/durable_states.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/input_state.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/mod.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/one_shot_states.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/output_state.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/price_paid_state.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/price_to_value.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/realized_state.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/supply_state.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/unrealized_state.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/any/utxo_state.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/mod.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/utxo/cohort_durable_states.rs (97%) rename {parser/src => src/parser}/states/cohorts_states/utxo/cohort_filter.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/utxo/cohort_filters.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/utxo/cohort_id.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/utxo/cohorts_durable_states.rs (99%) rename {parser/src => src/parser}/states/cohorts_states/utxo/cohorts_one_shot_states.rs (81%) rename {parser/src => src/parser}/states/cohorts_states/utxo/cohorts_sent_states.rs (98%) rename {parser/src => src/parser}/states/cohorts_states/utxo/mod.rs (100%) rename {parser/src => src/parser}/states/cohorts_states/utxo/split_by_utxo_cohort.rs (100%) rename {parser/src => src/parser}/states/counters.rs (100%) rename {parser/src => src/parser}/states/date_data_vec.rs (100%) rename {parser/src => src/parser}/states/mod.rs (58%) rename {server/src => src/server}/api/handlers/dataset.rs (80%) rename {server/src => src/server}/api/handlers/extension.rs (100%) rename {server/src => src/server}/api/handlers/fallback.rs (94%) rename {server/src => src/server}/api/handlers/mod.rs (100%) rename {server/src => src/server}/api/handlers/response.rs (96%) rename {server/src => src/server}/api/mod.rs (50%) rename {server/src => src/server}/api/structs/chunk.rs (100%) rename {server/src => src/server}/api/structs/kind.rs (100%) rename {server/src => src/server}/api/structs/mod.rs (100%) rename {server/src => src/server}/api/structs/paths.rs (88%) create mode 100644 src/server/api/structs/routes.rs rename {server/src => src/server}/header_map.rs (98%) rename server/src/main.rs => src/server/mod.rs (55%) rename {server/src => src/server}/website/handlers/_minify.rs (100%) rename {server/src => src/server}/website/handlers/file.rs (53%) rename {server/src => src/server}/website/handlers/minify.rs (100%) rename {server/src => src/server}/website/handlers/mod.rs (100%) rename {server/src => src/server}/website/mod.rs (94%) rename {parser/src => src}/structs/address.rs (97%) rename {parser/src => src}/structs/address_data.rs (100%) rename {parser/src => src}/structs/address_liquidity.rs (100%) rename {parser/src => src}/structs/address_realized_data.rs (100%) rename {parser/src => src}/structs/address_size.rs (100%) rename {parser/src => src}/structs/address_split.rs (100%) rename {parser/src => src}/structs/address_type.rs (100%) rename {parser/src => src}/structs/amount.rs (100%) rename {parser/src => src}/structs/any_map.rs (53%) rename {parser/src => src}/structs/array.rs (100%) rename {parser/src => src}/structs/bi_map.rs (98%) rename {parser/src => src}/structs/block_data.rs (100%) rename {parser/src => src}/structs/block_path.rs (100%) rename {parser/src => src}/structs/config.rs (51%) rename {parser/src => src}/structs/counter.rs (100%) rename {parser/src => src}/structs/date.rs (92%) rename {parser/src => src}/structs/date_data.rs (100%) rename {parser/src => src}/structs/date_map.rs (94%) rename {parser/src => src}/structs/date_map_chunk_id.rs (95%) rename {parser/src => src}/structs/empty_address_data.rs (100%) rename {parser/src => src}/structs/epoch.rs (100%) rename {parser/src => src}/structs/exit.rs (92%) rename {parser/src => src}/structs/generic_map.rs (95%) rename {parser/src => src}/structs/height.rs (91%) rename {parser/src => src}/structs/height_map.rs (97%) rename {parser/src => src}/structs/height_map_chunk_id.rs (94%) rename {parser/src => src}/structs/liquidity.rs (100%) create mode 100644 src/structs/map_path.rs rename {parser/src => src}/structs/map_value.rs (100%) rename {parser/src => src}/structs/mod.rs (97%) rename {parser/src => src}/structs/ohlc.rs (100%) rename {parser/src => src}/structs/partial_txout_data.rs (100%) rename {parser/src => src}/structs/price.rs (100%) rename {parser/src => src}/structs/ram.rs (100%) rename {parser/src => src}/structs/sent_data.rs (100%) rename {parser/src => src}/structs/serialized_btreemap.rs (98%) rename {parser/src => src}/structs/serialized_vec.rs (98%) rename {parser/src => src}/structs/timestamp.rs (91%) rename {parser/src => src}/structs/tx_data.rs (100%) rename {parser/src => src}/structs/txout_index.rs (100%) rename {parser/src => src}/utils/consts.rs (100%) rename {parser/src => src}/utils/flamegraph.rs (84%) create mode 100644 src/utils/log.rs rename {parser/src => src}/utils/lossy.rs (100%) rename {parser/src => src}/utils/mod.rs (93%) rename {parser/src => src}/utils/percentile.rs (100%) rename {parser/src => src}/utils/retry.rs (100%) rename {parser/src => src}/utils/rpc.rs (61%) rename {parser/src => src}/utils/time.rs (66%) rename {website => src/website}/assets/fonts/satoshi/2024-09/font.var.woff2 (100%) rename {website => src/website}/assets/fonts/satoshi/FFL.txt (100%) rename {website => src/website}/assets/pdfs/block/2022-report.pdf (100%) rename {website => src/website}/assets/pdfs/braiins/building-bitcoin-in-rust.pdf (100%) rename {website => src/website}/assets/pdfs/glassnode/cointime-economics.pdf (100%) rename {website => src/website}/assets/pdfs/multi-author/bcap_v1.0.pdf (100%) rename {website => src/website}/assets/pdfs/nakamoto-project/understanding-bitcoin-adoption-in-the-united-states.pdf (100%) rename {website => src/website}/assets/pdfs/nydig/protection-under-first-amendment.pdf (100%) rename {website => src/website}/assets/pdfs/satoshi-nakamoto/whitepaper.pdf (100%) rename {website => src/website}/assets/pdfs/square/2021-bitcoin-clean-energy-initiative.pdf (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-icon-180.png (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1125-2436.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1136-640.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1170-2532.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1179-2556.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1242-2208.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1242-2688.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1284-2778.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1290-2796.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1334-750.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1488-2266.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1536-2048.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1620-2160.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1640-2360.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1668-2224.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1668-2388.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-1792-828.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2048-1536.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2048-2732.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2160-1620.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2208-1242.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2224-1668.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2266-1488.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2360-1640.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2388-1668.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2436-1125.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2532-1170.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2556-1179.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2688-1242.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2732-2048.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2778-1284.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-2796-1290.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-640-1136.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-750-1334.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/apple-splash-828-1792.jpg (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/favicon-196.png (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/index.html (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/manifest-icon-192.maskable.png (100%) rename {website => src/website}/assets/pwa/2024-11-20_09-41-25/manifest-icon-512.maskable.png (100%) rename {website => src/website}/assets/pwa/index.html (100%) rename {website => src/website}/generate-icons.sh (100%) rename {website => src/website}/index.html (99%) rename {website => src/website}/jsconfig.json (100%) rename {website => src/website}/manifest.webmanifest (100%) rename {website => src/website}/misc/tailwindTo550.js (100%) rename {website => src/website}/packages/lean-qr/README.md (100%) rename {website => src/website}/packages/lean-qr/v2.3.4/script.js (100%) rename {website => src/website}/packages/lean-qr/v2.3.4/types.d.ts (100%) rename {website => src/website}/packages/lightweight-charts/NOTICE.md (100%) rename {website => src/website}/packages/lightweight-charts/README.md (100%) rename {website => src/website}/packages/lightweight-charts/types.d.ts (100%) rename {website => src/website}/packages/lightweight-charts/v4.2.0/script.js (100%) rename {website => src/website}/packages/lightweight-charts/v4.2.0/types.d.ts (100%) rename {website => src/website}/packages/lightweight-charts/wrapper.js (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/script.js (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/core/constants.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/core/core.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/core/effect.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/core/error.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/core/flags.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/core/index.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/core/owner.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/core/scheduler.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/index.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/map.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/signals.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/store/index.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/store/store.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-02/types/utils.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/script.js (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/core/constants.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/core/core.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/core/effect.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/core/error.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/core/flags.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/core/index.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/core/owner.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/core/scheduler.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/index.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/map.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/signals.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/store/index.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/store/store.d.ts (100%) rename {website => src/website}/packages/solid-signals/2024-11-08/types/utils.d.ts (100%) rename {website => src/website}/packages/solid-signals/README.md (100%) rename {website => src/website}/packages/solid-signals/types.d.ts (100%) rename {website => src/website}/packages/solid-signals/wrapper.js (100%) rename {website => src/website}/packages/ufuzzy/README.md (100%) rename {website => src/website}/packages/ufuzzy/v1.0.14/script.js (100%) rename {website => src/website}/packages/ufuzzy/v1.0.14/types.d.ts (100%) rename {website => src/website}/robots.txt (100%) rename {website => src/website}/scripts/chart.js (100%) rename {website => src/website}/scripts/live-price.js (100%) rename {website => src/website}/scripts/main.js (99%) rename {website => src/website}/scripts/moscow-time.js (100%) rename {website => src/website}/scripts/options.js (100%) rename {website => src/website}/scripts/service-worker.js (100%) rename {website => src/website}/scripts/simulation.js (100%) rename {website => src/website}/scripts/types/self.d.ts (100%) rename {website => src/website}/styles/chart.css (100%) rename {website => src/website}/styles/live-price.css (100%) rename {website => src/website}/styles/moscow-time.css (100%) rename {website => src/website}/styles/simulation.css (100%) rename {website => src/website}/tsconfig.json (100%) diff --git a/.gitignore b/.gitignore index af98ef201..da713f859 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,9 @@ # Mac OS .DS_Store -# To do -/charts -TODO.md - # Builds -dist target -# I/O -in -out -.log -/datasets -/datasets2 -/price -*..* -/txout_* -/db - # Sync .stfolder @@ -29,16 +13,10 @@ out # Ignored ignore -# Scripts -/start-node.sh - # Editors .vscode .zed -# Configs -config.toml - # Flamegraph flamegraph/ flamegraph.svg @@ -53,7 +31,7 @@ snapshots*/ docker/kibo # Types -website/scripts/types/paths.d.ts +paths.d.ts -# Misc -OPENSATS.md +# Git +.git* diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f703c65c..97b4a75e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,20 @@ -# Changelog - -## v. 0.5.0 | [873199](https://mempool.space/block/0000000000000000000270925aa6a565be92e13164565a3f7994ca1966e48050) - 2024/12/04 +# v0.6.0 | WIP -![Image of the kibō Web App version 0.5.0](./assets/v0.5.0.jpg) +- Merged parser and server crates into a single project (and thus executable) +- Started using `log` and `env_logger` crates +- Improved logs +- Added `--server BOOL` and `--parser BOOL` parameters (both are true by default) +- Automated databases defragmention (and removed parameter) +- Fixed input being unfocused right after being focused in Brave browser + +# [v0.5.0](https://github.com/kibo-money/kibo/tree/eea56d394bf92c62c81da8b78b8c47ea730683f5) | [873199](https://mempool.space/block/0000000000000000000270925aa6a565be92e13164565a3f7994ca1966e48050) - 2024/12/04 + +![Image of the kibō Web App version 0.5.0](https://github.com/kibo-money/kibo/blob/main/assets/v0.5.0.jpg) ## Datasets @@ -72,15 +79,15 @@ - Moved back to this repo -## v. 0.4.0 | [861950](https://mempool.space/block/00000000000000000000530d0e30ccf7deeace122dcc99f2668a06c6dad83629) - 2024/09/19 +# [v0.4.0](https://github.com/kibo-money/kibo/tree/a64c544815d9ef785e2fc1323582f774f16b9200) | [861950](https://mempool.space/block/00000000000000000000530d0e30ccf7deeace122dcc99f2668a06c6dad83629) - 2024/09/19 -![Image of the kibō Web App version 0.4.0](./assets/v0.4.0.jpg) +![Image of the kibō Web App version 0.4.0](https://github.com/kibo-money/kibo/blob/main/assets/v0.4.0.jpg) -### Brand +## Brand - **Satonomics** is now **kibō** 🎉 -### Website +## Website - Complete redesign of the website - Rewrote the whole application and removed `node`/`npm`/`pnpm` dependencies in favor for pure `HTML`/`CSS`/`Javascript` @@ -88,7 +95,7 @@ - Added Trading View attribution link to the settings frame and file in the lightweight charts folder - Many other changes -### Parser +## Parser - Changed the block iterator from a custom version of [bitcoin-explorer](https://crates.io/crates/bitcoin-explorer) to the homemade [biter](https://crates.io/crates/biter) which allows the parser to run alongside `bitcoind` - Added datasets compression thanks to [zstd](https://crates.io/crates/zstd) to reduce disk usage @@ -103,17 +110,17 @@ - Various first run fixes - Added to `-h` which arguments are saved, which is all of them at the time of writing -### Server +## Server - Updated the code to support compressed binaries - Added serving of the website - Improved `Cache-Control` behavior -## v. 0.3.0 | [853930](https://mempool.space/block/00000000000000000002eb5e9a7950ca2d5d98bd1ed28fc9098aa630d417985d) - 2024/07/26 +# [v0.3.0](https://github.com/kibo-money/kibo/tree/b68b016091c45b071218fba01bac5b76e8eaf18c) | [853930](https://mempool.space/block/00000000000000000002eb5e9a7950ca2d5d98bd1ed28fc9098aa630d417985d) - 2024/07/26 -![Image of the Satonomics Web App version 0.3.0](./assets/v0.3.0.jpg) +![Image of the Satonomics Web App version 0.3.0](https://github.com/kibo-money/kibo/blob/main/assets/v0.3.0.jpg) -### Parser +## Parser - Global - Improved self-hosting by: @@ -156,7 +163,7 @@ - Price - Improved error message when price cannot be found -### App +## App - General - Added chart scroll button for nice animations à la Wicked @@ -182,17 +189,17 @@ - Settings - Removed the horizontal scroll bar which was unintended -### Server +## Server - Run file - Only run with a watcher if `cargo watch` is available - Removed id_to_path file in favor for only `paths.d.ts` in `app/src/types` -## v. 0.2.0 | [851286](https://mempool.space/block/0000000000000000000281ca7f1bf8c50702bfca168c7af1bdc67c977c1ac8ed) - 2024/07/08 +# [v0.2.0](https://github.com/kibo-money/kibo/tree/248187889283597c5dbb806292297453c25e97b8) | [851286](https://mempool.space/block/0000000000000000000281ca7f1bf8c50702bfca168c7af1bdc67c977c1ac8ed) - 2024/07/08 -![Image of the Satonomics Web App version 0.2.0](./assets/v0.2.0.jpg) +![Image of the Satonomics Web App version 0.2.0](https://github.com/kibo-money/kibo/blob/main/assets/v0.2.0.jpg) -### App +## App - General - Added the height version of all datasets and many optimizations to make them usable but only available on desktop and tablets for now @@ -220,24 +227,24 @@ - Hopefully made scrollbars a little more subtle on WIndows and Linux, can't test - Generale style updates -### Parser +## Parser - Fixed ulimit only being run in Mac OS instead of whenever the program is detected -## v. 0.1.1 | [849240](https://mempool.space/block/000000000000000000002b8653988655071c07bb5f7181c038f9326bc86db741) - 2024/06/24 +# [v0.1.1](https://github.com/kibo-money/kibo/tree/e55b5195a9de9aea306903c94ed63cb1720fda5f) | [849240](https://mempool.space/block/000000000000000000002b8653988655071c07bb5f7181c038f9326bc86db741) - 2024/06/24 -![Image of the Satonomics Web App version 0.1.1](./assets/v0.1.1.jpg) +![Image of the Satonomics Web App version 0.1.1](https://github.com/kibo-money/kibo/blob/main/assets/v0.1.1.jpg) -### Parser +## Parser - Fixed overflow in `Price` struct which caused many Realized Caps and Realized Prices to have completely bogus data - Fixed Realized Cap computation which was using rounded prices instead normal ones -### Server +## Server - Added the chunk, date and time of the request to the terminal logs -### App +## App - Chart - Added double click option on a legend to toggle the visibility of all other series @@ -270,14 +277,14 @@ - Misc - Removed tracker even though it was a very privacy friendly as it appeared to not be working properly -### Price +## Price - Deleted old price datasets and their backups -## v. 0.1.0 | [848642](https://mempool.space/block/000000000000000000020be5761d70751252219a9557f55e91ecdfb86c4e026a) - 2024/06/19 +# [v0.1.0](https://github.com/kibo-money/kibo/tree/a1a576d088c8f83ed32d48753a7611f70a964574) | [848642](https://mempool.space/block/000000000000000000020be5761d70751252219a9557f55e91ecdfb86c4e026a) - 2024/06/19 -![Image of the Satonomics Web App version 0.1.0](./assets/v0.1.0.jpg) +![Image of the Satonomics Web App version 0.1.0](https://github.com/kibo-money/kibo/blob/main/assets/v0.1.0.jpg) -## v. 0.0.X | [835444](https://mempool.space/block/000000000000000000009f93907a0dd83c080d5585cc7ec82c076d45f6d7c872) - 2024/03/20 +# v0.0.1 | [835444](https://mempool.space/block/000000000000000000009f93907a0dd83c080d5585cc7ec82c076d45f6d7c872) - 2024/03/20 -![Image of the Satonomics Web App version 0.0.X](./assets/v0.0.X.jpg) +![Image of the Satonomics Web App version 0.0.X](https://github.com/kibo-money/kibo/blob/main/assets/v0.0.X.jpg) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6c0043c3..e23616908 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,6 +3,6 @@ ## Parser - Avoid floats as much as possible - - Use structs like `WAmount` and `Price` for calculations - - **Only** use `WAmount.to_btc()` when inserting or computing inside a dataset. It is **very** expensive. + - Use structs like `Amount` and `Price` for calculations + - **Only** use `Amount.to_btc()` when inserting or computing inside a dataset. It is **very** expensive. - No `Arc`, `Rc`, `Mutex` even from third party libraries, they're slower diff --git a/server/Cargo.lock b/Cargo.lock similarity index 90% rename from server/Cargo.lock rename to Cargo.lock index 540a98d4f..cd0f6c81f 100644 --- a/server/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -164,9 +164,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "arrayvec" @@ -176,9 +176,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "ast_node" -version = "1.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4d23a6d1d5f18bdbc06d9aa908880e5f49205156ba804751af731c51f5cf81a" +checksum = "91fb5864e2f5bf9fd9797b94b2dfd1554d4c3092b535008b27d7e15c86675a2f" dependencies = [ "proc-macro2", "quote", @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.13" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e614738943d3f68c628ae3dbce7c3daffb196665f82f8c8ea6b65de73c79429" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" dependencies = [ "brotli", "flate2", @@ -238,9 +238,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core", @@ -312,8 +312,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" dependencies = [ - "bitcoin-internals", - "bitcoin_hashes", + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.0", ] [[package]] @@ -351,9 +351,9 @@ checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" [[package]] name = "better_scoped_tls" -version = "0.1.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794edcc9b3fb07bb4aecaa11f093fd45663b4feadb782d68303a2268bc2701de" +checksum = "50fd297a11c709be8348aec039c8b91de16075d2b2bdaee1bd562c0875993664" dependencies = [ "scoped-tls", ] @@ -378,17 +378,17 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.3" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0032b0e8ead7074cda7fc4f034409607e3f03a6f71d66ade8a307f79b4d99e73" +checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" dependencies = [ "base58ck", "bech32", - "bitcoin-internals", - "bitcoin-io", + "bitcoin-internals 0.3.0", + "bitcoin-io 0.1.2", "bitcoin-units", - "bitcoin_hashes", - "hex-conservative", + "bitcoin_hashes 0.14.0", + "hex-conservative 0.2.1", "hex_lit", "secp256k1", "serde", @@ -403,19 +403,34 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin-internals" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b854212e29b96c8f0fe04cab11d57586c8f3257de0d146c76cb3b42b3eb9118" + [[package]] name = "bitcoin-io" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" +[[package]] +name = "bitcoin-io" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26792cd2bf245069a1c5acb06aa7ad7abe1de69b507c90b490bca81e0665d0ee" +dependencies = [ + "bitcoin-internals 0.4.0", +] + [[package]] name = "bitcoin-units" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" dependencies = [ - "bitcoin-internals", + "bitcoin-internals 0.3.0", "serde", ] @@ -425,11 +440,21 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ - "bitcoin-io", - "hex-conservative", + "bitcoin-io 0.1.2", + "hex-conservative 0.2.1", "serde", ] +[[package]] +name = "bitcoin_hashes" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0982261c82a50d89d1a411602afee0498b3e0debe3d36693f0c661352809639" +dependencies = [ + "bitcoin-io 0.2.0", + "hex-conservative 0.3.0", +] + [[package]] name = "bitcoincore-rpc" version = "0.19.0" @@ -456,7 +481,7 @@ dependencies = [ [[package]] name = "biter" -version = "0.1.1" +version = "0.2.1" dependencies = [ "bitcoin", "bitcoincore-rpc", @@ -591,9 +616,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -606,9 +631,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", "clap_derive", @@ -616,9 +641,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstream", "anstyle", @@ -640,9 +665,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "color-eyre" @@ -695,9 +720,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -894,6 +919,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" dependencies = [ "log", + "regex", ] [[package]] @@ -902,7 +928,10 @@ version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" dependencies = [ + "anstream", + "anstyle", "env_filter", + "humantime", "log", ] @@ -946,9 +975,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -986,9 +1015,9 @@ dependencies = [ [[package]] name = "from_variant" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d8947525c49c73130b5a7187b55b027b6b78fe60268d9f4c283ed690698cb1" +checksum = "8d7ccf961415e7aa17ef93dcb6c2441faaa8e768abe09e659b908089546f74c5" dependencies = [ "proc-macro2", "swc_macros_common", @@ -1144,12 +1173,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" - [[package]] name = "hex-conservative" version = "0.2.1" @@ -1159,6 +1182,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-conservative" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afe881d0527571892c4034822e59bb10c6c991cce6abe8199b6f5cf10766f55" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex_lit" version = "0.1.1" @@ -1225,6 +1257,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "1.4.1" @@ -1356,9 +1394,9 @@ dependencies = [ [[package]] name = "inferno" -version = "0.11.21" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" +checksum = "75a5d75fee4d36809e6b021e4b96b686e763d365ffdb03af2bd00786353f84fe" dependencies = [ "ahash", "clap", @@ -1367,7 +1405,6 @@ dependencies = [ "dashmap 6.1.0", "env_logger", "indexmap", - "is-terminal", "itoa", "log", "num-format", @@ -1394,27 +1431,16 @@ checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is-macro" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2069faacbe981460232f880d26bf3c7634e322d49053aa48c27e3ae642f728f1" +checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" dependencies = [ - "Inflector", + "heck", "proc-macro2", "quote", "syn 2.0.85", ] -[[package]] -name = "is-terminal" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" -dependencies = [ - "hermit-abi 0.4.0", - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -1475,6 +1501,41 @@ dependencies = [ "serde_json", ] +[[package]] +name = "kibo_money" +version = "0.6.0" +dependencies = [ + "allocative", + "axum", + "bincode", + "bitcoin_hashes 0.15.0", + "biter", + "chrono", + "clap", + "color-eyre", + "ctrlc", + "derive_deref", + "env_logger", + "inferno", + "itertools", + "log", + "ordered-float", + "rayon", + "regex", + "reqwest", + "rlimit", + "sanakirja", + "serde", + "serde_json", + "struct_iterable", + "swc", + "swc_common", + "tokio", + "toml", + "tower-http", + "zstd", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1508,6 +1569,9 @@ name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +dependencies = [ + "serde", +] [[package]] name = "lru" @@ -1541,9 +1605,9 @@ dependencies = [ [[package]] name = "miette" -version = "7.2.0" +version = "7.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +checksum = "317f146e2eb7021892722af37cf1b971f0a70c8406f487e24952667616192c64" dependencies = [ "cfg-if", "miette-derive", @@ -1555,9 +1619,9 @@ dependencies = [ [[package]] name = "miette-derive" -version = "7.2.0" +version = "7.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +checksum = "23c9b935fbe1d6cbd1dac857b54a688145e2d93f48db36010514d0f612d0ad67" dependencies = [ "proc-macro2", "quote", @@ -1611,7 +1675,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", "wasi", "windows-sys 0.52.0", @@ -1716,7 +1780,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] @@ -1781,9 +1845,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "4.4.0" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e7ccb95e240b7c9506a3d544f10d935e142cc90b0a1d56954fb44d89ad6b97" +checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e" dependencies = [ "num-traits", ] @@ -1854,32 +1918,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "parser" -version = "0.5.0" -dependencies = [ - "allocative", - "bincode", - "bitcoin_hashes", - "biter", - "chrono", - "clap", - "color-eyre", - "ctrlc", - "derive_deref", - "inferno", - "itertools", - "ordered-float", - "rayon", - "reqwest", - "sanakirja", - "serde", - "serde_json", - "struct_iterable", - "toml", - "zstd", -] - [[package]] name = "path-clean" version = "0.1.0" @@ -1894,9 +1932,9 @@ checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "percent-encoding" @@ -1985,9 +2023,9 @@ dependencies = [ [[package]] name = "preset_env_base" -version = "0.6.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaeaf4147a07c6615d43fce02d8139e1f835e6ee53e69db2a4810823c138c26" +checksum = "7c8a797e42c09d55157424ac6e9b6e9e5843fc68b887691b280b055e8c3ca5e4" dependencies = [ "ahash", "anyhow", @@ -2012,38 +2050,38 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] [[package]] name = "ptr_meta" -version = "0.1.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" dependencies = [ "ptr_meta_derive", ] [[package]] name = "ptr_meta_derive" -version = "0.1.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.85", ] [[package]] name = "quick-xml" -version = "0.26.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +checksum = "f22f29bdff3987b4d8632ef95fd6424ec7e4e0a57e2f4fc63e489e75357f6a03" dependencies = [ "memchr", ] @@ -2139,9 +2177,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -2151,9 +2189,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2234,6 +2272,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rlimit" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7043b63bd0cd1aaa628e476b80e6d4023a3b50eb32789f2728908107bd0c793a" +dependencies = [ + "libc", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2309,9 +2356,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" @@ -2373,7 +2420,7 @@ version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.0", "rand", "secp256k1-sys", "serde", @@ -2437,18 +2484,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", @@ -2457,9 +2504,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -2498,27 +2545,6 @@ dependencies = [ "serde", ] -[[package]] -name = "server" -version = "0.5.0" -dependencies = [ - "axum", - "bincode", - "chrono", - "color-eyre", - "derive_deref", - "itertools", - "parser", - "regex", - "reqwest", - "serde", - "serde_json", - "swc", - "swc_common", - "tokio", - "tower-http", -] - [[package]] name = "sha1" version = "0.10.6" @@ -2595,12 +2621,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - [[package]] name = "socket2" version = "0.5.7" @@ -2613,9 +2633,9 @@ dependencies = [ [[package]] name = "sourcemap" -version = "9.0.0" +version = "9.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab08a862c70980b8e23698b507e272317ae52a608a164a844111f5372374f1f" +checksum = "27c4ea7042fd1a155ad95335b5d505ab00d5124ea0332a06c8390d200bb1a76a" dependencies = [ "base64-simd", "bitvec", @@ -2690,9 +2710,9 @@ checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" [[package]] name = "string_enum" -version = "0.4.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90" +checksum = "c9fe66b8ee349846ce2f9557a26b8f1e74843c4a13fb381f9a3d73617a5f956a" dependencies = [ "proc-macro2", "quote", @@ -2736,9 +2756,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "swc" -version = "0.289.1" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65d51f69ac919b22a08e8654f63def43578a174132bda87309dea8fddd41a5d9" +checksum = "a8ded6be21206da7fb30e245d72373346d72fe095b75354a413b1d2b6bfda0b7" dependencies = [ "anyhow", "base64 0.21.7", @@ -2786,9 +2806,9 @@ dependencies = [ [[package]] name = "swc_allocator" -version = "0.1.9" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7016ee7a5186d6a80e381aa1926e0f3c7b06eaf444745ff7af3632e978eb8dc5" +checksum = "117d5d3289663f53022ebf157df8a42b3872d7ac759e63abf96b5987b85d4af3" dependencies = [ "bumpalo", "hashbrown 0.14.5", @@ -2799,9 +2819,9 @@ dependencies = [ [[package]] name = "swc_atoms" -version = "1.0.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b25ff0f3fd48ab1a95d86fd0505fdd1ac904f84d0350dc8222bbc824e9d4fdf6" +checksum = "151a6feb82b989a087433baca7f6a6eb4fcf83f828c479eecd039c9312d60e10" dependencies = [ "hstr", "once_cell", @@ -2811,9 +2831,9 @@ dependencies = [ [[package]] name = "swc_cached" -version = "0.3.20" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c" +checksum = "96b6a5ef4cfec51d3fa30b73600f206453a37fc30cf1141e4644a57b1ed88616" dependencies = [ "ahash", "anyhow", @@ -2825,9 +2845,9 @@ dependencies = [ [[package]] name = "swc_common" -version = "0.40.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f355465eaed1104244ce918b7ffb77ceb109aabeb74b04b98acae85683b0215b" +checksum = "a521e8120dc0401580864a643b5bffa035c29fc3fc41697c972743d4f008ed22" dependencies = [ "ahash", "ast_node", @@ -2854,9 +2874,9 @@ dependencies = [ [[package]] name = "swc_compiler_base" -version = "0.23.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20b82e7c3cc5954a5b2e9c545920232a1d37a70215719f5021f090eda9fcf88a" +checksum = "91e6b81e3504db35cb4931ac448cc88a80e89d04de00412b74102b9dc86ba131" dependencies = [ "anyhow", "base64 0.21.7", @@ -2880,9 +2900,9 @@ dependencies = [ [[package]] name = "swc_config" -version = "0.1.15" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" +checksum = "4aa30931f9b26af8edcb4cce605909d15dcfd7577220b22c50a2988f2a53c4c1" dependencies = [ "anyhow", "indexmap", @@ -2895,9 +2915,9 @@ dependencies = [ [[package]] name = "swc_config_macro" -version = "0.1.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5f56139042c1a95b54f5ca48baa0e0172d369bcc9d3d473dad1de36bae8399" +checksum = "7f2ebd37ef52a8555c8c9be78b694d64adcb5e3bc16c928f030d82f1d65fac57" dependencies = [ "proc-macro2", "quote", @@ -2907,9 +2927,9 @@ dependencies = [ [[package]] name = "swc_ecma_ast" -version = "0.121.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7e1b73c85ff968404867505646b3c6f26e4661e4fc831593b9e182fa59ddd4" +checksum = "94cf86f17358b93fcfe2876a9f0f7a7ebbff94cd6eaab4c809c7a0da1f4b892e" dependencies = [ "bitflags 2.6.0", "is-macro", @@ -2920,14 +2940,15 @@ dependencies = [ "string_enum", "swc_atoms", "swc_common", + "swc_visit", "unicode-id-start", ] [[package]] name = "swc_ecma_codegen" -version = "0.158.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1953878c2f44d796ff4ee8bde727890d24fe3bed9a86a23be4c4ef9ad79b6a6c" +checksum = "fb17e77270860f2a975c546c4609e9fa7ae8dbcf85260497e31af19890645800" dependencies = [ "memchr", "num-bigint", @@ -2945,9 +2966,9 @@ dependencies = [ [[package]] name = "swc_ecma_codegen_macros" -version = "0.7.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859fabde36db38634f3fad548dd5e3410c1aebba1b67a3c63e67018fa57a0bca" +checksum = "5f9a42f479a6475647e248fa9750982c87cd985e19d1016a1fc18a70682305d1" dependencies = [ "proc-macro2", "quote", @@ -2957,9 +2978,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_bugfixes" -version = "0.16.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff0f4ede38d4110f8c639a4c07c8d2b677d3eac968ab33da312bbaf6ad5adf0" +checksum = "448864548ea7c1866d140e36829196e5d7b35a55b4a1563945a8ce39322b8e7a" dependencies = [ "swc_atoms", "swc_common", @@ -2974,9 +2995,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_common" -version = "0.14.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a479b2061a3b2c939a9abd34cfd3e9a6983a8bf12f7d3da89ac7ad6a64d9a4" +checksum = "9a04016559d293d2ac507d931e46ecfd92eb5e96ccf5452bc81c18c47806b858" dependencies = [ "swc_common", "swc_ecma_ast", @@ -2987,9 +3008,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2015" -version = "0.16.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "129fa363e3ea01f9e2d2dbd06675bfd515689b261c28b30296d89c91b549916a" +checksum = "aceb1b432819541aa1dd129b4a226f41cf881a53040efc711a625ee5f23e9afd" dependencies = [ "arrayvec", "indexmap", @@ -3014,9 +3035,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2016" -version = "0.16.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0f1dd466100cd41e85be398d1f4d973f8d5760ec52376294918def88e620ad" +checksum = "fdc0d69f6541b781e4e95c1954e8ab916095b7369cdf69e14bf83be17b2dd943" dependencies = [ "swc_atoms", "swc_common", @@ -3031,9 +3052,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2017" -version = "0.16.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a3fcc3ae30628fc752d04b44c885ea6202ecfb187311d8827cc851ba7d906d" +checksum = "1ac80866f541899e4c59a954c0a37a4fd21ea1455dc44400c088b72299c1d2cc" dependencies = [ "serde", "swc_atoms", @@ -3049,9 +3070,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2018" -version = "0.16.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31160d66339dda14faefd3a2816a0436f0a1e4f33a2057d8f3663a1a567eaf2e" +checksum = "cdc28efd763ad5fea7acc2f1fc73ade5b334201369b88aff472053b64b304a76" dependencies = [ "serde", "swc_atoms", @@ -3068,9 +3089,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2019" -version = "0.16.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f0164ad462c81c421de2af75f408ca3a96324a3c5e81d999a90a58e6e40dbef" +checksum = "f2439138f8b8c7cf1401395e0bbb6d3e35d20277268f8e8aa41fa64841af992e" dependencies = [ "swc_atoms", "swc_common", @@ -3084,9 +3105,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2020" -version = "0.16.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da49ae2565352c179657b47e87b4879b8c1128b5447e8fbfaaa020702f740636" +checksum = "b7a37218dcb1ad82943b9ebfbfbc283ba1ba542ed3c60aacea0b3a06e7f38809" dependencies = [ "serde", "swc_atoms", @@ -3102,9 +3123,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2021" -version = "0.16.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9fed665a9f5b2b3c55fc04f36b12c8d2148fd25fb216a4d9ae9fac331a5374" +checksum = "9c4c9d40dcf5094e863ceac27c92053ef970991e46f190ddad678b8e3ca3ecde" dependencies = [ "swc_atoms", "swc_common", @@ -3118,9 +3139,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2022" -version = "0.16.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241bf311a26447690bbfc36ec76886dac04343f82e3fe638fc0e0cdbaeae534b" +checksum = "93edab9540513a7622addbf2494fa61f487daf98d2c1ed819958b2a3d271835e" dependencies = [ "swc_atoms", "swc_common", @@ -3137,9 +3158,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es3" -version = "0.16.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0c7867bba0129c8844cf274c9a0e3f1926d7225d6dce36343a4b1626399992" +checksum = "0f158dcb284c676bfb425c42adf307a3259d71c999c0970370ed3e09886d83c9" dependencies = [ "swc_common", "swc_ecma_ast", @@ -3152,9 +3173,9 @@ dependencies = [ [[package]] name = "swc_ecma_ext_transforms" -version = "0.123.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ea0bdb96b8d24305cf23614db4c734425c44329bd6e1734e6ebd068a88b536" +checksum = "d3cecd84290edc5c27d0cb6df79fcd82fe500772cd2526642955227e50359675" dependencies = [ "phf", "swc_atoms", @@ -3166,9 +3187,9 @@ dependencies = [ [[package]] name = "swc_ecma_lints" -version = "0.104.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afb24beda205684867ba555f59e4a470afbe67d9deaacc29b1a5e3a4a1fd58d" +checksum = "808120ab5d77e0c9484f4e1b248d5f9dfecb103160340eab5710f068d69964b1" dependencies = [ "auto_impl", "dashmap 5.5.3", @@ -3186,9 +3207,9 @@ dependencies = [ [[package]] name = "swc_ecma_loader" -version = "0.52.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f521eea3476abc7ae7adcbfa3479881087e5ae8fe358d46376d761e83fdb3120" +checksum = "1a19b132079bfcd19d6fdabce7e55ece93a30787f3b8684c8646ddaf2237812d" dependencies = [ "anyhow", "dashmap 5.5.3", @@ -3208,9 +3229,9 @@ dependencies = [ [[package]] name = "swc_ecma_minifier" -version = "0.208.1" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67d0fec4e2a4a00c1605f6baa2845cc05b4059c1bf9a6e45377c8ae67f04207d" +checksum = "5368ebcf923fb8e94c94f5fe0caa230c7920b72e5d79ed4cc32592c03213b43d" dependencies = [ "arrayvec", "indexmap", @@ -3243,9 +3264,9 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "0.152.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4cfd7930abe18c6829d4adedb5249e1b9fa68e8e786c6636250637992ba0466" +checksum = "c2c361b4153905dc088a6bacfaa944b582305cf94fbfcaa9b3aa61a7dd3adbf9" dependencies = [ "either", "new_debug_unreachable", @@ -3265,9 +3286,9 @@ dependencies = [ [[package]] name = "swc_ecma_preset_env" -version = "0.221.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b6b5a7222fe85be1732faa7a2c9812fc516d934ba320662c90c066264cd9eec" +checksum = "39ac4bd1620798c38f767417f9b364b87fd2505a9e748d6b1d7549f155a0aa96" dependencies = [ "anyhow", "dashmap 5.5.3", @@ -3290,9 +3311,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms" -version = "0.243.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b22f584075dfbd349c107def9c52de654979f0a1e8cdc4255dd4e7ec5b39800" +checksum = "dccedb5a1e52795e132615a3ca01e2adb8fc9214ee75c258f8a3124a9b42c47e" dependencies = [ "swc_atoms", "swc_common", @@ -3310,9 +3331,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "0.149.0" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2194d20416a03ae02058934f8387691809f86466a2f2e7a65c56dcb001bd46b" +checksum = "f31557485025a2fd1f833d63601c53010857e45633f44bcc87510f3578bde0c5" dependencies = [ "better_scoped_tls", "bitflags 2.6.0", @@ -3333,9 +3354,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_classes" -version = "0.138.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e93ef88e45587864ef645e29c33f04f75360e47194c084f931855af3aac246" +checksum = "3cd3a9eacaf2aa2ec7a8139e3c3883ddf4f8e948a08ee378725a4ad658beb3d9" dependencies = [ "swc_atoms", "swc_common", @@ -3347,9 +3368,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_compat" -version = "0.175.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35dd174f6c954a4fe1bff88a64de777ef65041411f96b60daf4a78864ad47789" +checksum = "e245e2cb96ff093c0c3170dbe4c063a0b937ce4527d37f52f467529d4977634b" dependencies = [ "arrayvec", "indexmap", @@ -3383,9 +3404,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_macros" -version = "0.5.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500a1dadad1e0e41e417d633b3d6d5de677c9e0d3159b94ba3348436cdb15aab" +checksum = "6845dfb88569f3e8cd05901505916a8ebe98be3922f94769ca49f84e8ccec8f7" dependencies = [ "proc-macro2", "quote", @@ -3395,9 +3416,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_module" -version = "0.194.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4317b5ce1f5fc00b7d72b712c822e1aa66cdb9dc155799ee2278e00b2172f918" +checksum = "5480053dfa9b7ae34c6d5cdaa33bee3d7dfd33a259cabe9122806f36625912c1" dependencies = [ "Inflector", "anyhow", @@ -3422,9 +3443,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_optimization" -version = "0.212.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2722095aef7b6de79b170c8c4e3c885576c5394e7a20328fdc51e880d6257b" +checksum = "350a4965abfada7d5b23b3140896652acc11e110ac042a160bcea5bf8b08d367" dependencies = [ "dashmap 5.5.3", "indexmap", @@ -3446,9 +3467,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_proposal" -version = "0.183.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718f1e24dd96cfb0b7ba8f8a4e61c98338cdac7a3f5f9f4a83951d776ac398bf" +checksum = "cba55188e66696c43333d19492f69a6f4a7785f9486ebc1b3cf7c5f743927505" dependencies = [ "either", "rustc-hash", @@ -3466,9 +3487,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_react" -version = "0.195.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ddcb8943976319a4f8b899862f094f6984511490e4bfa9b4c8fbd334539b62" +checksum = "4cabf9375cfb71fc0e3d98e07e6fca39a18daa23d4878d8d2daa4c2b6c07b379" dependencies = [ "base64 0.21.7", "dashmap 5.5.3", @@ -3491,9 +3512,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_typescript" -version = "0.202.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31acb773a2d82e5524d021aa77a38ae8bc708171b0005fd420b43356d8dfba25" +checksum = "77346c37397fb238f991d6dccc027881caca539628e9a6c629299c7b94bdb08a" dependencies = [ "ryu-js", "serde", @@ -3508,9 +3529,9 @@ dependencies = [ [[package]] name = "swc_ecma_usage_analyzer" -version = "0.33.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a1d8b627b6adc706ccd2a4a30a5413e9df91a9cff6569cb9d3b9f41c1bc8de" +checksum = "6f58c91cfddea5723e22dbb8b1738642714d0acd1463d26d3bc7518b83468414" dependencies = [ "indexmap", "rustc-hash", @@ -3525,9 +3546,9 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "0.137.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "939f21c75eff61ad3e485fc54d38988f2f9744ceda24a3feb8228dd072171d94" +checksum = "527fad9bdb16883782d55291fd3330925b3572f512ef89b3d92a29e2f713fe4f" dependencies = [ "indexmap", "num_cpus", @@ -3544,9 +3565,9 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "0.107.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15bd17e03251272ee04d1155036be5288055ab43d40ab2d9fd63ff815d326dad" +checksum = "b04c06c1805bda18c27165560f1617a57453feb9fb0638d90839053641af42d4" dependencies = [ "new_debug_unreachable", "num-bigint", @@ -3559,9 +3580,9 @@ dependencies = [ [[package]] name = "swc_eq_ignore_macros" -version = "0.1.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" +checksum = "e96e15288bf385ab85eb83cff7f9e2d834348da58d0a31b33bdb572e66ee413e" dependencies = [ "proc-macro2", "quote", @@ -3570,9 +3591,9 @@ dependencies = [ [[package]] name = "swc_error_reporters" -version = "1.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f9286183c9be40aafcbbe8c397403fb32af812a3dffe93eee9067aee4fbedb" +checksum = "4f741b530b2df577a287e193c4a111182de01b43361617af228ec9e6e6222fa4" dependencies = [ "anyhow", "miette", @@ -3583,9 +3604,9 @@ dependencies = [ [[package]] name = "swc_fast_graph" -version = "1.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3daff8d4379be2a99ab4b146e4dd631ef2415965dc4f1d33e988a737c5ccc39a" +checksum = "c22e0a0478b1b06610453a97c8371cafa742e371a79aff860ccfbabe1ab160a7" dependencies = [ "indexmap", "petgraph", @@ -3595,9 +3616,9 @@ dependencies = [ [[package]] name = "swc_macros_common" -version = "0.3.13" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" +checksum = "a509f56fca05b39ba6c15f3e58636c3924c78347d63853632ed2ffcb6f5a0ac7" dependencies = [ "proc-macro2", "quote", @@ -3606,9 +3627,9 @@ dependencies = [ [[package]] name = "swc_node_comments" -version = "0.27.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c325461f5e78e7749241f917945164adccfadcc2138a9a2e5ac9d186e1c459" +checksum = "1b56d29b30a2b3f407cc8a64e01414a4150d10cc5dd72d9c2d34734d8c0af951" dependencies = [ "dashmap 5.5.3", "swc_atoms", @@ -3617,18 +3638,18 @@ dependencies = [ [[package]] name = "swc_timer" -version = "0.27.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f53899e4ab3f9ce3db83b4c845f8a27f37bf700b233febbc9a6da749651c2fbb" +checksum = "4db06b46cc832f7cf83c2ce21905fc465d01443a2bdccf63644383e1f5847532" dependencies = [ "tracing", ] [[package]] name = "swc_trace_macro" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69aa25f667e4d74ab10a17a266edeb8b354273817b20b91e60471f1c860a221b" +checksum = "4c78717a841565df57f811376a3d19c9156091c55175e12d378f3a522de70cef" dependencies = [ "proc-macro2", "quote", @@ -3637,9 +3658,9 @@ dependencies = [ [[package]] name = "swc_transform_common" -version = "0.1.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda3e80e1ad638d3575bc07745a914af13dcb02215098659f864731078271f2c" +checksum = "f23ade45bb0d8b5299022dc0f674c2125512412f5b26f42cfaffa16dcc00d56b" dependencies = [ "better_scoped_tls", "once_cell", @@ -3650,21 +3671,25 @@ dependencies = [ [[package]] name = "swc_typescript" -version = "0.8.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b61f86dce2eeabd85d934a89cd481709918c751c44dfe433b5e4f1f40075fb5a" +checksum = "fe778782270111e7152a99af340bf763f12cdc60df775cd0ce51eaa74dcc0249" dependencies = [ + "petgraph", + "rustc-hash", "swc_atoms", "swc_common", "swc_ecma_ast", + "swc_ecma_utils", + "swc_ecma_visit", "thiserror", ] [[package]] name = "swc_visit" -version = "0.6.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceb044142ba2719ef9eb3b6b454fce61ab849eb696c34d190f04651955c613d" +checksum = "9138b6a36bbe76dd6753c4c0794f7e26480ea757bee499738bedbbb3ae3ec5f3" dependencies = [ "either", "new_debug_unreachable", @@ -3753,7 +3778,6 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" dependencies = [ - "smawk", "unicode-linebreak", "unicode-width", ] @@ -3805,9 +3829,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -3918,9 +3942,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "async-compression", "bitflags 2.6.0", @@ -3961,9 +3985,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -4043,9 +4067,9 @@ checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" [[package]] name = "unicode-id-start" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97e2a3c5fc9de285c0e805d98eba666adb4b2d9e1049ce44821ff7707cc34e91" +checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" [[package]] name = "unicode-ident" @@ -4105,9 +4129,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" [[package]] name = "valuable" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..bd7c6bde3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "kibo_money" +version = "0.6.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +allocative = "0.3.3" +axum = "0.7.9" +bincode = { git = "https://github.com/bincode-org/bincode.git", features = [ + "serde", +] } +bitcoin_hashes = { version = "0.15.0" } +biter = { path = "./crates/biter" } +chrono = { version = "0.4.39", features = ["serde"] } +clap = { version = "4.5.23", features = ["derive"] } +color-eyre = "0.6.3" +ctrlc = { version = "3.4.5", features = ["termination"] } +derive_deref = "1.1.1" +env_logger = "0.11.5" +inferno = "0.12.0" +itertools = "0.13.0" +log = { version = "0.4.22", features = ["std", "serde"] } +ordered-float = "4.5.0" +rayon = "1.10.0" +regex = "1.11.1" +reqwest = { version = "0.12.9", features = ["blocking", "json"] } +rlimit = "0.10.2" +sanakirja = "1.4.3" +serde = { version = "1.0.216", features = ["derive"] } +serde_json = "1.0.133" +struct_iterable = { path = "./crates/iterable" } +swc = "9.0.0" +swc_common = "5.0.0" +tokio = { version = "1.42.0", features = ["full"] } +toml = "0.8.19" +tower-http = { version = "0.6.2", features = ["compression-full"] } +zstd = "0.13.2" diff --git a/README.md b/README.md index c2e0ce2cb..d602d55d8 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ Now we can finally start by running the parser, you need to use the `./run.sh` s For the first launch, the parser will need several information such as: - `--datadir`: which is bitcoin data directory path, prefer `$HOME` to `~` as the latter might not work +- `--outdir`: where all outputs will be saved, prefer `$HOME` to `~` as the latter might not work Optionally you can also specify: @@ -136,7 +137,7 @@ Everything will be saved in a `config.toml` file, which will allow you to simply Here's an example ```bash -./run.sh --datadir=$HOME/Developer/bitcoin +./run.sh --datadir=$HOME/Developer/bitcoin --outdir=$HOME/.kibo/out ``` In a **new** terminal, go to the `server`'s folder of the repository diff --git a/crates/biter/CHANGELOG.md b/crates/biter/CHANGELOG.md new file mode 100644 index 000000000..9b9c5b195 --- /dev/null +++ b/crates/biter/CHANGELOG.md @@ -0,0 +1,12 @@ +# v0.2.1 + +- Clean `.json` if necessary +- Only save `.json` if needed +- Updated benchmarks +- Updated packages + +# v0.2.0 + +- Removed the need for an output directory path +- Changed the location of the saved json file from the previously needed output directory path to the Bitcoin data directory +- Added a save of the json file every 144 * 30 blocks instead of only at the end diff --git a/biter/Cargo.lock b/crates/biter/Cargo.lock similarity index 85% rename from biter/Cargo.lock rename to crates/biter/Cargo.lock index e178bb13f..b71079ba4 100644 --- a/biter/Cargo.lock +++ b/crates/biter/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "base58ck" @@ -32,9 +32,9 @@ checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" [[package]] name = "bitcoin" -version = "0.32.2" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea507acc1cd80fc084ace38544bbcf7ced7c2aa65b653b102de0ce718df668f6" +checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" dependencies = [ "base58ck", "bech32", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "biter" -version = "0.1.1" +version = "0.2.1" dependencies = [ "bitcoin", "bitcoincore-rpc", @@ -129,9 +129,12 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.6" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -258,9 +261,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "log" @@ -287,27 +290,27 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -370,9 +373,9 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "secp256k1" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes", "rand", @@ -382,27 +385,27 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", ] [[package]] name = "serde" -version = "1.0.204" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", @@ -411,9 +414,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.122" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -422,10 +425,16 @@ dependencies = [ ] [[package]] -name = "syn" -version = "2.0.72" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -434,9 +443,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "wasi" @@ -446,9 +455,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "zerocopy" -version = "0.6.6" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", "zerocopy-derive", @@ -456,9 +465,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.6.6" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", diff --git a/biter/Cargo.toml b/crates/biter/Cargo.toml similarity index 65% rename from biter/Cargo.toml rename to crates/biter/Cargo.toml index f01e3608d..fb39c739d 100644 --- a/biter/Cargo.toml +++ b/crates/biter/Cargo.toml @@ -1,19 +1,19 @@ [package] name = "biter" description = "A very fast Bitcoin block iterator" -version = "0.1.1" +version = "0.2.1" license = "MIT" -repository = "https://github.com/kibo-money/kibo/tree/main/biter" +repository = "https://github.com/kibo-money/kibo/tree/main/crates/biter" keywords = ["bitcoin", "block", "iterator"] categories = ["cryptography::cryptocurrencies", "encoding"] edition = "2021" [dependencies] -bitcoin = { version = "0.32.2", features = ["serde"] } +bitcoin = { version = "0.32.5", features = ["serde"] } rayon = "1.10.0" crossbeam = { version = "0.8.4", features = ["crossbeam-channel"] } -serde = { version = "1.0.204", features = ["derive"] } -serde_json = "1.0.122" +serde = { version = "1.0.216", features = ["derive"] } +serde_json = "1.0.133" derived-deref = "2.1.0" bitcoincore-rpc = "0.19.0" # tokio = { version = "1.39.2", features = ["rt-multi-thread"] } diff --git a/biter/LICENSE.md b/crates/biter/LICENSE.md similarity index 100% rename from biter/LICENSE.md rename to crates/biter/LICENSE.md diff --git a/biter/README.md b/crates/biter/README.md similarity index 71% rename from biter/README.md rename to crates/biter/README.md index 932108dac..13c3097e6 100644 --- a/biter/README.md +++ b/crates/biter/README.md @@ -10,6 +10,8 @@ The element returned by the iterator is a tuple which includes the: ## Example ```rust +use std::path::Path; + use bitcoincore_rpc::{Auth, Client}; fn main() { @@ -18,9 +20,6 @@ fn main() { // Path to the Bitcoin data directory let data_dir = "../../bitcoin"; - // Path to the export directory where a mini blk indexer will be exported - let export_dir = "./target"; - // Inclusive starting height of the blocks received, `None` for 0 let start = Some(850_000); @@ -29,11 +28,15 @@ fn main() { // RPC client to filter out forks let url = "http://localhost:8332"; - let auth = Auth::UserPass("satoshi".to_string(), "nakamoto".to_string()); + let cookie = Path::new(data_dir).join(".cookie"); + let auth = Auth::CookieFile(cookie); let rpc = Client::new(url, auth).unwrap(); + if cookie.is_file() { + Ok() + // Create channel receiver then iterate over the blocks - biter::new(data_dir, export_dir, start, end, rpc) + biter::new(data_dir, start, end, rpc) .iter() .for_each(|(height, _block, hash)| { println!("{height}: {hash}"); @@ -52,11 +55,11 @@ Peak memory should be around 500MB. ## Comparaison -| | [biter](https://crates.io/crates/biter) | [bitcoin-explorer](https://crates.io/crates/bitcoin-explorer) | [blocks_iterator](https://crates.io/crates/blocks_iterator) | +| | [biter](https://crates.io/crates/biter) | [bitcoin-explorer (depreciated)](https://crates.io/crates/bitcoin-explorer) | [blocks_iterator](https://crates.io/crates/blocks_iterator) | | --- | --- | --- | --- | -| Run **with** `bitcoind` | Yes ✅ | No ❌ | Yes ✅ | -| Run **without** `bitcoind` | No ❌ | Yes ✅ | Yes ✅ | -| `0..=855_000` | 16mn40s | 17mn 46s | > 2h | -| `800_000..=855_000` | 2mn 53s (16mn40s if first run) | 3mn 2s | > 2h | +| 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 | *Benchmarked on a Macbook Pro M3 Pro* diff --git a/biter/src/blk_index_to_blk_recap.rs b/crates/biter/src/blk_index_to_blk_recap.rs similarity index 73% rename from biter/src/blk_index_to_blk_recap.rs rename to crates/biter/src/blk_index_to_blk_recap.rs index 9c9d53c53..c3823447c 100644 --- a/biter/src/blk_index_to_blk_recap.rs +++ b/crates/biter/src/blk_index_to_blk_recap.rs @@ -1,28 +1,31 @@ use std::{ cmp::Ordering, - collections::BTreeMap, + collections::{BTreeMap, BTreeSet}, fs::{self, File}, io::{BufReader, BufWriter}, - path::PathBuf, + path::{Path, PathBuf}, }; use derived_deref::{Deref, DerefMut}; use crate::{blk_recap::BlkRecap, BlkMetadataAndBlock}; +const TARGET_BLOCKS_PER_MONTH: usize = 144 * 30; + #[derive(Deref, DerefMut, Debug)] pub struct BlkIndexToBlkRecap { - path: String, + path: PathBuf, #[target] tree: BTreeMap, + last_safe_recap: Option, } impl BlkIndexToBlkRecap { - pub fn import(blocks_dir: &BTreeMap, export_dir: &str) -> Self { - let path = format!("{export_dir}/blk_index_to_blk_recap.json"); + pub fn import(blocks_dir: &BTreeMap, data_dir: &Path) -> Self { + let path = data_dir.join("blk_index_to_blk_recap.json"); let tree = { - fs::create_dir_all(export_dir).unwrap(); + fs::create_dir_all(data_dir).unwrap(); if let Ok(file) = File::open(&path) { let reader = BufReader::new(file); @@ -32,7 +35,11 @@ impl BlkIndexToBlkRecap { } }; - let mut this = Self { path, tree }; + let mut this = Self { + path, + tree, + last_safe_recap: None, + }; this.clean_outdated(blocks_dir); @@ -40,13 +47,22 @@ impl BlkIndexToBlkRecap { } pub fn clean_outdated(&mut self, blocks_dir: &BTreeMap) { + let mut unprocessed_keys = self.keys().copied().collect::>(); + blocks_dir.iter().for_each(|(blk_index, blk_path)| { + unprocessed_keys.remove(blk_index); if let Some(blk_recap) = self.get(blk_index) { if blk_recap.has_different_modified_time(blk_path) { self.remove(blk_index); } } }); + + unprocessed_keys.iter().for_each(|blk_index| { + self.remove(blk_index); + }); + + self.last_safe_recap = self.last_entry().map(|e| e.get().clone()); } pub fn get_start_recap(&self, start: Option) -> Option<(usize, BlkRecap)> { @@ -73,7 +89,6 @@ impl BlkIndexToBlkRecap { let blk_index = blk_metadata_and_block.blk_metadata.index; if let Some(last_entry) = self.last_entry() { - // if last_entry.get().is_older_than(height) { match last_entry.key().cmp(&blk_index) { Ordering::Greater => { last_entry.remove_entry(); @@ -83,7 +98,6 @@ impl BlkIndexToBlkRecap { } Ordering::Equal => {} }; - // } } else { if blk_index != 0 || height != 0 { // dbg!(blk_index, height); @@ -92,6 +106,14 @@ impl BlkIndexToBlkRecap { self.insert(blk_index, BlkRecap::first(blk_metadata_and_block)); } + + if self + .last_safe_recap + .map_or(true, |recap| recap.height() >= height) + && (height % TARGET_BLOCKS_PER_MONTH) == 0 + { + self.export(); + } } pub fn export(&self) { diff --git a/biter/src/blk_metadata.rs b/crates/biter/src/blk_metadata.rs similarity index 100% rename from biter/src/blk_metadata.rs rename to crates/biter/src/blk_metadata.rs diff --git a/biter/src/blk_metadata_and_block.rs b/crates/biter/src/blk_metadata_and_block.rs similarity index 100% rename from biter/src/blk_metadata_and_block.rs rename to crates/biter/src/blk_metadata_and_block.rs diff --git a/biter/src/blk_recap.rs b/crates/biter/src/blk_recap.rs similarity index 100% rename from biter/src/blk_recap.rs rename to crates/biter/src/blk_recap.rs diff --git a/biter/src/lib.rs b/crates/biter/src/lib.rs similarity index 96% rename from biter/src/lib.rs rename to crates/biter/src/lib.rs index a064fa1cb..ed80f1882 100644 --- a/biter/src/lib.rs +++ b/crates/biter/src/lib.rs @@ -2,6 +2,7 @@ use std::{ collections::{BTreeMap, BTreeSet, VecDeque}, fs::{self}, ops::ControlFlow, + path::Path, thread, }; @@ -44,7 +45,6 @@ enum BlockState { /// # Arguments /// /// * `data_dir` - Path to the Bitcoin data directory -/// * `export_dir` - Path to the export directory where a mini blk indexer will be exported /// * `start` - Inclusive starting height of the blocks received, `None` for 0 /// * `end` - Inclusive ending height of the blocks received, `None` for the last one /// * `rpc` - RPC client to filter out forks @@ -52,21 +52,23 @@ enum BlockState { /// # Example /// /// ```rust +/// use std::path::Path; +/// /// use bitcoincore_rpc::{Auth, Client}; /// /// fn main() { /// let i = std::time::Instant::now(); /// +/// let data_dir = Path::new("../../bitcoin"); /// let url = "http://localhost:8332"; -/// let auth = Auth::UserPass("satoshi".to_string(), "nakamoto".to_string()); +/// let cookie = Path::new(data_dir).join(".cookie"); +/// let auth = Auth::CookieFile(cookie); /// let rpc = Client::new(url, auth).unwrap(); /// -/// let data_dir = "../../bitcoin"; -/// let export_dir = "./target"; /// let start = Some(850_000); /// let end = None; /// -/// biter::new(data_dir, export_dir, start, end, rpc) +/// biter::new(data_dir, start, end, rpc) /// .iter() /// .for_each(|(height, _block, hash)| { /// println!("{height}: {hash}"); @@ -77,8 +79,7 @@ enum BlockState { /// ``` /// pub fn new( - data_dir: &str, - export_dir: &str, + data_dir: &Path, start: Option, end: Option, rpc: bitcoincore_rpc::Client, @@ -87,15 +88,15 @@ pub fn new( let (send_block, recv_block) = bounded(BOUND_CAP); let (send_height_block_hash, recv_height_block_hash) = bounded(BOUND_CAP); - let blocks_dir = scan_blocks_dir(data_dir); + let blk_index_to_blk_path = scan_blocks_dir(data_dir); - let mut blk_index_to_blk_recap = BlkIndexToBlkRecap::import(&blocks_dir, export_dir); + let mut blk_index_to_blk_recap = BlkIndexToBlkRecap::import(&blk_index_to_blk_path, data_dir); let start_recap = blk_index_to_blk_recap.get_start_recap(start); let starting_blk_index = start_recap.as_ref().map_or(0, |(index, _)| *index); thread::spawn(move || { - blocks_dir + blk_index_to_blk_path .into_iter() .filter(|(blk_index, _)| blk_index >= &starting_blk_index) .try_for_each(move |(blk_index, blk_path)| { diff --git a/biter/src/main.rs b/crates/biter/src/main.rs similarity index 54% rename from biter/src/main.rs rename to crates/biter/src/main.rs index 57ac1f020..ed1da58cb 100644 --- a/biter/src/main.rs +++ b/crates/biter/src/main.rs @@ -1,18 +1,20 @@ +use std::path::Path; + use bitcoincore_rpc::{Auth, Client}; fn main() { let i = std::time::Instant::now(); + let data_dir = Path::new("../../../bitcoin"); let url = "http://localhost:8332"; - let auth = Auth::UserPass("satoshi".to_string(), "nakamoto".to_string()); + let cookie = Path::new(data_dir).join(".cookie"); + let auth = Auth::CookieFile(cookie); let rpc = Client::new(url, auth).unwrap(); - let data_dir = "../bitcoin"; - let export_dir = "./target"; - let start = None; - let end = None; + let start = Some(800_000); + let end = Some(855_000); - biter::new(data_dir, export_dir, start, end, rpc) + biter::new(data_dir, start, end, rpc) .iter() .for_each(|(height, _block, hash)| { println!("{height}: {hash}"); diff --git a/biter/src/utils.rs b/crates/biter/src/utils.rs similarity index 79% rename from biter/src/utils.rs rename to crates/biter/src/utils.rs index 9b1bdb945..78819179e 100644 --- a/biter/src/utils.rs +++ b/crates/biter/src/utils.rs @@ -1,12 +1,17 @@ -use std::{collections::BTreeMap, fs, path::PathBuf, time::UNIX_EPOCH}; +use std::{ + collections::BTreeMap, + fs, + path::{Path, PathBuf}, + time::UNIX_EPOCH, +}; const BLK: &str = "blk"; const DAT: &str = ".dat"; -pub fn scan_blocks_dir(data_dir_path: &str) -> BTreeMap { - let blocks_dir_path = &format!("{data_dir_path}/blocks"); +pub fn scan_blocks_dir(data_dir: &Path) -> BTreeMap { + let blocks_dir = data_dir.join("blocks"); - fs::read_dir(blocks_dir_path) + fs::read_dir(blocks_dir) .unwrap() .map(|entry| entry.unwrap().path()) .filter(|path| { diff --git a/iterable/Cargo.lock b/crates/iterable/Cargo.lock similarity index 100% rename from iterable/Cargo.lock rename to crates/iterable/Cargo.lock diff --git a/iterable/Cargo.toml b/crates/iterable/Cargo.toml similarity index 100% rename from iterable/Cargo.toml rename to crates/iterable/Cargo.toml diff --git a/iterable/README.md b/crates/iterable/README.md similarity index 100% rename from iterable/README.md rename to crates/iterable/README.md diff --git a/iterable/src/lib.rs b/crates/iterable/src/lib.rs similarity index 100% rename from iterable/src/lib.rs rename to crates/iterable/src/lib.rs diff --git a/iterable/struct_iterable_derive/Cargo.toml b/crates/iterable/struct_iterable_derive/Cargo.toml similarity index 100% rename from iterable/struct_iterable_derive/Cargo.toml rename to crates/iterable/struct_iterable_derive/Cargo.toml diff --git a/iterable/struct_iterable_derive/README.md b/crates/iterable/struct_iterable_derive/README.md similarity index 100% rename from iterable/struct_iterable_derive/README.md rename to crates/iterable/struct_iterable_derive/README.md diff --git a/iterable/struct_iterable_derive/src/lib.rs b/crates/iterable/struct_iterable_derive/src/lib.rs similarity index 100% rename from iterable/struct_iterable_derive/src/lib.rs rename to crates/iterable/struct_iterable_derive/src/lib.rs diff --git a/iterable/struct_iterable_internal/Cargo.toml b/crates/iterable/struct_iterable_internal/Cargo.toml similarity index 100% rename from iterable/struct_iterable_internal/Cargo.toml rename to crates/iterable/struct_iterable_internal/Cargo.toml diff --git a/iterable/struct_iterable_internal/README.md b/crates/iterable/struct_iterable_internal/README.md similarity index 100% rename from iterable/struct_iterable_internal/README.md rename to crates/iterable/struct_iterable_internal/README.md diff --git a/iterable/struct_iterable_internal/src/lib.rs b/crates/iterable/struct_iterable_internal/src/lib.rs similarity index 100% rename from iterable/struct_iterable_internal/src/lib.rs rename to crates/iterable/struct_iterable_internal/src/lib.rs diff --git a/parser/Cargo.lock b/parser/Cargo.lock deleted file mode 100644 index 91514253c..000000000 --- a/parser/Cargo.lock +++ /dev/null @@ -1,2430 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "allocative" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "082af274fd02beef17b7f0725a49ecafe6c075ef56cac9d6363eb3916a9817ae" -dependencies = [ - "allocative_derive", - "ctor", -] - -[[package]] -name = "allocative_derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe233a377643e0fc1a56421d7c90acdec45c291b30345eb9f08e8d0ddce5a4ab" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.85", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" - -[[package]] -name = "anstyle-parse" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "backtrace" -version = "0.3.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base58ck" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" -dependencies = [ - "bitcoin-internals", - "bitcoin_hashes", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bech32" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" - -[[package]] -name = "bincode" -version = "2.0.0-rc.3" -source = "git+https://github.com/bincode-org/bincode.git#5a91c1210168b968b957a14b743cf134f2d20719" -dependencies = [ - "bincode_derive", - "serde", - "unty", -] - -[[package]] -name = "bincode_derive" -version = "2.0.0-rc.3" -source = "git+https://github.com/bincode-org/bincode.git#5a91c1210168b968b957a14b743cf134f2d20719" -dependencies = [ - "virtue", -] - -[[package]] -name = "bitcoin" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0032b0e8ead7074cda7fc4f034409607e3f03a6f71d66ade8a307f79b4d99e73" -dependencies = [ - "base58ck", - "bech32", - "bitcoin-internals", - "bitcoin-io", - "bitcoin-units", - "bitcoin_hashes", - "hex-conservative", - "hex_lit", - "secp256k1", - "serde", -] - -[[package]] -name = "bitcoin-internals" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" -dependencies = [ - "serde", -] - -[[package]] -name = "bitcoin-io" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" - -[[package]] -name = "bitcoin-units" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" -dependencies = [ - "bitcoin-internals", - "serde", -] - -[[package]] -name = "bitcoin_hashes" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" -dependencies = [ - "bitcoin-io", - "hex-conservative", - "serde", -] - -[[package]] -name = "bitcoincore-rpc" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee" -dependencies = [ - "bitcoincore-rpc-json", - "jsonrpc", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "bitcoincore-rpc-json" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" -dependencies = [ - "bitcoin", - "serde", - "serde_json", -] - -[[package]] -name = "biter" -version = "0.1.1" -dependencies = [ - "bitcoin", - "bitcoincore-rpc", - "crossbeam", - "derived-deref", - "rayon", - "serde", - "serde_json", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "bytemuck" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" - -[[package]] -name = "cc" -version = "1.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" -dependencies = [ - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets", -] - -[[package]] -name = "clap" -version = "4.5.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.85", -] - -[[package]] -name = "clap_lex" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" - -[[package]] -name = "color-eyre" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" -dependencies = [ - "backtrace", - "color-spantrace", - "eyre", - "indenter", - "once_cell", - "owo-colors", - "tracing-error", -] - -[[package]] -name = "color-spantrace" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" -dependencies = [ - "once_cell", - "owo-colors", - "tracing-core", - "tracing-error", -] - -[[package]] -name = "colorchoice" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ctrlc" -version = "3.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" -dependencies = [ - "nix", - "windows-sys 0.59.0", -] - -[[package]] -name = "dashmap" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core 0.9.10", -] - -[[package]] -name = "derive_deref" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcdbcee2d9941369faba772587a565f4f534e42cb8d17e5295871de730163b2b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derived-deref" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "805ef2023ccd65425743a91ecd11fc020979a0b01921db3104fb606d18a7b43e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.85", -] - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_filter" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" -dependencies = [ - "log", -] - -[[package]] -name = "env_logger" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" -dependencies = [ - "env_filter", - "log", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "eyre" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fastrand" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "h2" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - -[[package]] -name = "hashbrown" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" - -[[package]] -name = "hex-conservative" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "hex_lit" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" -dependencies = [ - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" - -[[package]] -name = "hyper" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" -dependencies = [ - "futures-util", - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - -[[package]] -name = "indexmap" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" -dependencies = [ - "equivalent", - "hashbrown 0.15.0", -] - -[[package]] -name = "inferno" -version = "0.11.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" -dependencies = [ - "ahash", - "clap", - "crossbeam-channel", - "crossbeam-utils", - "dashmap", - "env_logger", - "indexmap", - "is-terminal", - "itoa", - "log", - "num-format", - "once_cell", - "quick-xml", - "rgb", - "str_stack", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "ipnet" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" - -[[package]] -name = "is-terminal" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" -dependencies = [ - "hermit-abi 0.4.0", - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "jobserver" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "jsonrpc" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" -dependencies = [ - "base64 0.13.1", - "minreq", - "serde", - "serde_json", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.159" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "memmap2" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" -dependencies = [ - "libc", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "minreq" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763d142cdff44aaadd9268bebddb156ef6c65a0e13486bb81673cf2d8739f9b0" -dependencies = [ - "log", - "serde", - "serde_json", -] - -[[package]] -name = "mio" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "wasi", - "windows-sys 0.52.0", -] - -[[package]] -name = "native-tls" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "num-format" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" -dependencies = [ - "arrayvec", - "itoa", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "openssl" -version = "0.10.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.85", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "ordered-float" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e7ccb95e240b7c9506a3d544f10d935e142cc90b0a1d56954fb44d89ad6b97" -dependencies = [ - "num-traits", -] - -[[package]] -name = "owo-colors" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.7", - "smallvec", - "windows-targets", -] - -[[package]] -name = "parser" -version = "0.5.0" -dependencies = [ - "allocative", - "bincode", - "bitcoin_hashes", - "biter", - "chrono", - "clap", - "color-eyre", - "ctrlc", - "derive_deref", - "inferno", - "itertools", - "ordered-float", - "rayon", - "reqwest", - "sanakirja", - "serde", - "serde_json", - "struct_iterable", - "toml", - "zstd", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro2" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quick-xml" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" -dependencies = [ - "memchr", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "reqwest" -version = "0.12.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-registry", -] - -[[package]] -name = "rgb" -version = "0.8.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustix" -version = "0.38.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" -dependencies = [ - "bitflags 2.6.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.23.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" -dependencies = [ - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "sanakirja" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81aaf70d064e2122209f04d01fd91e8908e7a327b516236e1cbc0c3f34ac6d11" -dependencies = [ - "fs2", - "log", - "memmap2", - "parking_lot", - "sanakirja-core", - "serde", - "thiserror", -] - -[[package]] -name = "sanakirja-core" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8376db34ae3eac6e7bd91168bc638450073b708ce9fb46940de676f552238bf5" - -[[package]] -name = "schannel" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "secp256k1" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" -dependencies = [ - "bitcoin_hashes", - "rand", - "secp256k1-sys", - "serde", -] - -[[package]] -name = "secp256k1-sys" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" -dependencies = [ - "cc", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.6.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "serde" -version = "1.0.214" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.214" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.85", -] - -[[package]] -name = "serde_json" -version = "1.0.132" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "str_stack" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "struct_iterable" -version = "0.1.2" -dependencies = [ - "struct_iterable_derive", - "struct_iterable_internal", -] - -[[package]] -name = "struct_iterable_derive" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "struct_iterable_internal", - "syn 2.0.85", -] - -[[package]] -name = "struct_iterable_internal" -version = "0.1.1" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" -dependencies = [ - "futures-core", -] - -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags 2.6.0", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tempfile" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" -dependencies = [ - "cfg-if", - "fastrand", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "thiserror" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.85", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" -dependencies = [ - "rustls", - "rustls-pki-types", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-error" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" -dependencies = [ - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "sharded-slab", - "thread_local", - "tracing-core", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "unty" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a88342087869553c259588a3ec9ca73ce9b2d538b7051ba5789ff236b6c129" - -[[package]] -name = "url" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "virtue" -version = "0.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7302ac74a033bf17b6e609ceec0f891ca9200d502d31f02dc7908d3d98767c9d" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.85", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.85", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" - -[[package]] -name = "web-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-registry" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" -dependencies = [ - "windows-result", - "windows-strings", - "windows-targets", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.85", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zstd" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/parser/Cargo.toml b/parser/Cargo.toml deleted file mode 100644 index bf12d64de..000000000 --- a/parser/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "parser" -version = "0.5.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -allocative = "0.3.3" -bincode = { git = "https://github.com/bincode-org/bincode.git", features = [ - "serde", -] } -bitcoin_hashes = { version = "0.14.0" } -biter = { path = "../biter" } -chrono = { version = "0.4.38", features = ["serde"] } -clap = { version = "4.5.20", features = ["derive"] } -color-eyre = "0.6.3" -ctrlc = { version = "3.4.5", features = ["termination"] } -derive_deref = "1.1.1" -inferno = "0.11.21" -itertools = "0.13.0" -ordered-float = "4.4.0" -rayon = "1.10.0" -reqwest = { version = "0.12.9", features = ["blocking", "json"] } -sanakirja = "1.4.3" -serde = { version = "1.0.214", features = ["derive"] } -serde_json = "1.0.132" -struct_iterable = { path = "../iterable" } -toml = "0.8.19" -zstd = "0.13.2" -# memory-stats = "1.2.0" -# sysinfo = "0.32.0" diff --git a/parser/reset.sh b/parser/reset.sh deleted file mode 100755 index d7a64be38..000000000 --- a/parser/reset.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -echo "Deleting datasets..." -rm -r ../datasets -echo "Deleting states and databases..." -rm -r ./out -echo "Done." diff --git a/parser/samply.sh b/parser/samply.sh deleted file mode 100755 index 6376c139a..000000000 --- a/parser/samply.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -echo "Increasing limit of opened files..." -ulimit -n 1000000 - -# Needed because the datasets tree is too big lol -echo "Increasing stack size..." -ulimit -s $(ulimit -Hs) - -cargo build --profile profiling && samply record ./target/profiling/parser "$HOME/Developer/bitcoin" diff --git a/parser/src/databases/_trait.rs b/parser/src/databases/_trait.rs deleted file mode 100644 index acf040d0b..000000000 --- a/parser/src/databases/_trait.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::{fs, io, path::PathBuf}; - -use crate::{ - io::OUTPUTS_FOLDER_PATH, - structs::{Date, Height}, - utils::log, -}; - -use super::AnyDatabase; - -pub trait AnyDatabaseGroup -where - Self: Sized, -{ - fn init() -> Self { - let s = Self::import(); - s.create_dir_all().unwrap(); - s - } - - fn import() -> Self; - - fn folder<'a>() -> &'a str; - - fn drain_to_vec(&mut self) -> Vec>; - fn open_all(&mut self); - - fn export_metadata(&mut self, height: Height, date: Date) -> color_eyre::Result<()>; - - fn create_dir_all(&self) -> color_eyre::Result<(), io::Error>; - - fn remove_dir_all(&self) -> color_eyre::Result<(), io::Error> { - fs::remove_dir_all(Self::root()) - } - - fn reset(&mut self) -> color_eyre::Result<(), io::Error> { - log(&format!("Reset {}", Self::folder())); - - self.reset_metadata(); - self.remove_dir_all()?; - self.create_dir_all()?; - - Ok(()) - } - - fn reset_metadata(&mut self); - - fn root() -> PathBuf { - let folder = Self::folder(); - PathBuf::from(format!("{OUTPUTS_FOLDER_PATH}/databases/{folder}")) - } -} diff --git a/parser/src/io/consts.rs b/parser/src/io/consts.rs deleted file mode 100644 index 750f7edb0..000000000 --- a/parser/src/io/consts.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub const INPUTS_FOLDER_PATH: &str = "./in"; -pub const OUTPUTS_FOLDER_PATH: &str = "./out"; diff --git a/parser/src/lib.rs b/parser/src/lib.rs deleted file mode 100644 index c0b5efd29..000000000 --- a/parser/src/lib.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod actions; -mod databases; -mod datasets; -mod io; -mod price; -mod states; -mod structs; -mod utils; - -pub use crate::{ - actions::iter_blocks, - databases::{AnyDatabase, Database}, - io::{Binary, Json, Serialization, COMPRESSED_BIN_EXTENSION, JSON_EXTENSION}, - structs::{ - Amount, Config, Date, DateMap, Exit, Height, HeightMap, MapChunkId, MapValue, - SerializedBTreeMap, SerializedVec, TxoutIndex, HEIGHT_MAP_CHUNK_SIZE, OHLC, - }, - utils::{create_rpc, log, reset_logs}, -}; diff --git a/parser/src/main.rs b/parser/src/main.rs deleted file mode 100644 index a95380569..000000000 --- a/parser/src/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::{thread::sleep, time::Duration}; - -use biter::bitcoincore_rpc::RpcApi; -use parser::{create_rpc, iter_blocks, log, reset_logs, Config, Exit}; - -fn main() -> color_eyre::Result<()> { - color_eyre::install()?; - - reset_logs(); - - let mut config = Config::import()?; - - let rpc = create_rpc(&config).unwrap(); - - let exit = Exit::new(); - - loop { - let block_count = rpc.get_blockchain_info().unwrap().blocks as usize; - - log(&format!("{block_count} blocks found.")); - - iter_blocks(&mut config, &rpc, block_count, exit.clone())?; - - if let Some(delay) = config.delay { - sleep(Duration::from_secs(delay)) - } - - log("Waiting for a new block...\n"); - - while block_count == rpc.get_blockchain_info().unwrap().blocks as usize { - sleep(Duration::from_secs(1)) - } - } - - // Ok(()) -} diff --git a/parser/src/states/_trait.rs b/parser/src/states/_trait.rs deleted file mode 100644 index 2b82fbd94..000000000 --- a/parser/src/states/_trait.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::{fmt::Debug, fs, io, path::Path}; - -use bincode::{Decode, Encode}; -use serde::{de::DeserializeOwned, Serialize}; - -use crate::{io::OUTPUTS_FOLDER_PATH, Serialization}; - -// https://github.com/djkoloski/rust_serialization_benchmark -pub trait AnyState -where - Self: Debug + Encode + Decode + Serialize + DeserializeOwned, -{ - fn name<'a>() -> &'a str; - - fn create_dir_all() -> color_eyre::Result<(), io::Error> { - fs::create_dir_all(Self::folder_path()) - } - - fn folder_path() -> String { - format!("{OUTPUTS_FOLDER_PATH}/states") - } - - fn full_path() -> String { - let name = Self::name(); - - let folder_path = Self::folder_path(); - - format!("{folder_path}/{name}") - } - - fn reset(&mut self) -> color_eyre::Result<(), io::Error> { - self.clear(); - - fs::remove_file(Self::full_path()) - } - - fn import() -> color_eyre::Result { - Self::create_dir_all()?; - - Serialization::Binary.import(Path::new(&Self::full_path())) - } - - fn export(&self) -> color_eyre::Result<()> { - Serialization::Binary.export(Path::new(&Self::full_path()), self) - } - - fn clear(&mut self); -} diff --git a/parser/src/utils/log.rs b/parser/src/utils/log.rs deleted file mode 100644 index d6593f75f..000000000 --- a/parser/src/utils/log.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::{ - fs::{self, OpenOptions}, - io::Write, -}; - -use chrono::Local; -use color_eyre::owo_colors::OwoColorize; - -const LOG_PATH: &str = "./.log"; - -pub fn reset_logs() { - let _ = fs::remove_file(LOG_PATH); -} - -#[inline(always)] -pub fn log(str: &str) { - let date_time = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S -")); - - str.lines() - .filter(|line| !line.is_empty()) - .for_each(|line| { - let mut file = OpenOptions::new() - .create(true) - .append(true) - .open(LOG_PATH) - .unwrap(); - - if let Err(e) = writeln!(file, "{} {}", date_time, line) { - eprintln!("Couldn't write to file: {}", e); - } - - println!("{} {}", date_time.bright_black(), line); - }); -} diff --git a/parser/run.sh b/run.sh similarity index 89% rename from parser/run.sh rename to run.sh index b82977f49..24531425a 100755 --- a/parser/run.sh +++ b/run.sh @@ -4,7 +4,7 @@ if command -v ulimit &> /dev/null; then echo "Increasing limit of opened files..." - ulimit -n 1000000 # Can't be $(ulimit -Hn), bitcoind needs some too ! + # ulimit -n 1000000 # Can't be $(ulimit -Hn), bitcoind needs some too ! # Needed because the datasets tree is too big lol echo "Increasing stack size..." diff --git a/server/Cargo.toml b/server/Cargo.toml deleted file mode 100644 index 92c9921f3..000000000 --- a/server/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "server" -version = "0.5.0" -edition = "2021" - -[dependencies] -axum = "0.7.7" -bincode = { git = "https://github.com/bincode-org/bincode.git" } -chrono = "0.4.38" -color-eyre = "0.6.3" -derive_deref = "1.1.1" -itertools = "0.13.0" -parser = { path = "../parser" } -regex = "1.11.0" -reqwest = { version = "0.12.8", features = ["json"] } -serde = { version = "1.0.210", features = ["derive"] } -serde_json = { version = "1.0.128" } -swc = "0.289.1" -swc_common = "0.40.1" -tokio = { version = "1.40.0", features = ["full"] } -tower-http = { version = "0.6.1", features = ["compression-full"] } -# oxc = { version = "0.34.0", features = ["codegen", "minifier"] } diff --git a/server/restart-cloudflared.sh b/server/restart-cloudflared.sh deleted file mode 100755 index fa1695f7a..000000000 --- a/server/restart-cloudflared.sh +++ /dev/null @@ -1,4 +0,0 @@ -sudo launchctl stop com.cloudflare.cloudflared -sudo launchctl unload /Library/LaunchDaemons/com.cloudflare.cloudflared.plist -sudo launchctl load /Library/LaunchDaemons/com.cloudflare.cloudflared.plist -sudo launchctl start com.cloudflare.cloudflared diff --git a/server/run.sh b/server/run.sh deleted file mode 100755 index 77a22baac..000000000 --- a/server/run.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -if cargo watch --help &> /dev/null; then - TRIGGER="./in/datasets_len.txt" - - if [ ! -f "$TRIGGER" ]; then - mkdir "./in" - echo "0" > $TRIGGER - fi - - cargo watch --no-vcs-ignores -w "./src" -w "$TRIGGER" -x "run -r" -else - cargo run -r -fi diff --git a/server/src/api/structs/routes.rs b/server/src/api/structs/routes.rs deleted file mode 100644 index fc33fbf5d..000000000 --- a/server/src/api/structs/routes.rs +++ /dev/null @@ -1,157 +0,0 @@ -use std::{ - collections::{BTreeMap, HashMap}, - fs, - path::{Path, PathBuf}, -}; - -use derive_deref::{Deref, DerefMut}; -use itertools::Itertools; -use parser::{Json, Serialization}; - -use crate::Grouped; - -use super::Paths; - -#[derive(Clone, Debug)] -pub struct Route { - pub url_path: String, - pub file_path: PathBuf, - pub values_type: String, - pub serialization: Serialization, -} - -#[derive(Clone, Default, Deref, DerefMut)] -pub struct Routes(pub Grouped>); - -const INPUTS_PATH: &str = "./in"; -const WEBSITE_TYPES_PATH: &str = "../website/scripts/types"; - -impl Routes { - pub fn build() -> Self { - let path_to_type: BTreeMap = - Json::import(Path::new(&format!("{INPUTS_PATH}/disk_path_to_type.json"))).unwrap(); - - let mut routes = Routes::default(); - - path_to_type.into_iter().for_each(|(key, value)| { - let mut split_key = key.split('/').collect_vec(); - let last = split_key.pop().unwrap().to_owned(); - - let mut skip = 2; - - let mut serialization = Serialization::Binary; - - if *split_key.get(1).unwrap() == "price" { - skip = 1; - serialization = Serialization::Json; - } - - let mut split_key = split_key.iter().skip(skip).collect_vec(); - - // Use case for: "../datasets/last": "Value", - if split_key.is_empty() { - split_key.push(&"last"); - } - - let map_key = split_key.iter().join("_"); - - let url_path = split_key.iter().join("-"); - - let file_path = PathBuf::from(key.to_owned()); - let values_type = value.to_owned(); - - if last == "date" { - routes.date.insert( - map_key, - Route { - url_path: format!("date-to-{url_path}"), - file_path, - values_type, - serialization, - }, - ); - } else if last == "height" { - routes.height.insert( - map_key, - Route { - url_path: format!("height-to-{url_path}"), - file_path, - values_type, - serialization, - }, - ); - } else if last == "last" { - routes.last.insert( - map_key, - Route { - url_path, - file_path, - values_type, - serialization, - }, - ); - } else { - dbg!(&key, value, &last); - panic!("") - } - }); - - routes - } - - pub fn generate_dts_file(&self) { - let map_to_type = |name: &str, map: &HashMap| -> String { - let paths = map - .values() - .map(|route| format!("\"{}\"", route.url_path)) - .join(" | "); - - format!("export type {}Path = {};\n", name, paths) - }; - - let date_type = map_to_type("Date", &self.date); - - let height_type = map_to_type("Height", &self.height); - - let last_type = map_to_type("Last", &self.last); - - fs::write( - format!("{WEBSITE_TYPES_PATH}/paths.d.ts"), - format!("// This file is auto generated by the server\n// Manual changes are forbidden\n\n{date_type}\n{height_type}\n{last_type}"), - ) - .unwrap(); - } - - pub fn to_full_paths(&self, host: String) -> Paths { - let url = { - let scheme = if host.contains("0.0.0.0") || host.contains("localhost") { - "http" - } else { - "https" - }; - - format!("{scheme}://{host}") - }; - - let transform = |map: &HashMap| -> BTreeMap { - map.iter() - .map(|(key, route)| { - ( - key.to_owned(), - format!("{url}/api/{}", route.url_path.to_owned()), - ) - }) - .collect() - }; - - let date_paths = transform(&self.date); - let height_paths = transform(&self.height); - let last_paths = transform(&self.last); - - Paths(Grouped { - date: date_paths, - height: height_paths, - last: last_paths, - }) - } -} diff --git a/parser/src/io/binary.rs b/src/io/binary.rs similarity index 100% rename from parser/src/io/binary.rs rename to src/io/binary.rs diff --git a/parser/src/io/json.rs b/src/io/json.rs similarity index 100% rename from parser/src/io/json.rs rename to src/io/json.rs diff --git a/parser/src/io/mod.rs b/src/io/mod.rs similarity index 77% rename from parser/src/io/mod.rs rename to src/io/mod.rs index 674784567..a0540ad92 100644 --- a/parser/src/io/mod.rs +++ b/src/io/mod.rs @@ -1,9 +1,7 @@ mod binary; -mod consts; mod json; mod serialization; pub use binary::*; -pub use consts::*; pub use json::*; pub use serialization::*; diff --git a/parser/src/io/serialization.rs b/src/io/serialization.rs similarity index 89% rename from parser/src/io/serialization.rs rename to src/io/serialization.rs index e9cd8137d..9dea77275 100644 --- a/parser/src/io/serialization.rs +++ b/src/io/serialization.rs @@ -1,4 +1,8 @@ -use std::{fmt::Debug, fs, path::Path}; +use std::{ + fmt::Debug, + fs, + path::{Path, PathBuf}, +}; use allocative::Allocative; use bincode::{Decode, Encode}; @@ -27,17 +31,6 @@ impl Serialization { } } - pub fn from_path(path: &Path) -> Self { - let path = path.to_str().unwrap(); - if path.ends_with(BIN_EXTENSION) || path.ends_with(COMPRESSED_BIN_EXTENSION) { - Self::Binary - } else if path.ends_with(JSON_EXTENSION) || path.ends_with(HAR_EXTENSION) { - Self::Json - } else { - panic!("Extension \"{path}\" isn't supported") - } - } - pub fn import(&self, path: &Path) -> color_eyre::Result where T: Debug + DeserializeOwned + Decode, @@ -119,3 +112,17 @@ impl Serialization { } } } + +impl TryFrom<&PathBuf> for Serialization { + type Error = (); + + fn try_from(path: &PathBuf) -> Result { + let extension = path.extension().ok_or(())?.to_str().unwrap(); + + match extension { + BIN_EXTENSION | COMPRESSED_BIN_EXTENSION => Ok(Self::Binary), + JSON_EXTENSION => Ok(Self::Json), + _ => Err(()), + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 000000000..97bdef558 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,70 @@ +use biter::bitcoincore_rpc::Client; +use log::info; +use rlimit::{getrlimit, setrlimit, Resource}; + +mod io; +mod parser; +mod server; +mod structs; +mod utils; + +use parser::{Databases, Datasets}; +use structs::{Config, Exit}; +use utils::init_log; + +fn main() -> color_eyre::Result<()> { + color_eyre::install()?; + + init_log(); + + let (_, nofile_limit) = getrlimit(Resource::NOFILE).unwrap(); + setrlimit(Resource::NOFILE, 138_240, nofile_limit)?; + + std::thread::Builder::new() + .stack_size(getrlimit(Resource::STACK).unwrap().1 as usize) + .spawn(|| -> color_eyre::Result<()> { + let exit = Exit::new(); + + let config = Config::import()?; + + info!("Starting..."); + + let rpc = Client::from(&config); + + let databases = Databases::import(&config); + + let datasets = Datasets::import(&config)?; + + let paths_to_type = datasets.get_paths_to_type(&config); + + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + let config_clone = config.clone(); + let run_parser = config.parser(); + let run_server = config.server(); + + let handle = tokio::spawn(async move { + if run_server { + server::main(paths_to_type, &config_clone).await.unwrap(); + } else { + info!("Skipping server"); + } + }); + + if run_parser { + parser::main(&config, &rpc, &exit, databases, datasets)?; + } else { + info!("Skipping parser"); + } + + handle.await?; + + Ok(()) + }) + })? + .join() + .unwrap() +} diff --git a/parser/src/actions/export.rs b/src/parser/actions/export.rs similarity index 52% rename from parser/src/actions/export.rs rename to src/parser/actions/export.rs index 88af14a76..f43d8e3be 100644 --- a/parser/src/actions/export.rs +++ b/src/parser/actions/export.rs @@ -1,52 +1,62 @@ use std::thread::{self}; +use log::info; + use crate::{ - databases::Databases, - datasets::AllDatasets, - states::States, - structs::{Date, Height}, - utils::{log, time}, - Exit, + parser::{databases::Databases, datasets::Datasets, states::States}, + structs::{Config, Date, Exit, Height}, + utils::time, }; pub struct ExportedData<'a> { + pub config: &'a Config, pub databases: Option<&'a mut Databases>, - pub datasets: &'a mut AllDatasets, + pub datasets: &'a mut Datasets, pub date: Date, + pub defragment: bool, + pub exit: Exit, pub height: Height, pub states: Option<&'a States>, - pub exit: Exit, } pub fn export( ExportedData { + config, databases, datasets, - states, - height, date, + defragment, exit, + height, + states, }: ExportedData, ) -> color_eyre::Result<()> { if exit.active() { - log("Exit in progress, skipping export"); + info!("Exit in progress, skipping export"); return Ok(()); } exit.block(); - log("Exporting..."); + info!("Exporting..."); + if defragment { + info!("Will also defragment databases, please be patient it might take a while") + } time("Total save time", || -> color_eyre::Result<()> { - time("Datasets saved", || datasets.export())?; + time("Datasets saved", || datasets.export(config))?; thread::scope(|s| { if let Some(databases) = databases { - s.spawn(|| time("Databases saved", || databases.export(height, date))); + s.spawn(|| { + time("Databases saved", || { + databases.export(height, date, defragment) + }) + }); } if let Some(states) = states { - s.spawn(|| time("States saved", || states.export())); + s.spawn(|| time("States saved", || states.export(config))); } }); diff --git a/parser/src/actions/iter_blocks.rs b/src/parser/actions/iter_blocks.rs similarity index 78% rename from parser/src/actions/iter_blocks.rs rename to src/parser/actions/iter_blocks.rs index aaf46e35e..84e5a4af4 100644 --- a/parser/src/actions/iter_blocks.rs +++ b/src/parser/actions/iter_blocks.rs @@ -1,63 +1,52 @@ use std::{collections::BTreeSet, time::Instant}; +use biter::bitcoincore_rpc::Client; +use chrono::Datelike; use export::ExportedData; use itertools::Itertools; +use log::info; use parse::ParseData; use crate::{ - actions::{export, find_first_inserted_unsafe_height, parse}, - create_rpc, - databases::Databases, - datasets::{AllDatasets, ComputeData}, - io::OUTPUTS_FOLDER_PATH, - states::{AddressCohortsDurableStates, States, UTXOCohortsDurableStates}, - structs::{DateData, MapKey, Timestamp}, - utils::{generate_allocation_files, log, time}, - Config, Exit, Height, + parser::{ + actions::{export, find_first_inserted_unsafe_height, parse}, + databases::Databases, + datasets::{ComputeData, Datasets}, + states::{AddressCohortsDurableStates, States, UTXOCohortsDurableStates}, + }, + structs::{Config, DateData, Exit, Height, MapKey, Timestamp}, + utils::{generate_allocation_files, time}, }; pub fn iter_blocks( - config: &mut Config, + config: &Config, rpc: &biter::bitcoincore_rpc::Client, approx_block_count: usize, exit: Exit, + databases: &mut Databases, + datasets: &mut Datasets, ) -> color_eyre::Result<()> { - log("Starting..."); + let mut states = States::import(config).unwrap_or_default(); - let mut datasets = AllDatasets::import(config)?; - - log("Imported datasets"); - - let mut databases = Databases::import(); - - if config.first_defragment() { - databases.defragment(&exit); - config.disable_defragment(); - } - - log("Imported databases"); - - let mut states = States::import().unwrap_or_default(); - - log("Imported states"); + info!("Imported states"); let first_unsafe_heights = - find_first_inserted_unsafe_height(&mut states, &mut databases, &mut datasets); + find_first_inserted_unsafe_height(&mut states, databases, datasets, config); let mut height = first_unsafe_heights.min(); - log(&format!("Starting parsing at height: {height}")); + info!("Starting parsing at height: {height}"); let mut next_block_opt = None; let mut blocks_loop_date = None; + let mut next_date_opt; let block_receiver = biter::new( - config.datadir.as_ref().unwrap(), - OUTPUTS_FOLDER_PATH, + &config.path_bitcoindir(), Some(height.to_usize()), None, - create_rpc(config).unwrap(), + Client::from(config), ); let mut block_iter = block_receiver.iter(); @@ -93,15 +82,13 @@ pub fn iter_blocks( panic!() } - let next_block_date = next_block_opt.as_ref().map(|(_, next_block, _)| { + next_date_opt = next_block_opt.as_ref().map(|(_, next_block, _)| { Timestamp::wrap(next_block.header.time).to_date() }); // Always run for the first block of the loop if blocks_loop_date.is_none() { - log(&format!( - "Processing {current_block_date} (height: {height})..." - )); + info!("Processing {current_block_date} (height: {height})..."); blocks_loop_date.replace(current_block_date); @@ -125,7 +112,7 @@ pub fn iter_blocks( panic!("current block should always have the same date as the current blocks loop"); } - let is_date_last_block = next_block_date + let is_date_last_block = next_date_opt // Do NOT change `blocks_loop_date` to `current_block_date` !!! .map_or(true, |next_block_date| blocks_loop_date < next_block_date); @@ -159,16 +146,17 @@ pub fn iter_blocks( } parse(ParseData { - rpc, block: current_block, block_index: blocks_loop_i, compute_addresses, - databases: &mut databases, - datasets: &mut datasets, + config, + databases, + datasets, date: blocks_loop_date, first_date_height: height, height: current_block_height, is_date_last_block, + rpc, states: &mut states, }); } @@ -178,7 +166,7 @@ pub fn iter_blocks( if is_date_last_block { height += blocks_loop_i; - let is_check_point = next_block_date + let is_check_point = next_date_opt .as_ref() .map_or(true, |date| date.is_first_of_month()); @@ -201,13 +189,13 @@ pub fn iter_blocks( // Don't remember why -1 let last_height = height - 1_u32; - log(&format!( - "Parsing group took {} seconds (last height: {last_height})\n", + info!( + "Parsing group took {} seconds (last height: {last_height})", instant.elapsed().as_secs_f32(), - )); + ); if first_unsafe_heights.computed <= last_height { - log("Computing datasets..."); + info!("Computing datasets..."); time("Computing datasets", || { let dates = processed_dates.into_iter().collect_vec(); @@ -223,10 +211,17 @@ pub fn iter_blocks( if !config.dry_run() { let is_safe = height.is_safe(approx_block_count); + let defragment = is_safe + && next_date_opt.is_some_and(|date| { + date.year() >= 2020 && date.is_january() && date.is_first_of_month() + }); + export(ExportedData { - databases: is_safe.then_some(&mut databases), - datasets: &mut datasets, + config, + databases: is_safe.then_some(databases), + datasets, date: blocks_loop_date.unwrap(), + defragment, height: last_height, states: is_safe.then_some(&states), exit: exit.clone(), @@ -234,11 +229,11 @@ pub fn iter_blocks( if config.record_ram_usage() { time("Exporing allocation files", || { - generate_allocation_files(&datasets, &databases, &states, last_height) + generate_allocation_files(datasets, databases, &states, last_height) })?; } } else { - log("Skipping export"); + info!("Skipping export"); } println!(); diff --git a/parser/src/actions/min_height.rs b/src/parser/actions/min_height.rs similarity index 84% rename from parser/src/actions/min_height.rs rename to src/parser/actions/min_height.rs index 953c1ddf1..eb767c08b 100644 --- a/parser/src/actions/min_height.rs +++ b/src/parser/actions/min_height.rs @@ -1,9 +1,12 @@ +use log::info; + use crate::{ - databases::Databases, - datasets::{AllDatasets, AnyDatasets}, - states::States, - structs::Height, - utils::log, + parser::{ + databases::Databases, + datasets::{AnyDatasets, Datasets}, + states::States, + }, + structs::{Config, Height}, }; #[derive(Default, Debug)] @@ -21,7 +24,8 @@ impl Heights { pub fn find_first_inserted_unsafe_height( states: &mut States, databases: &mut Databases, - datasets: &mut AllDatasets, + datasets: &mut Datasets, + config: &Config, ) -> Heights { let min_initial_inserted_last_address_height = datasets .address @@ -51,7 +55,7 @@ pub fn find_first_inserted_unsafe_height( .map(|date_data| date_data.date) .and_then(|last_safe_date| { if !usable_databases { - log("Unusable databases"); + info!("Unusable databases"); return None; } @@ -61,8 +65,8 @@ pub fn find_first_inserted_unsafe_height( let min_datasets_inserted_last_height = datasets_min_initial_states.inserted.last_height; let min_datasets_inserted_last_date = datasets_min_initial_states.inserted.last_date; - log(&format!("min_datasets_inserted_last_height: {:?}", min_datasets_inserted_last_height)); - log(&format!("min_datasets_inserted_last_date: {:?}", min_datasets_inserted_last_date)); + info!("min_datasets_inserted_last_height: {:?}", min_datasets_inserted_last_height); + info!("min_datasets_inserted_last_date: {:?}", min_datasets_inserted_last_date); let inserted_last_date_is_older_than_saved_state = min_datasets_inserted_last_date.map_or(true, |min_datasets_last_date| min_datasets_last_date < last_safe_date); @@ -80,7 +84,7 @@ pub fn find_first_inserted_unsafe_height( let inserted_heights_and_dates_are_out_of_sync = min_datasets_inserted_last_height.map_or(true, |min_datasets_inserted_last_height| min_datasets_inserted_last_height < last_safe_height); if inserted_heights_and_dates_are_out_of_sync { - log(&format!("last_safe_height ({last_safe_height}) > min_datasets_height ({min_datasets_inserted_last_height:?})")); + info!("last_safe_height ({last_safe_height}) > min_datasets_height ({min_datasets_inserted_last_height:?})"); None } else { @@ -108,7 +112,7 @@ pub fn find_first_inserted_unsafe_height( ) }) .unwrap_or_else(|| { - log("Starting over..."); + info!("Starting over..."); let include_addresses = !usable_databases || min_initial_inserted_last_address_date.is_none() @@ -119,7 +123,7 @@ pub fn find_first_inserted_unsafe_height( // panic!(""); // } - states.reset(include_addresses); + states.reset(config, include_addresses); databases.reset(include_addresses); diff --git a/parser/src/actions/mod.rs b/src/parser/actions/mod.rs similarity index 100% rename from parser/src/actions/mod.rs rename to src/parser/actions/mod.rs diff --git a/parser/src/actions/parse.rs b/src/parser/actions/parse.rs similarity index 98% rename from parser/src/actions/parse.rs rename to src/parser/actions/parse.rs index 2199a5c3b..648bfb53e 100644 --- a/parser/src/actions/parse.rs +++ b/src/parser/actions/parse.rs @@ -9,18 +9,21 @@ use itertools::Itertools; use rayon::prelude::*; use crate::{ - databases::{ - AddressIndexToAddressData, AddressIndexToEmptyAddressData, AddressToAddressIndex, - Databases, TxidToTxData, TxoutIndexToAddressIndex, TxoutIndexToAmount, - }, - datasets::{AllDatasets, InsertData}, - states::{ - AddressCohortsInputStates, AddressCohortsOutputStates, AddressCohortsRealizedStates, - States, UTXOCohortsOneShotStates, UTXOCohortsSentStates, + parser::{ + databases::{ + AddressIndexToAddressData, AddressIndexToEmptyAddressData, AddressToAddressIndex, + Databases, TxidToTxData, TxoutIndexToAddressIndex, TxoutIndexToAmount, + }, + datasets::{Datasets, InsertData}, + states::{ + AddressCohortsInputStates, AddressCohortsOutputStates, AddressCohortsRealizedStates, + States, UTXOCohortsOneShotStates, UTXOCohortsSentStates, + }, }, structs::{ - Address, AddressData, AddressRealizedData, Amount, BlockData, BlockPath, Counter, Date, - EmptyAddressData, Height, PartialTxoutData, Price, SentData, Timestamp, TxData, TxoutIndex, + Address, AddressData, AddressRealizedData, Amount, BlockData, BlockPath, Config, Counter, + Date, EmptyAddressData, Height, PartialTxoutData, Price, SentData, Timestamp, TxData, + TxoutIndex, }, }; @@ -28,9 +31,10 @@ pub struct ParseData<'a> { // pub bitcoin_cli: &'a BitcoinCli, pub block: Block, pub block_index: usize, + pub config: &'a Config, pub compute_addresses: bool, pub databases: &'a mut Databases, - pub datasets: &'a mut AllDatasets, + pub datasets: &'a mut Datasets, pub date: Date, pub first_date_height: Height, pub height: Height, @@ -43,6 +47,7 @@ pub fn parse( ParseData { block, block_index, + config, compute_addresses, databases, datasets, @@ -72,7 +77,7 @@ pub fn parse( let block_price = Price::from_dollar( datasets .price - .get_height_ohlc(height, timestamp, previous_timestamp) + .get_height_ohlc(height, timestamp, previous_timestamp, config) .unwrap_or_else(|_| panic!("Expect {height} to have a price")) .close as f64, ); diff --git a/parser/src/databases/_database.rs b/src/parser/databases/_database.rs similarity index 76% rename from parser/src/databases/_database.rs rename to src/parser/databases/_database.rs index abc695f56..bd61f1078 100644 --- a/parser/src/databases/_database.rs +++ b/src/parser/databases/_database.rs @@ -1,35 +1,35 @@ +// https://docs.rs/sanakirja/latest/sanakirja/index.html +// https://pijul.org/posts/2021-02-06-rethinking-sanakirja/ + use std::{ collections::{BTreeMap, BTreeSet}, fmt::Debug, - fs, mem, + fs, io, mem, path::PathBuf, }; use allocative::Allocative; -// https://docs.rs/sanakirja/latest/sanakirja/index.html -// https://pijul.org/posts/2021-02-06-rethinking-sanakirja/ -// -// Seems indeed much faster than ReDB and LMDB (heed) -// But a lot has changed code wise between them so a retest wouldn't hurt -// -// Possible compression: https://pijul.org/posts/sanakirja-zstd/ use sanakirja::{ btree::{self, page, Db_, Iter}, Commit, Env, Error, MutTxn, RootDb, Storable, }; +/// +/// Simple wrapper around Sanakirja Database with cached puts and dels for safe use outside exports. +/// +/// There is no `cached_gets` since it's much cheaper and faster to do a parallel search first using `unsafe_get` than caching "gets" along the way. +/// #[derive(Allocative)] #[allocative(bound = "Key: Allocative, Value: Allocative")] -/// There is no `cached_gets` since it's much cheaper and faster to do a parallel search first using `unsafe_get` than caching gets along the way. pub struct Database where Key: Ord + Clone + Debug + Storable, Value: Storable + PartialEq, { - pub cached_puts: BTreeMap, - pub cached_dels: BTreeSet, path: PathBuf, + cached_puts: BTreeMap, + cached_dels: BTreeSet, #[allocative(skip)] db: Db_>, #[allocative(skip)] @@ -62,11 +62,12 @@ where }) } + #[inline] pub fn iter(&self) -> Iter<'_, MutTxn, Key, Value, page::Page> { btree::iter(&self.txn, &self.db, None).unwrap() } - pub fn iter_collect(&self) -> BTreeMap + pub fn collect(&self) -> BTreeMap where Value: Clone, { @@ -76,6 +77,7 @@ where .collect::<_>() } + #[inline] pub fn get(&self, key: &Key) -> Option<&Value> { if let Some(cached_put) = self.get_from_puts(key) { return Some(cached_put); @@ -84,6 +86,7 @@ where self.db_get(key) } + #[inline] pub fn db_get(&self, key: &Key) -> Option<&Value> { let option = btree::get(&self.txn, &self.db, key, None).unwrap(); @@ -96,17 +99,17 @@ where None } - #[inline(always)] + #[inline] pub fn get_from_puts(&self, key: &Key) -> Option<&Value> { self.cached_puts.get(key) } - #[inline(always)] + #[inline] pub fn get_mut_from_puts(&mut self, key: &Key) -> Option<&mut Value> { self.cached_puts.get_mut(key) } - #[inline(always)] + #[inline] pub fn remove(&mut self, key: &Key) -> Option { self.remove_from_puts(key).or_else(|| { self.db_remove(key); @@ -139,7 +142,6 @@ where #[inline] pub fn insert(&mut self, key: Key, value: Value) -> Option { self.cached_dels.remove(&key); - self.unsafe_insert(key, value) } @@ -165,12 +167,11 @@ where } pub trait AnyDatabase { - fn export(self) -> color_eyre::Result<(), Error>; - fn boxed_export(self: Box) -> color_eyre::Result<(), Error>; #[allow(unused)] - fn defragment(self); - fn boxed_defragment(self: Box); - fn destroy(self); + fn export(self, defragment: bool) -> color_eyre::Result<(), Error>; + fn boxed_export(self: Box, defragment: bool) -> color_eyre::Result<(), Error>; + #[allow(unused)] + fn destroy(self) -> io::Result<()>; } impl AnyDatabase for Database @@ -178,11 +179,31 @@ where Key: Ord + Clone + Debug + Storable, Value: Storable + PartialEq + Clone, { - fn export(self) -> color_eyre::Result<(), Error> { - Box::new(self).boxed_export() + fn export(self, defragment: bool) -> color_eyre::Result<(), Error> { + Box::new(self).boxed_export(defragment) } - fn boxed_export(mut self: Box) -> color_eyre::Result<(), Error> { + fn boxed_export(mut self: Box, defragment: bool) -> color_eyre::Result<(), Error> { + if defragment { + let mut btree = self.as_ref().collect(); + + let path = self.path.to_owned(); + self.cached_dels.iter().for_each(|key| { + btree.remove(key); + }); + btree.append(&mut self.cached_puts); + + self.destroy()?; + + *self = Self::open(path).unwrap(); + + if !self.is_empty() { + panic!() + } + + self.cached_puts = btree; + } + if self.cached_dels.is_empty() && self.cached_puts.is_empty() { return Ok(()); } @@ -198,35 +219,11 @@ where self.txn.commit() } - fn defragment(self) { - Box::new(self).boxed_defragment() - } - - fn boxed_defragment(self: Box) { - let btree = self.iter_collect(); - - let path = self.path.to_owned(); - - self.destroy(); - - let mut db = Self::open(path).unwrap(); - - if !db.is_empty() { - panic!() - } - - db.cached_puts = btree; - db.export().unwrap(); - } - - fn destroy(self) { + fn destroy(self) -> io::Result<()> { let path = self.path.to_owned(); drop(self); - fs::remove_file(&path).unwrap_or_else(|_| { - dbg!(path); - panic!("Error"); - }); + fs::remove_file(&path) } } diff --git a/src/parser/databases/_trait.rs b/src/parser/databases/_trait.rs new file mode 100644 index 000000000..f238c82b1 --- /dev/null +++ b/src/parser/databases/_trait.rs @@ -0,0 +1,59 @@ +use std::{fs, io, path::Path}; + +use log::info; + +use crate::structs::{Config, Date, Height}; + +use super::{AnyDatabase, Metadata}; + +pub trait AnyDatabaseGroup +where + Self: Sized, +{ + fn init(config: &Config) -> Self { + let s = Self::import(config); + s.create_dir_all().unwrap(); + s + } + + fn import(config: &Config) -> Self; + + fn drain_to_vec(&mut self) -> Vec>; + fn open_all(&mut self); + + fn metadata(&mut self) -> &mut Metadata; + fn export_metadata(&mut self, height: Height, date: Date) -> color_eyre::Result<()> { + self.metadata().export(height, date) + } + + fn create_dir_all(&self) -> color_eyre::Result<(), std::io::Error> { + fs::create_dir_all(self.path()) + } + + fn remove_dir_all(&self) -> color_eyre::Result<(), io::Error> { + fs::remove_dir_all(self.path()) + } + + fn reset(&mut self) -> color_eyre::Result<(), io::Error> { + info!( + "Reset {}", + self.path() + .components() + .last() + .unwrap() + .as_os_str() + .to_str() + .unwrap() + ); + + self.reset_metadata(); + self.remove_dir_all()?; + self.create_dir_all()?; + + Ok(()) + } + + fn reset_metadata(&mut self); + + fn path(&self) -> &Path; +} diff --git a/parser/src/databases/address_index_to_address_data.rs b/src/parser/databases/address_index_to_address_data.rs similarity index 86% rename from parser/src/databases/address_index_to_address_data.rs rename to src/parser/databases/address_index_to_address_data.rs index e6ed7fc80..82c90acf0 100644 --- a/parser/src/databases/address_index_to_address_data.rs +++ b/src/parser/databases/address_index_to_address_data.rs @@ -2,6 +2,7 @@ use std::{ collections::BTreeMap, fs, mem, ops::{Deref, DerefMut}, + path::{Path, PathBuf}, }; use allocative::Allocative; @@ -9,8 +10,8 @@ use itertools::Itertools; use rayon::prelude::*; use crate::{ - states::AddressCohortsDurableStates, - structs::{AddressData, Date, Height}, + parser::states::AddressCohortsDurableStates, + structs::{AddressData, Config}, utils::time, }; @@ -22,8 +23,8 @@ type Database = _Database; #[derive(Allocative)] pub struct AddressIndexToAddressData { + path: PathBuf, pub metadata: Metadata, - pub map: BTreeMap, } @@ -72,6 +73,7 @@ impl AddressIndexToAddressData { pub fn open_db(&mut self, key: &Key) -> &mut Database { let db_index = Self::db_index(key); + let path = self.path().to_owned(); self.entry(db_index).or_insert_with(|| { let db_name = format!( @@ -80,7 +82,7 @@ impl AddressIndexToAddressData { (db_index + 1) * ADDRESS_INDEX_DB_MAX_SIZE ); - let path = Self::root().join(db_name); + let path = path.join(db_name); Database::open(path).unwrap() }) @@ -113,30 +115,23 @@ impl AddressIndexToAddressData { } impl AnyDatabaseGroup for AddressIndexToAddressData { - fn import() -> Self { + fn import(config: &Config) -> Self { + let path = config + .path_databases() + .join("address_index_to_address_data"); Self { - metadata: Metadata::import(Self::root(), 1), - + metadata: Metadata::import(&path, 1), + path, map: BTreeMap::default(), } } - fn create_dir_all(&self) -> color_eyre::Result<(), std::io::Error> { - fs::create_dir_all(Self::root()) - } - fn reset_metadata(&mut self) { self.metadata.reset(); } - fn folder<'a>() -> &'a str { - "address_index_to_address_data" - } - fn open_all(&mut self) { - let path = Self::root(); - - let folder = fs::read_dir(path); + let folder = fs::read_dir(&self.path); if folder.is_err() { return; @@ -167,7 +162,11 @@ impl AnyDatabaseGroup for AddressIndexToAddressData { .collect_vec() } - fn export_metadata(&mut self, height: Height, date: Date) -> color_eyre::Result<()> { - self.metadata.export(height, date) + fn metadata(&mut self) -> &mut Metadata { + &mut self.metadata + } + + fn path(&self) -> &Path { + &self.path } } diff --git a/parser/src/databases/address_index_to_empty_address_data.rs b/src/parser/databases/address_index_to_empty_address_data.rs similarity index 84% rename from parser/src/databases/address_index_to_empty_address_data.rs rename to src/parser/databases/address_index_to_empty_address_data.rs index cdb0127fc..923ae4850 100644 --- a/parser/src/databases/address_index_to_empty_address_data.rs +++ b/src/parser/databases/address_index_to_empty_address_data.rs @@ -2,12 +2,13 @@ use std::{ collections::BTreeMap, fs, mem, ops::{Deref, DerefMut}, + path::{Path, PathBuf}, }; use allocative::Allocative; use itertools::Itertools; -use crate::structs::{Date, EmptyAddressData, Height}; +use crate::structs::{Config, EmptyAddressData}; use super::{ AnyDatabase, AnyDatabaseGroup, Database as _Database, Metadata, ADDRESS_INDEX_DB_MAX_SIZE, @@ -19,8 +20,8 @@ type Database = _Database; #[derive(Allocative)] pub struct AddressIndexToEmptyAddressData { + path: PathBuf, pub metadata: Metadata, - map: BTreeMap, } @@ -72,6 +73,7 @@ impl AddressIndexToEmptyAddressData { pub fn open_db(&mut self, key: &Key) -> &mut Database { let db_index = Self::db_index(key); + let path = self.path.to_owned(); self.entry(db_index).or_insert_with(|| { let db_name = format!( @@ -80,7 +82,7 @@ impl AddressIndexToEmptyAddressData { (db_index + 1) * ADDRESS_INDEX_DB_MAX_SIZE ); - let path = Self::root().join(db_name); + let path = path.join(db_name); Database::open(path).unwrap() }) @@ -92,30 +94,23 @@ impl AddressIndexToEmptyAddressData { } impl AnyDatabaseGroup for AddressIndexToEmptyAddressData { - fn import() -> Self { + fn import(config: &Config) -> Self { + let path = config + .path_databases() + .join("address_index_to_empty_address_data"); Self { - metadata: Metadata::import(Self::root(), 1), - + metadata: Metadata::import(&path, 1), + path, map: BTreeMap::default(), } } - fn create_dir_all(&self) -> color_eyre::Result<(), std::io::Error> { - fs::create_dir_all(Self::root()) - } - fn reset_metadata(&mut self) { self.metadata.reset(); } - fn folder<'a>() -> &'a str { - "address_index_to_empty_address_data" - } - fn open_all(&mut self) { - let path = Self::root(); - - let folder = fs::read_dir(path); + let folder = fs::read_dir(&self.path); if folder.is_err() { return; @@ -146,7 +141,11 @@ impl AnyDatabaseGroup for AddressIndexToEmptyAddressData { .collect_vec() } - fn export_metadata(&mut self, height: Height, date: Date) -> color_eyre::Result<()> { - self.metadata.export(height, date) + fn metadata(&mut self) -> &mut Metadata { + &mut self.metadata + } + + fn path(&self) -> &Path { + &self.path } } diff --git a/parser/src/databases/address_to_address_index.rs b/src/parser/databases/address_to_address_index.rs similarity index 86% rename from parser/src/databases/address_to_address_index.rs rename to src/parser/databases/address_to_address_index.rs index 34b7e8d5b..35d9b7e30 100644 --- a/parser/src/databases/address_to_address_index.rs +++ b/src/parser/databases/address_to_address_index.rs @@ -7,7 +7,7 @@ use std::{ use allocative::Allocative; use itertools::Itertools; -use crate::structs::{Address, Date, Height, U8x19, U8x31}; +use crate::structs::{Address, Config, U8x19, U8x31}; use super::{AnyDatabase, AnyDatabaseGroup, Database, Metadata}; @@ -30,6 +30,7 @@ type MultisigDatabase = U32Database; #[derive(Allocative)] pub struct AddressToAddressIndex { + path: PathBuf, pub metadata: Metadata, p2pk: BTreeMap, @@ -160,12 +161,12 @@ impl AddressToAddressIndex { .collect_vec() } - fn path_p2pk() -> PathBuf { - Self::root().join("p2pk") + fn path_p2pk(&self) -> PathBuf { + self.path().join("p2pk") } pub fn open_p2pk(&mut self, prefix: u16) -> &mut P2PKDatabase { - let path = Self::path_p2pk(); + let path = self.path_p2pk(); self.p2pk.entry(prefix).or_insert_with(|| { let path = path.join(prefix.to_string()); Database::open(path).unwrap() @@ -173,7 +174,7 @@ impl AddressToAddressIndex { } fn open_all_p2pk(&mut self) { - let path = Self::path_p2pk(); + let path = self.path_p2pk(); Self::path_to_group_prefixes(&path) .into_iter() .for_each(|prefix| { @@ -184,12 +185,12 @@ impl AddressToAddressIndex { }); } - fn path_p2pkh() -> PathBuf { - Self::root().join("p2pkh") + fn path_p2pkh(&self) -> PathBuf { + self.path().join("p2pkh") } pub fn open_p2pkh(&mut self, prefix: u16) -> &mut P2PKHDatabase { - let path = Self::path_p2pkh(); + let path = self.path_p2pkh(); self.p2pkh.entry(prefix).or_insert_with(|| { let path = path.join(prefix.to_string()); @@ -198,7 +199,7 @@ impl AddressToAddressIndex { } fn open_all_p2pkh(&mut self) { - let path = Self::path_p2pkh(); + let path = self.path_p2pkh(); Self::path_to_group_prefixes(&path) .into_iter() .for_each(|prefix| { @@ -209,12 +210,12 @@ impl AddressToAddressIndex { }); } - fn path_p2sh() -> PathBuf { - Self::root().join("p2sh") + fn path_p2sh(&self) -> PathBuf { + self.path().join("p2sh") } pub fn open_p2sh(&mut self, prefix: u16) -> &mut P2SHDatabase { - let path = Self::path_p2sh(); + let path = self.path_p2sh(); self.p2sh.entry(prefix).or_insert_with(|| { let path = path.join(prefix.to_string()); @@ -223,7 +224,7 @@ impl AddressToAddressIndex { } fn open_all_p2sh(&mut self) { - let path = Self::path_p2sh(); + let path = self.path_p2sh(); Self::path_to_group_prefixes(&path) .into_iter() .for_each(|prefix| { @@ -234,12 +235,12 @@ impl AddressToAddressIndex { }); } - fn path_p2wpkh() -> PathBuf { - Self::root().join("p2wpkh") + fn path_p2wpkh(&self) -> PathBuf { + self.path().join("p2wpkh") } pub fn open_p2wpkh(&mut self, prefix: u16) -> &mut P2WPKHDatabase { - let path = Self::path_p2wpkh(); + let path = self.path_p2wpkh(); self.p2wpkh.entry(prefix).or_insert_with(|| { let path = path.join(prefix.to_string()); @@ -248,7 +249,7 @@ impl AddressToAddressIndex { } fn open_all_p2wpkh(&mut self) { - let path = Self::path_p2wpkh(); + let path = self.path_p2wpkh(); Self::path_to_group_prefixes(&path) .into_iter() .for_each(|prefix| { @@ -259,12 +260,12 @@ impl AddressToAddressIndex { }); } - fn path_p2wsh() -> PathBuf { - Self::root().join("p2wsh") + fn path_p2wsh(&self) -> PathBuf { + self.path().join("p2wsh") } pub fn open_p2wsh(&mut self, prefix: u16) -> &mut P2WSHDatabase { - let path = Self::path_p2wsh(); + let path = self.path_p2wsh(); self.p2wsh.entry(prefix).or_insert_with(|| { let path = path.join(prefix.to_string()); @@ -273,7 +274,7 @@ impl AddressToAddressIndex { } fn open_all_p2wsh(&mut self) { - let path = Self::path_p2wsh(); + let path = self.path_p2wsh(); Self::path_to_group_prefixes(&path) .into_iter() .for_each(|prefix| { @@ -284,12 +285,12 @@ impl AddressToAddressIndex { }); } - fn path_p2tr() -> PathBuf { - Self::root().join("p2tr") + fn path_p2tr(&self) -> PathBuf { + self.path().join("p2tr") } pub fn open_p2tr(&mut self, prefix: u16) -> &mut P2TRDatabase { - let path = Self::path_p2tr(); + let path = self.path_p2tr(); self.p2tr.entry(prefix).or_insert_with(|| { let path = path.join(prefix.to_string()); @@ -298,7 +299,7 @@ impl AddressToAddressIndex { } fn open_all_p2tr(&mut self) { - let path = Self::path_p2tr(); + let path = self.path_p2tr(); Self::path_to_group_prefixes(&path) .into_iter() .for_each(|prefix| { @@ -311,34 +312,36 @@ impl AddressToAddressIndex { pub fn open_unknown(&mut self) -> &mut UnknownDatabase { self.unknown - .get_or_insert_with(|| Database::open(Self::root().join("unknown")).unwrap()) + .get_or_insert_with(|| Database::open(self.path.join("unknown")).unwrap()) } pub fn open_op_return(&mut self) -> &mut UnknownDatabase { self.op_return - .get_or_insert_with(|| Database::open(Self::root().join("op_return")).unwrap()) + .get_or_insert_with(|| Database::open(self.path.join("op_return")).unwrap()) } pub fn open_push_only(&mut self) -> &mut UnknownDatabase { self.push_only - .get_or_insert_with(|| Database::open(Self::root().join("push_only")).unwrap()) + .get_or_insert_with(|| Database::open(self.path.join("push_only")).unwrap()) } pub fn open_empty(&mut self) -> &mut UnknownDatabase { self.empty - .get_or_insert_with(|| Database::open(Self::root().join("empty")).unwrap()) + .get_or_insert_with(|| Database::open(self.path.join("empty")).unwrap()) } pub fn open_multisig(&mut self) -> &mut MultisigDatabase { self.multisig - .get_or_insert_with(|| Database::open(Self::root().join("multisig")).unwrap()) + .get_or_insert_with(|| Database::open(self.path.join("multisig")).unwrap()) } } impl AnyDatabaseGroup for AddressToAddressIndex { - fn import() -> Self { + fn import(config: &Config) -> Self { + let path = config.path_databases().join("address_to_address_index"); Self { - metadata: Metadata::import(Self::root(), 1), + metadata: Metadata::import(&path, 1), + path, p2pk: BTreeMap::default(), p2pkh: BTreeMap::default(), @@ -355,22 +358,18 @@ impl AnyDatabaseGroup for AddressToAddressIndex { } fn create_dir_all(&self) -> color_eyre::Result<(), std::io::Error> { - fs::create_dir_all(Self::path_p2pk()).unwrap(); - fs::create_dir_all(Self::path_p2pkh()).unwrap(); - fs::create_dir_all(Self::path_p2sh()).unwrap(); - fs::create_dir_all(Self::path_p2wpkh()).unwrap(); - fs::create_dir_all(Self::path_p2wsh()).unwrap(); - fs::create_dir_all(Self::path_p2tr()) + fs::create_dir_all(self.path_p2pk()).unwrap(); + fs::create_dir_all(self.path_p2pkh()).unwrap(); + fs::create_dir_all(self.path_p2sh()).unwrap(); + fs::create_dir_all(self.path_p2wpkh()).unwrap(); + fs::create_dir_all(self.path_p2wsh()).unwrap(); + fs::create_dir_all(self.path_p2tr()) } fn reset_metadata(&mut self) { self.metadata.reset() } - fn folder<'a>() -> &'a str { - "address_to_address_index" - } - fn drain_to_vec(&mut self) -> Vec> { mem::take(&mut self.p2pk) .into_values() @@ -424,7 +423,11 @@ impl AnyDatabaseGroup for AddressToAddressIndex { self.open_all_p2tr(); } - fn export_metadata(&mut self, height: Height, date: Date) -> color_eyre::Result<()> { - self.metadata.export(height, date) + fn metadata(&mut self) -> &mut Metadata { + &mut self.metadata + } + + fn path(&self) -> &Path { + &self.path } } diff --git a/parser/src/databases/metadata.rs b/src/parser/databases/metadata.rs similarity index 94% rename from parser/src/databases/metadata.rs rename to src/parser/databases/metadata.rs index eb0046c5d..9d69c4817 100644 --- a/parser/src/databases/metadata.rs +++ b/src/parser/databases/metadata.rs @@ -10,8 +10,8 @@ use std::{ }; use crate::{ + io::Serialization, structs::{Counter, Date, Height}, - Serialization, }; #[derive(Default, Debug, Encode, Decode, Allocative)] @@ -35,10 +35,10 @@ impl DerefMut for Metadata { } impl Metadata { - pub fn import(path: PathBuf, version: u16) -> Self { + pub fn import(path: &Path, version: u16) -> Self { Self { - data: MetadataData::import(&path, version), - path, + data: MetadataData::import(path, version), + path: path.to_owned(), } } diff --git a/parser/src/databases/mod.rs b/src/parser/databases/mod.rs similarity index 78% rename from parser/src/databases/mod.rs rename to src/parser/databases/mod.rs index 9ff6bc752..9b32a982c 100644 --- a/parser/src/databases/mod.rs +++ b/src/parser/databases/mod.rs @@ -1,5 +1,3 @@ -use std::thread::{self}; - use allocative::Allocative; mod _database; @@ -18,18 +16,14 @@ pub use address_index_to_address_data::*; pub use address_index_to_empty_address_data::*; pub use address_to_address_index::*; use itertools::Itertools; +use log::info; use metadata::*; use rayon::iter::{IntoParallelIterator, ParallelIterator}; pub use txid_to_tx_data::*; pub use txout_index_to_address_index::*; pub use txout_index_to_amount::*; -use crate::{ - log, - structs::{Date, Height}, - utils::time, - Exit, -}; +use crate::structs::{Config, Date, Height}; #[derive(Allocative)] pub struct Databases { @@ -42,18 +36,20 @@ pub struct Databases { } impl Databases { - pub fn import() -> Self { - let address_index_to_address_data = AddressIndexToAddressData::init(); + pub fn import(config: &Config) -> Self { + let address_index_to_address_data = AddressIndexToAddressData::init(config); - let address_index_to_empty_address_data = AddressIndexToEmptyAddressData::init(); + let address_index_to_empty_address_data = AddressIndexToEmptyAddressData::init(config); - let address_to_address_index = AddressToAddressIndex::init(); + let address_to_address_index = AddressToAddressIndex::init(config); - let txid_to_tx_data = TxidToTxData::init(); + let txid_to_tx_data = TxidToTxData::init(config); - let txout_index_to_address_index = TxoutIndexToAddressIndex::init(); + let txout_index_to_address_index = TxoutIndexToAddressIndex::init(config); - let txout_index_to_amount = TxoutIndexToAmount::init(); + let txout_index_to_amount = TxoutIndexToAmount::init(config); + + info!("Imported databases"); Self { address_index_to_address_data, @@ -91,62 +87,21 @@ impl Databases { Ok(()) } - pub fn export(&mut self, height: Height, date: Date) -> color_eyre::Result<()> { + pub fn export( + &mut self, + height: Height, + date: Date, + defragment: bool, + ) -> color_eyre::Result<()> { self.export_metadata(height, date)?; self.drain_to_vec() .into_par_iter() - .try_for_each(AnyDatabase::boxed_export)?; + .try_for_each(|s| AnyDatabase::boxed_export(s, defragment))?; Ok(()) } - fn open_all(&mut self) { - thread::scope(|s| { - s.spawn(|| { - self.address_index_to_address_data.open_all(); - }); - - s.spawn(|| { - self.address_index_to_empty_address_data.open_all(); - }); - - s.spawn(|| { - self.address_to_address_index.open_all(); - }); - - s.spawn(|| { - self.txid_to_tx_data.open_all(); - }); - - s.spawn(|| { - self.txout_index_to_address_index.open_all(); - }); - - s.spawn(|| { - self.txout_index_to_amount.open_all(); - }); - }); - } - - pub fn defragment(&mut self, exit: &Exit) { - exit.block(); - - log("Databases defragmentation"); - - time("Defragmenting databases", || { - time("Opened all databases", || self.open_all()); - - log("Defragmenting..."); - - self.drain_to_vec() - .into_par_iter() - .for_each(AnyDatabase::boxed_defragment); - }); - - exit.unblock(); - } - pub fn reset(&mut self, include_addresses: bool) { if include_addresses { let _ = self.address_index_to_address_data.reset(); diff --git a/parser/src/databases/txid_to_tx_data.rs b/src/parser/databases/txid_to_tx_data.rs similarity index 87% rename from parser/src/databases/txid_to_tx_data.rs rename to src/parser/databases/txid_to_tx_data.rs index 59a706249..14ee58555 100644 --- a/parser/src/databases/txid_to_tx_data.rs +++ b/src/parser/databases/txid_to_tx_data.rs @@ -2,13 +2,14 @@ use std::{ collections::BTreeMap, fs, mem, ops::{Deref, DerefMut}, + path::{Path, PathBuf}, }; use allocative::Allocative; use biter::bitcoin::Txid; use itertools::Itertools; -use crate::structs::{Date, Height, TxData, U8x31}; +use crate::structs::{Config, TxData, U8x31}; use super::{AnyDatabase, AnyDatabaseGroup, Database as _Database, Metadata}; @@ -18,8 +19,8 @@ type Database = _Database; #[derive(Allocative)] pub struct TxidToTxData { + path: PathBuf, pub metadata: Metadata, - map: BTreeMap, } @@ -109,8 +110,9 @@ impl TxidToTxData { #[inline(always)] pub fn _open_db(&mut self, db_index: u16) -> &mut Database { + let path = self.path.to_owned(); self.entry(db_index).or_insert_with(|| { - let path = Self::root().join(db_index.to_string()); + let path = path.join(db_index.to_string()); Database::open(path).unwrap() }) } @@ -125,31 +127,23 @@ impl TxidToTxData { } impl AnyDatabaseGroup for TxidToTxData { - fn import() -> Self { - let metadata = Metadata::import(Self::root(), 2); + fn import(config: &Config) -> Self { + let path = config.path_databases().join("txid_to_tx_data"); + let metadata = Metadata::import(&path, 2); Self { + path, metadata, map: BTreeMap::default(), } } - fn create_dir_all(&self) -> color_eyre::Result<(), std::io::Error> { - fs::create_dir_all(Self::root()) - } - fn reset_metadata(&mut self) { self.metadata.reset(); } - fn folder<'a>() -> &'a str { - "txid_to_tx_data" - } - fn open_all(&mut self) { - let path = Self::root(); - - let folder = fs::read_dir(path); + let folder = fs::read_dir(&self.path); if folder.is_err() { return; @@ -180,7 +174,11 @@ impl AnyDatabaseGroup for TxidToTxData { .collect_vec() } - fn export_metadata(&mut self, height: Height, date: Date) -> color_eyre::Result<()> { - self.metadata.export(height, date) + fn metadata(&mut self) -> &mut Metadata { + &mut self.metadata + } + + fn path(&self) -> &Path { + &self.path } } diff --git a/parser/src/databases/txout_index_to_address_index.rs b/src/parser/databases/txout_index_to_address_index.rs similarity index 85% rename from parser/src/databases/txout_index_to_address_index.rs rename to src/parser/databases/txout_index_to_address_index.rs index 79baeac10..5db9f4484 100644 --- a/parser/src/databases/txout_index_to_address_index.rs +++ b/src/parser/databases/txout_index_to_address_index.rs @@ -2,12 +2,13 @@ use std::{ collections::BTreeMap, fs, mem, ops::{Deref, DerefMut}, + path::{Path, PathBuf}, }; use allocative::Allocative; use itertools::Itertools; -use crate::structs::{Date, Height, TxoutIndex}; +use crate::structs::{Config, TxoutIndex}; use super::{AnyDatabase, AnyDatabaseGroup, Database as _Database, Metadata}; @@ -17,8 +18,8 @@ type Database = _Database; #[derive(Allocative)] pub struct TxoutIndexToAddressIndex { + path: PathBuf, pub metadata: Metadata, - map: BTreeMap, } @@ -69,6 +70,7 @@ impl TxoutIndexToAddressIndex { pub fn open_db(&mut self, key: &Key) -> &mut Database { let db_index = Self::db_index(key); + let path = self.path.to_owned(); self.entry(db_index).or_insert_with(|| { let db_name = format!( @@ -77,7 +79,7 @@ impl TxoutIndexToAddressIndex { (db_index + 1) * DB_MAX_SIZE ); - let path = Self::root().join(db_name); + let path = path.join(db_name); Database::open(path).unwrap() }) @@ -89,30 +91,21 @@ impl TxoutIndexToAddressIndex { } impl AnyDatabaseGroup for TxoutIndexToAddressIndex { - fn import() -> Self { + fn import(config: &Config) -> Self { + let path = config.path_databases().join("txout_index_to_address_index"); Self { - metadata: Metadata::import(Self::root(), 1), - + metadata: Metadata::import(&path, 1), + path, map: BTreeMap::default(), } } - fn create_dir_all(&self) -> color_eyre::Result<(), std::io::Error> { - fs::create_dir_all(Self::root()) - } - fn reset_metadata(&mut self) { self.metadata.reset(); } - fn folder<'a>() -> &'a str { - "txout_index_to_address_index" - } - fn open_all(&mut self) { - let path = Self::root(); - - let folder = fs::read_dir(path); + let folder = fs::read_dir(&self.path); if folder.is_err() { return; @@ -151,7 +144,11 @@ impl AnyDatabaseGroup for TxoutIndexToAddressIndex { .collect_vec() } - fn export_metadata(&mut self, height: Height, date: Date) -> color_eyre::Result<()> { - self.metadata.export(height, date) + fn metadata(&mut self) -> &mut Metadata { + &mut self.metadata + } + + fn path(&self) -> &Path { + &self.path } } diff --git a/parser/src/databases/txout_index_to_amount.rs b/src/parser/databases/txout_index_to_amount.rs similarity index 84% rename from parser/src/databases/txout_index_to_amount.rs rename to src/parser/databases/txout_index_to_amount.rs index 6774dfdb4..0464b04c7 100644 --- a/parser/src/databases/txout_index_to_amount.rs +++ b/src/parser/databases/txout_index_to_amount.rs @@ -2,12 +2,13 @@ use std::{ collections::BTreeMap, fs, mem, ops::{Deref, DerefMut}, + path::{Path, PathBuf}, }; use allocative::Allocative; use itertools::Itertools; -use crate::structs::{Amount, Date, Height, TxoutIndex}; +use crate::structs::{Amount, Config, TxoutIndex}; use super::{AnyDatabase, AnyDatabaseGroup, Database as _Database, Metadata}; @@ -17,8 +18,8 @@ type Database = _Database; #[derive(Allocative)] pub struct TxoutIndexToAmount { + path: PathBuf, pub metadata: Metadata, - map: BTreeMap, } @@ -69,6 +70,7 @@ impl TxoutIndexToAmount { pub fn open_db(&mut self, key: &Key) -> &mut Database { let db_index = Self::db_index(key); + let path = self.path.to_owned(); self.entry(db_index).or_insert_with(|| { let db_name = format!( @@ -77,7 +79,7 @@ impl TxoutIndexToAmount { (db_index + 1) * DB_MAX_SIZE ); - let path = Self::root().join(db_name); + let path = path.join(db_name); Database::open(path).unwrap() }) @@ -89,30 +91,21 @@ impl TxoutIndexToAmount { } impl AnyDatabaseGroup for TxoutIndexToAmount { - fn import() -> Self { + fn import(config: &Config) -> Self { + let path = config.path_databases().join("txout_index_to_amount"); Self { - metadata: Metadata::import(Self::root(), 1), - + metadata: Metadata::import(&path, 1), + path, map: BTreeMap::default(), } } - fn create_dir_all(&self) -> color_eyre::Result<(), std::io::Error> { - fs::create_dir_all(Self::root()) - } - fn reset_metadata(&mut self) { self.metadata.reset(); } - fn folder<'a>() -> &'a str { - "txout_index_to_amount" - } - fn open_all(&mut self) { - let path = Self::root(); - - let folder = fs::read_dir(path); + let folder = fs::read_dir(&self.path); if folder.is_err() { return; @@ -151,7 +144,11 @@ impl AnyDatabaseGroup for TxoutIndexToAmount { .collect_vec() } - fn export_metadata(&mut self, height: Height, date: Date) -> color_eyre::Result<()> { - self.metadata.export(height, date) + fn metadata(&mut self) -> &mut Metadata { + &mut self.metadata + } + + fn path(&self) -> &Path { + &self.path } } diff --git a/parser/src/datasets/_traits/any_dataset.rs b/src/parser/datasets/_traits/any_dataset.rs similarity index 99% rename from parser/src/datasets/_traits/any_dataset.rs rename to src/parser/datasets/_traits/any_dataset.rs index 6a5091efc..4f5eba00b 100644 --- a/parser/src/datasets/_traits/any_dataset.rs +++ b/src/parser/datasets/_traits/any_dataset.rs @@ -3,14 +3,14 @@ use rayon::prelude::*; use struct_iterable::Iterable; use crate::{ - datasets::{ + parser::datasets::{ cohort_metadata::AddressCohortMetadataDataset, ComputeData, DateRecapDataset, RatioDataset, SubDataset, }, structs::{ - AnyBiMap, AnyDateMap, AnyHeightMap, AnyMap, BiMap, Date, Height, MapKind, Timestamp, OHLC, + AnyBiMap, AnyDateMap, AnyHeightMap, AnyMap, BiMap, Date, DateMap, Height, HeightMap, + MapKind, Timestamp, OHLC, }, - DateMap, HeightMap, }; use super::{AnyDatasetGroup, MinInitialStates}; diff --git a/parser/src/datasets/_traits/any_dataset_group.rs b/src/parser/datasets/_traits/any_dataset_group.rs similarity index 100% rename from parser/src/datasets/_traits/any_dataset_group.rs rename to src/parser/datasets/_traits/any_dataset_group.rs diff --git a/parser/src/datasets/_traits/any_datasets.rs b/src/parser/datasets/_traits/any_datasets.rs similarity index 100% rename from parser/src/datasets/_traits/any_datasets.rs rename to src/parser/datasets/_traits/any_datasets.rs diff --git a/parser/src/datasets/_traits/min_initial_state.rs b/src/parser/datasets/_traits/min_initial_state.rs similarity index 100% rename from parser/src/datasets/_traits/min_initial_state.rs rename to src/parser/datasets/_traits/min_initial_state.rs diff --git a/parser/src/datasets/_traits/mod.rs b/src/parser/datasets/_traits/mod.rs similarity index 100% rename from parser/src/datasets/_traits/mod.rs rename to src/parser/datasets/_traits/mod.rs diff --git a/parser/src/datasets/address/all_metadata.rs b/src/parser/datasets/address/all_metadata.rs similarity index 88% rename from parser/src/datasets/address/all_metadata.rs rename to src/parser/datasets/address/all_metadata.rs index 124c27e6d..1087d62c6 100644 --- a/parser/src/datasets/address/all_metadata.rs +++ b/src/parser/datasets/address/all_metadata.rs @@ -2,8 +2,8 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates}, - structs::{BiMap, Config, MapKind}, + parser::datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates}, + structs::{BiMap, Config, MapKind, MapPath}, }; #[derive(Allocative, Iterable)] @@ -15,8 +15,8 @@ pub struct AllAddressesMetadataDataset { } impl AllAddressesMetadataDataset { - pub fn import(parent_path: &str, config: &Config) -> color_eyre::Result { - let f = |s: &str| format!("{parent_path}/{s}"); + pub fn import(path: &MapPath, config: &Config) -> color_eyre::Result { + let f = |s: &str| path.join(s); let mut s = Self { min_initial_states: MinInitialStates::default(), diff --git a/parser/src/datasets/address/cohort.rs b/src/parser/datasets/address/cohort.rs similarity index 96% rename from parser/src/datasets/address/cohort.rs rename to src/parser/datasets/address/cohort.rs index c7d24ac82..f7676ad83 100644 --- a/parser/src/datasets/address/cohort.rs +++ b/src/parser/datasets/address/cohort.rs @@ -3,9 +3,11 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates, SubDataset}, - states::{AddressCohortId, DurableStates}, - structs::{AddressSplit, BiMap, Config, Date, Height}, + parser::{ + datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates, SubDataset}, + states::{AddressCohortId, DurableStates}, + }, + structs::{AddressSplit, BiMap, Config, Date, Height, MapPath}, }; use super::cohort_metadata::AddressCohortMetadataDataset; @@ -23,7 +25,7 @@ pub struct CohortDataset { impl CohortDataset { pub fn import( - parent_path: &str, + path: &MapPath, id: AddressCohortId, config: &Config, ) -> color_eyre::Result { @@ -33,8 +35,8 @@ impl CohortDataset { let mut s = Self { min_initial_states: MinInitialStates::default(), split, - metadata: AddressCohortMetadataDataset::import(parent_path, &name, config)?, - subs: SubDataset::import(parent_path, &name, config)?, + metadata: AddressCohortMetadataDataset::import(path, &name, config)?, + subs: SubDataset::import(path, &name, config)?, }; s.min_initial_states diff --git a/parser/src/datasets/address/cohort_metadata.rs b/src/parser/datasets/address/cohort_metadata.rs similarity index 87% rename from parser/src/datasets/address/cohort_metadata.rs rename to src/parser/datasets/address/cohort_metadata.rs index 185cda2de..860596d0c 100644 --- a/parser/src/datasets/address/cohort_metadata.rs +++ b/src/parser/datasets/address/cohort_metadata.rs @@ -2,8 +2,8 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, InsertData, MinInitialStates}, - structs::{BiMap, Config, MapKind}, + parser::datasets::{AnyDataset, InsertData, MinInitialStates}, + structs::{BiMap, Config, MapKind, MapPath}, }; #[derive(Allocative, Iterable)] @@ -19,15 +19,15 @@ pub struct AddressCohortMetadataDataset { impl AddressCohortMetadataDataset { pub fn import( - parent_path: &str, + path: &MapPath, name: &Option, config: &Config, ) -> color_eyre::Result { let f = |s: &str| { if let Some(name) = name { - format!("{parent_path}/{name}/{s}") + path.join(&format!("{name}/{s}")) } else { - format!("{parent_path}/{s}") + path.join(s) } }; diff --git a/parser/src/datasets/address/mod.rs b/src/parser/datasets/address/mod.rs similarity index 93% rename from parser/src/datasets/address/mod.rs rename to src/parser/datasets/address/mod.rs index 392bf8bd6..a8760c860 100644 --- a/parser/src/datasets/address/mod.rs +++ b/src/parser/datasets/address/mod.rs @@ -7,9 +7,8 @@ use itertools::Itertools; use rayon::prelude::*; use crate::{ - states::SplitByAddressCohort, - structs::{BiMap, Config, Height}, - Date, + parser::states::SplitByAddressCohort, + structs::{BiMap, Config, Date, Height}, }; use self::{all_metadata::AllAddressesMetadataDataset, cohort::CohortDataset}; @@ -26,13 +25,15 @@ pub struct AddressDatasets { } impl AddressDatasets { - pub fn import(parent_path: &str, config: &Config) -> color_eyre::Result { + pub fn import(config: &Config) -> color_eyre::Result { let mut cohorts = SplitByAddressCohort::>::default(); + let path_dataset = config.path_datasets(); + cohorts .as_vec() .into_par_iter() - .map(|(_, id)| (id, CohortDataset::import(parent_path, id, config))) + .map(|(_, id)| (id, CohortDataset::import(&path_dataset, id, config))) .collect::>() .into_iter() .try_for_each(|(id, dataset)| -> color_eyre::Result<()> { @@ -43,7 +44,7 @@ impl AddressDatasets { let mut s = Self { min_initial_states: MinInitialStates::default(), - metadata: AllAddressesMetadataDataset::import(parent_path, config)?, + metadata: AllAddressesMetadataDataset::import(&path_dataset, config)?, cohorts: cohorts.unwrap(), }; diff --git a/parser/src/datasets/block_metadata.rs b/src/parser/datasets/block_metadata.rs similarity index 87% rename from parser/src/datasets/block_metadata.rs rename to src/parser/datasets/block_metadata.rs index aaa6ddfe9..1bfd17b0a 100644 --- a/parser/src/datasets/block_metadata.rs +++ b/src/parser/datasets/block_metadata.rs @@ -2,7 +2,7 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::AnyDataset, + parser::datasets::AnyDataset, structs::{Config, Date, HeightMap, MapKind, Timestamp}, }; @@ -16,8 +16,8 @@ pub struct BlockMetadataDataset { } impl BlockMetadataDataset { - pub fn import(parent_path: &str, config: &Config) -> color_eyre::Result { - let f = |s: &str| format!("{parent_path}/{s}"); + pub fn import(config: &Config) -> color_eyre::Result { + let f = |s: &str| config.path_datasets().join(s); let mut s = Self { min_initial_states: MinInitialStates::default(), diff --git a/parser/src/datasets/coindays.rs b/src/parser/datasets/coindays.rs similarity index 87% rename from parser/src/datasets/coindays.rs rename to src/parser/datasets/coindays.rs index aae63dfc1..c6eca0151 100644 --- a/parser/src/datasets/coindays.rs +++ b/src/parser/datasets/coindays.rs @@ -2,9 +2,8 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::AnyDataset, - structs::{Config, MapKind}, - DateMap, HeightMap, + parser::datasets::AnyDataset, + structs::{Config, DateMap, HeightMap, MapKind}, }; use super::{InsertData, MinInitialStates}; @@ -17,8 +16,8 @@ pub struct CoindaysDataset { } impl CoindaysDataset { - pub fn import(parent_path: &str, config: &Config) -> color_eyre::Result { - let f = |s: &str| format!("{parent_path}/{s}"); + pub fn import(config: &Config) -> color_eyre::Result { + let f = |s: &str| config.path_datasets().join(s); let mut s = Self { min_initial_states: MinInitialStates::default(), diff --git a/parser/src/datasets/cointime.rs b/src/parser/datasets/cointime.rs similarity index 97% rename from parser/src/datasets/cointime.rs rename to src/parser/datasets/cointime.rs index 0cd6d6773..2c4b79271 100644 --- a/parser/src/datasets/cointime.rs +++ b/src/parser/datasets/cointime.rs @@ -2,9 +2,8 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - structs::{BiMap, Config, DateMap, Height, MapKind}, + structs::{BiMap, Config, DateMap, Height, HeightMap, MapKind}, utils::{ONE_DAY_IN_DAYS, ONE_YEAR_IN_DAYS, THREE_MONTHS_IN_DAYS, TWO_WEEK_IN_DAYS}, - HeightMap, }; use super::{AnyDataset, ComputeData, InsertData, MinInitialStates, RatioDataset}; @@ -72,8 +71,9 @@ pub struct CointimeDataset { } impl CointimeDataset { - pub fn import(parent_path: &str, config: &Config) -> color_eyre::Result { - let f = |s: &str| format!("{parent_path}/{s}"); + pub fn import(config: &Config) -> color_eyre::Result { + let path_dataset = config.path_datasets(); + let f = |s: &str| path_dataset.join(s); let mut s = Self { min_initial_states: MinInitialStates::default(), @@ -93,7 +93,7 @@ impl CointimeDataset { // Computed active_cap: BiMap::new_bin(1, MapKind::Computed, &f("active_cap")), active_price: BiMap::new_bin(1, MapKind::Computed, &f("active_price")), - active_price_ratio: RatioDataset::import(parent_path, "active_price", config)?, + active_price_ratio: RatioDataset::import(&path_dataset, "active_price", config)?, active_supply: BiMap::new_bin(1, MapKind::Computed, &f("active_supply")), active_supply_3m_net_change: BiMap::new_bin( 1, @@ -140,7 +140,7 @@ impl CointimeDataset { ), cointime_cap: BiMap::new_bin(1, MapKind::Computed, &f("cointime_cap")), cointime_price: BiMap::new_bin(1, MapKind::Computed, &f("cointime_price")), - cointime_price_ratio: RatioDataset::import(parent_path, "cointime_price", config)?, + cointime_price_ratio: RatioDataset::import(&path_dataset, "cointime_price", config)?, cointime_value_created: HeightMap::new_bin( 1, MapKind::Computed, @@ -237,7 +237,11 @@ impl CointimeDataset { &f("true_market_deviation"), ), true_market_mean: BiMap::new_bin(1, MapKind::Computed, &f("true_market_mean")), - true_market_mean_ratio: RatioDataset::import(parent_path, "true_market_mean", config)?, + true_market_mean_ratio: RatioDataset::import( + &path_dataset, + "true_market_mean", + config, + )?, true_market_net_unrealized_profit_and_loss: BiMap::new_bin( 1, MapKind::Computed, @@ -245,7 +249,7 @@ impl CointimeDataset { ), vaulted_cap: BiMap::new_bin(1, MapKind::Computed, &f("vaulted_cap")), vaulted_price: BiMap::new_bin(1, MapKind::Computed, &f("vaulted_price")), - vaulted_price_ratio: RatioDataset::import(parent_path, "vaulted_price", config)?, + vaulted_price_ratio: RatioDataset::import(&path_dataset, "vaulted_price", config)?, vaulted_supply: BiMap::new_bin(1, MapKind::Computed, &f("vaulted_supply")), vaulted_supply_3m_net_change: BiMap::new_bin( 1, diff --git a/parser/src/datasets/constant.rs b/src/parser/datasets/constant.rs similarity index 90% rename from parser/src/datasets/constant.rs rename to src/parser/datasets/constant.rs index ae94965ae..4f1dce63f 100644 --- a/parser/src/datasets/constant.rs +++ b/src/parser/datasets/constant.rs @@ -16,8 +16,8 @@ pub struct ConstantDataset { } impl ConstantDataset { - pub fn import(parent_path: &str, config: &Config) -> color_eyre::Result { - let f = |s: &str| format!("{parent_path}/{s}"); + pub fn import(config: &Config) -> color_eyre::Result { + let f = |s: &str| config.path_datasets().join(s); let mut s = Self { min_initial_states: MinInitialStates::default(), diff --git a/parser/src/datasets/date_metadata.rs b/src/parser/datasets/date_metadata.rs similarity index 88% rename from parser/src/datasets/date_metadata.rs rename to src/parser/datasets/date_metadata.rs index 4f69ec24f..80f8fa9d6 100644 --- a/parser/src/datasets/date_metadata.rs +++ b/src/parser/datasets/date_metadata.rs @@ -2,7 +2,7 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::AnyDataset, + parser::datasets::AnyDataset, structs::{Config, DateMap, Height, MapKind}, }; @@ -17,8 +17,8 @@ pub struct DateMetadataDataset { } impl DateMetadataDataset { - pub fn import(parent_path: &str, config: &Config) -> color_eyre::Result { - let f = |s: &str| format!("{parent_path}/{s}"); + pub fn import(config: &Config) -> color_eyre::Result { + let f = |s: &str| config.path_datasets().join(s); let mut s = Self { min_initial_states: MinInitialStates::default(), diff --git a/parser/src/datasets/mining.rs b/src/parser/datasets/mining.rs similarity index 99% rename from parser/src/datasets/mining.rs rename to src/parser/datasets/mining.rs index 759fcf35b..667b0a8a7 100644 --- a/parser/src/datasets/mining.rs +++ b/src/parser/datasets/mining.rs @@ -4,7 +4,7 @@ use ordered_float::OrderedFloat; use struct_iterable::Iterable; use crate::{ - datasets::AnyDataset, + parser::datasets::AnyDataset, structs::{Amount, BiMap, Config, DateMap, Height, HeightMap, MapKey, MapKind}, utils::{ BYTES_IN_MB, ONE_DAY_IN_DAYS, ONE_MONTH_IN_DAYS, ONE_WEEK_IN_DAYS, ONE_YEAR_IN_DAYS, @@ -123,8 +123,8 @@ pub struct MiningDataset { } impl MiningDataset { - pub fn import(parent_path: &str, config: &Config) -> color_eyre::Result { - let f = |s: &str| format!("{parent_path}/{s}"); + pub fn import(config: &Config) -> color_eyre::Result { + let f = |s: &str| config.path_datasets().join(s); let mut s = Self { min_initial_states: MinInitialStates::default(), diff --git a/parser/src/datasets/mod.rs b/src/parser/datasets/mod.rs similarity index 75% rename from parser/src/datasets/mod.rs rename to src/parser/datasets/mod.rs index 43a8c94c1..bc4edc899 100644 --- a/parser/src/datasets/mod.rs +++ b/src/parser/datasets/mod.rs @@ -1,9 +1,10 @@ -use std::{collections::BTreeMap, fs, ops::RangeInclusive, path::Path}; +use std::{collections::BTreeMap, ops::RangeInclusive, path::PathBuf}; use allocative::Allocative; use itertools::Itertools; +use log::info; use rayon::prelude::*; mod _traits; @@ -34,16 +35,18 @@ pub use transaction::*; pub use utxo::*; use crate::{ - databases::Databases, - io::{Json, JSON_EXTENSION}, - states::{ - AddressCohortsInputStates, - AddressCohortsOneShotStates, - AddressCohortsRealizedStates, - States, - UTXOCohortsOneShotStates, - // UTXOCohortsReceivedStates, - UTXOCohortsSentStates, + io::Json, + parser::{ + databases::Databases, + states::{ + AddressCohortsInputStates, + AddressCohortsOneShotStates, + AddressCohortsRealizedStates, + States, + UTXOCohortsOneShotStates, + // UTXOCohortsReceivedStates, + UTXOCohortsSentStates, + }, }, structs::{Amount, Config, Date, Height, Price, Timestamp}, }; @@ -84,7 +87,7 @@ pub struct ComputeData<'a> { } #[derive(Allocative)] -pub struct AllDatasets { +pub struct Datasets { min_initial_states: MinInitialStates, pub constant: ConstantDataset, @@ -99,31 +102,27 @@ pub struct AllDatasets { pub utxo: UTXODatasets, } -const DATASETS_PATH: &str = "../datasets"; - -impl AllDatasets { +impl Datasets { pub fn import(config: &Config) -> color_eyre::Result { - let path = DATASETS_PATH; + let price = PriceDatasets::import(config)?; - let price = PriceDatasets::import(path, config)?; + let constant = ConstantDataset::import(config)?; - let constant = ConstantDataset::import(path, config)?; + let date_metadata = DateMetadataDataset::import(config)?; - let date_metadata = DateMetadataDataset::import(path, config)?; + let cointime = CointimeDataset::import(config)?; - let cointime = CointimeDataset::import(path, config)?; + let coindays = CoindaysDataset::import(config)?; - let coindays = CoindaysDataset::import(path, config)?; + let mining = MiningDataset::import(config)?; - let mining = MiningDataset::import(path, config)?; + let block_metadata = BlockMetadataDataset::import(config)?; - let block_metadata = BlockMetadataDataset::import(path, config)?; + let transaction = TransactionDataset::import(config)?; - let transaction = TransactionDataset::import(path, config)?; + let address = AddressDatasets::import(config)?; - let address = AddressDatasets::import(path, config)?; - - let utxo = UTXODatasets::import(path, config)?; + let utxo = UTXODatasets::import(config)?; let mut s = Self { min_initial_states: MinInitialStates::default(), @@ -143,7 +142,7 @@ impl AllDatasets { s.min_initial_states .consume(MinInitialStates::compute_from_datasets(&s, config)); - s.export_meta_files()?; + info!("Imported datasets"); Ok(s) } @@ -269,43 +268,23 @@ impl AllDatasets { } } - pub fn export_meta_files(&self) -> color_eyre::Result<()> { - let mut path_to_type: BTreeMap<&Path, &str> = self + pub fn get_paths_to_type(&self, config: &Config) -> BTreeMap { + let mut path_to_type: BTreeMap = self .to_any_dataset_vec() .into_iter() .flat_map(|dataset| dataset.to_all_map_vec()) - .flat_map(|map| map.exported_path_with_t_name()) + .flat_map(|map| map.get_paths_to_type()) .collect(); - path_to_type.insert(Path::new("../datasets/last"), "Value"); + path_to_type.insert( + config.path_datasets_last_values().unwrap().to_owned(), + "Value".to_string(), + ); - let datasets_len = path_to_type.len(); - - let server_inputs_path = "../server/in"; - - fs::create_dir_all(server_inputs_path)?; - - Json::export( - Path::new(&format!("{server_inputs_path}/disk_path_to_type.json")), - &path_to_type, - )?; - - let datasets_len_path = format!("{server_inputs_path}/datasets_len.txt"); - - if let Ok(len) = fs::read_to_string(&datasets_len_path) { - if let Ok(len) = len.parse::() { - if datasets_len == len { - return Ok(()); - } - } - } - - fs::write(datasets_len_path, datasets_len.to_string())?; - - Ok(()) + path_to_type } - pub fn export(&mut self) -> color_eyre::Result<()> { + pub fn export(&mut self, config: &Config) -> color_eyre::Result<()> { self.to_mut_any_dataset_vec() .into_iter() .for_each(|dataset| dataset.pre_export()); @@ -316,6 +295,11 @@ impl AllDatasets { let mut path_to_last: BTreeMap = BTreeMap::default(); + let path_dataset = config.path_datasets(); + let path_dataset_ser = path_dataset.to_str().unwrap(); + let path_price = config.path_price(); + let path_price_ser = path_price.to_str().unwrap(); + self.to_mut_any_dataset_vec() .into_iter() .for_each(|dataset| { @@ -326,34 +310,28 @@ impl AllDatasets { if let Some(last_value) = map.last_value() { let mut last_path = last_path.clone(); last_path.pop(); + let key = last_path + .to_str() + .unwrap() + .replace(path_dataset_ser, "") + .replace(path_price_ser, "") + .split("/") + .join("-") + .to_string(); - let last_path = last_path.to_str().unwrap(); - - let skip = if last_path.starts_with(DATASETS_PATH) { - 2 - } else { - 1 - }; - - path_to_last.insert( - last_path.split('/').skip(skip).join("-").to_string(), - last_value, - ); + path_to_last.insert(key, last_value); } } }); }); - Json::export( - Path::new(&format!("{DATASETS_PATH}/last.{JSON_EXTENSION}")), - &path_to_last, - )?; + Json::export(&config.path_datasets_last_values(), &path_to_last)?; Ok(()) } } -impl AnyDatasets for AllDatasets { +impl AnyDatasets for Datasets { fn get_min_initial_states(&self) -> &MinInitialStates { &self.min_initial_states } diff --git a/parser/src/datasets/price.rs b/src/parser/datasets/price.rs similarity index 93% rename from parser/src/datasets/price.rs rename to src/parser/datasets/price.rs index d2735c4ee..ca79bd945 100644 --- a/parser/src/datasets/price.rs +++ b/src/parser/datasets/price.rs @@ -7,7 +7,7 @@ use color_eyre::eyre::Error; use struct_iterable::Iterable; use crate::{ - price::{Binance, Kibo, Kraken}, + parser::price::{Binance, Kibo, Kraken}, structs::{ Amount, BiMap, Config, Date, DateMap, DateMapChunkId, Height, HeightMapChunkId, MapKey, MapKind, Timestamp, OHLC, @@ -85,10 +85,9 @@ pub struct PriceDatasets { } impl PriceDatasets { - pub fn import(datasets_path: &str, config: &Config) -> color_eyre::Result { - let price_path = "../price"; - - let f = |s: &str| format!("{datasets_path}/{s}"); + pub fn import(config: &Config) -> color_eyre::Result { + let path_dataset = config.path_datasets(); + let f = |s: &str| path_dataset.join(s); let mut s = Self { min_initial_states: MinInitialStates::default(), @@ -104,7 +103,7 @@ impl PriceDatasets { // --- // Inserted // --- - ohlc: BiMap::new_json(1, MapKind::Inserted, price_path), + ohlc: BiMap::new_json(1, MapKind::Inserted, &config.path_price()), // --- // Computed @@ -115,31 +114,31 @@ impl PriceDatasets { close: BiMap::new_bin(1, MapKind::Computed, &f("close")), market_cap: BiMap::new_bin(1, MapKind::Computed, &f("market_cap")), price_1w_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_1w_sma")), - price_1w_sma_ratio: RatioDataset::import(datasets_path, "price_1w_sma", config)?, + price_1w_sma_ratio: RatioDataset::import(&path_dataset, "price_1w_sma", config)?, price_1m_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_1m_sma")), - price_1m_sma_ratio: RatioDataset::import(datasets_path, "price_1m_sma", config)?, + price_1m_sma_ratio: RatioDataset::import(&path_dataset, "price_1m_sma", config)?, price_1y_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_1y_sma")), - price_1y_sma_ratio: RatioDataset::import(datasets_path, "price_1y_sma", config)?, + price_1y_sma_ratio: RatioDataset::import(&path_dataset, "price_1y_sma", config)?, price_2y_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_2y_sma")), - price_2y_sma_ratio: RatioDataset::import(datasets_path, "price_2y_sma", config)?, + price_2y_sma_ratio: RatioDataset::import(&path_dataset, "price_2y_sma", config)?, price_4y_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_4y_sma")), - price_4y_sma_ratio: RatioDataset::import(datasets_path, "price_4y_sma", config)?, + price_4y_sma_ratio: RatioDataset::import(&path_dataset, "price_4y_sma", config)?, price_8d_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_8d_sma")), - price_8d_sma_ratio: RatioDataset::import(datasets_path, "price_8d_sma", config)?, + price_8d_sma_ratio: RatioDataset::import(&path_dataset, "price_8d_sma", config)?, price_13d_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_13d_sma")), - price_13d_sma_ratio: RatioDataset::import(datasets_path, "price_13d_sma", config)?, + price_13d_sma_ratio: RatioDataset::import(&path_dataset, "price_13d_sma", config)?, price_21d_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_21d_sma")), - price_21d_sma_ratio: RatioDataset::import(datasets_path, "price_21d_sma", config)?, + price_21d_sma_ratio: RatioDataset::import(&path_dataset, "price_21d_sma", config)?, price_34d_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_34d_sma")), - price_34d_sma_ratio: RatioDataset::import(datasets_path, "price_34d_sma", config)?, + price_34d_sma_ratio: RatioDataset::import(&path_dataset, "price_34d_sma", config)?, price_55d_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_55d_sma")), - price_55d_sma_ratio: RatioDataset::import(datasets_path, "price_55d_sma", config)?, + price_55d_sma_ratio: RatioDataset::import(&path_dataset, "price_55d_sma", config)?, price_89d_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_89d_sma")), - price_89d_sma_ratio: RatioDataset::import(datasets_path, "price_89d_sma", config)?, + price_89d_sma_ratio: RatioDataset::import(&path_dataset, "price_89d_sma", config)?, price_144d_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_144d_sma")), - price_144d_sma_ratio: RatioDataset::import(datasets_path, "price_144d_sma", config)?, + price_144d_sma_ratio: RatioDataset::import(&path_dataset, "price_144d_sma", config)?, price_200w_sma: BiMap::new_bin(1, MapKind::Computed, &f("price_200w_sma")), - price_200w_sma_ratio: RatioDataset::import(datasets_path, "price_200w_sma", config)?, + price_200w_sma_ratio: RatioDataset::import(&path_dataset, "price_200w_sma", config)?, price_1d_total_return: DateMap::new_bin( 1, MapKind::Computed, @@ -540,6 +539,7 @@ impl PriceDatasets { height: Height, timestamp: Timestamp, previous_timestamp: Option, + config: &Config, ) -> color_eyre::Result { if let Some(ohlc) = self.ohlc.height.get_or_import(&height) { return Ok(ohlc); @@ -558,7 +558,7 @@ impl PriceDatasets { .unwrap_or_else(|_| { self.get_from_1mn_binance(timestamp, previous_timestamp) .unwrap_or_else(|_| { - self.get_from_har_binance(timestamp, previous_timestamp) + self.get_from_har_binance(timestamp, previous_timestamp, config) .unwrap_or_else(|_| { self.get_from_height_kibo(&height).unwrap_or_else(|_| { let date = timestamp.to_date(); @@ -659,10 +659,11 @@ How to fix this: &mut self, timestamp: Timestamp, previous_timestamp: Option, + config: &Config, ) -> color_eyre::Result { if self.binance_har.is_none() { self.binance_har - .replace(Binance::read_har_file().unwrap_or_default()); + .replace(Binance::read_har_file(config).unwrap_or_default()); } Self::find_height_ohlc( diff --git a/parser/src/datasets/subs/capitalization.rs b/src/parser/datasets/subs/capitalization.rs similarity index 90% rename from parser/src/datasets/subs/capitalization.rs rename to src/parser/datasets/subs/capitalization.rs index 20a792662..834f19a3e 100644 --- a/parser/src/datasets/subs/capitalization.rs +++ b/src/parser/datasets/subs/capitalization.rs @@ -2,9 +2,11 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates}, - states::CapitalizationState, - structs::{BiMap, Config, MapKind}, + parser::{ + datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates}, + states::CapitalizationState, + }, + structs::{BiMap, Config, MapKind, MapPath}, utils::ONE_MONTH_IN_DAYS, }; @@ -22,15 +24,15 @@ pub struct CapitalizationDataset { impl CapitalizationDataset { pub fn import( - parent_path: &str, + path: &MapPath, name: &Option, config: &Config, ) -> color_eyre::Result { let f = |s: &str| { if let Some(name) = name { - format!("{parent_path}/{name}/{s}") + path.join(&format!("{name}/{s}")) } else { - format!("{parent_path}/{s}") + path.join(s) } }; @@ -52,7 +54,7 @@ impl CapitalizationDataset { ), realized_price: BiMap::new_bin(1, MapKind::Computed, &f("realized_price")), realized_price_ratio: RatioDataset::import( - parent_path, + path, &format!( "{}realized_price", name.as_ref().map_or("".to_owned(), |n| format!("{n}-")) diff --git a/parser/src/datasets/subs/input.rs b/src/parser/datasets/subs/input.rs similarity index 87% rename from parser/src/datasets/subs/input.rs rename to src/parser/datasets/subs/input.rs index b9091205e..f7522cc03 100644 --- a/parser/src/datasets/subs/input.rs +++ b/src/parser/datasets/subs/input.rs @@ -2,10 +2,11 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, InsertData, MinInitialStates}, - states::InputState, - structs::{BiMap, Config, MapKind}, - DateMap, HeightMap, + parser::{ + datasets::{AnyDataset, InsertData, MinInitialStates}, + states::InputState, + }, + structs::{BiMap, Config, DateMap, HeightMap, MapKind, MapPath}, }; #[derive(Allocative, Iterable)] @@ -22,15 +23,15 @@ pub struct InputSubDataset { impl InputSubDataset { pub fn import( - parent_path: &str, + path: &MapPath, name: &Option, config: &Config, ) -> color_eyre::Result { let f = |s: &str| { if let Some(name) = name { - format!("{parent_path}/{name}/{s}") + path.join(&format!("{name}/{s}")) } else { - format!("{parent_path}/{s}") + path.join(s) } }; diff --git a/parser/src/datasets/subs/mod.rs b/src/parser/datasets/subs/mod.rs similarity index 95% rename from parser/src/datasets/subs/mod.rs rename to src/parser/datasets/subs/mod.rs index c34be68cd..566cd8388 100644 --- a/parser/src/datasets/subs/mod.rs +++ b/src/parser/datasets/subs/mod.rs @@ -21,7 +21,10 @@ pub use supply::*; pub use unrealized::*; pub use utxo::*; -use crate::{datasets::AnyDataset, structs::Config}; +use crate::{ + parser::datasets::AnyDataset, + structs::{Config, MapPath}, +}; use super::AnyDatasetGroup; @@ -39,7 +42,7 @@ pub struct SubDataset { impl SubDataset { pub fn import( - parent_path: &str, + parent_path: &MapPath, name: &Option, config: &Config, ) -> color_eyre::Result { diff --git a/parser/src/datasets/subs/output.rs b/src/parser/datasets/subs/output.rs similarity index 100% rename from parser/src/datasets/subs/output.rs rename to src/parser/datasets/subs/output.rs diff --git a/parser/src/datasets/subs/price_paid.rs b/src/parser/datasets/subs/price_paid.rs similarity index 96% rename from parser/src/datasets/subs/price_paid.rs rename to src/parser/datasets/subs/price_paid.rs index ca06ef2b5..7f1bba43d 100644 --- a/parser/src/datasets/subs/price_paid.rs +++ b/src/parser/datasets/subs/price_paid.rs @@ -2,9 +2,11 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, InsertData, MinInitialStates}, - states::PricePaidState, - structs::{BiMap, Config, Date, Height, MapKind}, + parser::{ + datasets::{AnyDataset, InsertData, MinInitialStates}, + states::PricePaidState, + }, + structs::{BiMap, Config, Date, Height, MapKind, MapPath}, }; #[derive(Allocative, Iterable)] @@ -34,15 +36,15 @@ pub struct PricePaidSubDataset { impl PricePaidSubDataset { pub fn import( - parent_path: &str, + path: &MapPath, name: &Option, config: &Config, ) -> color_eyre::Result { let f = |s: &str| { if let Some(name) = name { - format!("{parent_path}/{name}/{s}") + path.join(&format!("{name}/{s}")) } else { - format!("{parent_path}/{s}") + path.join(s) } }; diff --git a/parser/src/datasets/subs/ratio.rs b/src/parser/datasets/subs/ratio.rs similarity index 93% rename from parser/src/datasets/subs/ratio.rs rename to src/parser/datasets/subs/ratio.rs index d7ca03c3e..270f9dde0 100644 --- a/parser/src/datasets/subs/ratio.rs +++ b/src/parser/datasets/subs/ratio.rs @@ -2,8 +2,8 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, ComputeData, MinInitialStates}, - structs::{BiMap, Config, MapKind}, + parser::datasets::{AnyDataset, ComputeData, MinInitialStates}, + structs::{BiMap, Config, MapKind, MapPath}, utils::{ONE_MONTH_IN_DAYS, ONE_WEEK_IN_DAYS, ONE_YEAR_IN_DAYS}, }; @@ -31,9 +31,9 @@ pub struct RatioDataset { } impl RatioDataset { - pub fn import(parent_path: &str, name: &str, config: &Config) -> color_eyre::Result { - let f_ratio = |s: &str| format!("{parent_path}/market_price_to_{name}_{s}"); - let f_price = |s: &str| format!("{parent_path}/{name}_{s}"); + pub fn import(path: &MapPath, name: &str, config: &Config) -> color_eyre::Result { + let f_ratio = |s: &str| path.join(&format!("market_price_to_{name}_{s}")); + let f_price = |s: &str| path.join(&format!("{name}_{s}")); let mut s = Self { min_initial_states: MinInitialStates::default(), diff --git a/parser/src/datasets/subs/realized.rs b/src/parser/datasets/subs/realized.rs similarity index 97% rename from parser/src/datasets/subs/realized.rs rename to src/parser/datasets/subs/realized.rs index 005d4b389..c5c4ae98b 100644 --- a/parser/src/datasets/subs/realized.rs +++ b/src/parser/datasets/subs/realized.rs @@ -2,11 +2,12 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates}, - states::RealizedState, - structs::{BiMap, Config, MapKind, Price}, + parser::{ + datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates}, + states::RealizedState, + }, + structs::{BiMap, Config, DateMap, HeightMap, MapKind, MapPath, Price}, utils::ONE_MONTH_IN_DAYS, - DateMap, HeightMap, }; #[derive(Allocative, Iterable)] @@ -45,18 +46,17 @@ pub struct RealizedSubDataset { impl RealizedSubDataset { pub fn import( - parent_path: &str, + path: &MapPath, name: &Option, config: &Config, ) -> color_eyre::Result { let f = |s: &str| { if let Some(name) = name { - format!("{parent_path}/{name}/{s}") + path.join(&format!("{name}/{s}")) } else { - format!("{parent_path}/{s}") + path.join(s) } }; - let mut s = Self { min_initial_states: MinInitialStates::default(), diff --git a/parser/src/datasets/subs/recap.rs b/src/parser/datasets/subs/recap.rs similarity index 96% rename from parser/src/datasets/subs/recap.rs rename to src/parser/datasets/subs/recap.rs index e5661fa53..e02277c1e 100644 --- a/parser/src/datasets/subs/recap.rs +++ b/src/parser/datasets/subs/recap.rs @@ -3,9 +3,11 @@ use std::{iter::Sum, ops::Add}; use allocative::Allocative; use crate::{ - structs::{DateMapChunkId, GenericMap, MapKey, MapKind, MapSerialized, MapValue}, + structs::{ + Date, DateMapChunkId, GenericMap, MapChunkId, MapKey, MapKind, MapPath, MapSerialized, + MapValue, SerializedBTreeMap, + }, utils::{get_percentile, LossyFrom}, - Date, MapChunkId, SerializedBTreeMap, }; pub type DateRecapDataset = RecapDataset>; @@ -91,8 +93,8 @@ where Key: MapKey, Serialized: MapSerialized, { - pub fn import(parent_path: &str, options: RecapOptions) -> color_eyre::Result { - let f = |s: &str| format!("{parent_path}/{s}"); + pub fn import(path: &MapPath, options: RecapOptions) -> color_eyre::Result { + let f = |s: &str| path.join(s); let s = Self { // --- diff --git a/parser/src/datasets/subs/supply.rs b/src/parser/datasets/subs/supply.rs similarity index 90% rename from parser/src/datasets/subs/supply.rs rename to src/parser/datasets/subs/supply.rs index 316ae0904..7fe41efec 100644 --- a/parser/src/datasets/subs/supply.rs +++ b/src/parser/datasets/subs/supply.rs @@ -2,9 +2,11 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates}, - states::SupplyState, - structs::{BiMap, Config, MapKind}, + parser::{ + datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates}, + states::SupplyState, + }, + structs::{BiMap, Config, MapKind, MapPath}, }; #[derive(Allocative, Iterable)] @@ -19,15 +21,15 @@ pub struct SupplySubDataset { impl SupplySubDataset { pub fn import( - parent_path: &str, + path: &MapPath, name: &Option, config: &Config, ) -> color_eyre::Result { let f = |s: &str| { if let Some(name) = name { - format!("{parent_path}/{name}/{s}") + path.join(&format!("{name}/{s}")) } else { - format!("{parent_path}/{s}") + path.join(s) } }; diff --git a/parser/src/datasets/subs/unrealized.rs b/src/parser/datasets/subs/unrealized.rs similarity index 95% rename from parser/src/datasets/subs/unrealized.rs rename to src/parser/datasets/subs/unrealized.rs index 7801cf243..6290f589b 100644 --- a/parser/src/datasets/subs/unrealized.rs +++ b/src/parser/datasets/subs/unrealized.rs @@ -2,9 +2,11 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates}, - states::UnrealizedState, - structs::{BiMap, Config, MapKind}, + parser::{ + datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates}, + states::UnrealizedState, + }, + structs::{BiMap, Config, MapKind, MapPath}, }; #[derive(Allocative, Iterable)] @@ -26,15 +28,15 @@ pub struct UnrealizedSubDataset { impl UnrealizedSubDataset { pub fn import( - parent_path: &str, + path: &MapPath, name: &Option, config: &Config, ) -> color_eyre::Result { let f = |s: &str| { if let Some(name) = name { - format!("{parent_path}/{name}/{s}") + path.join(&format!("{name}/{s}")) } else { - format!("{parent_path}/{s}") + path.join(s) } }; diff --git a/parser/src/datasets/subs/utxo.rs b/src/parser/datasets/subs/utxo.rs similarity index 83% rename from parser/src/datasets/subs/utxo.rs rename to src/parser/datasets/subs/utxo.rs index 61ecf9f90..4f5a071ae 100644 --- a/parser/src/datasets/subs/utxo.rs +++ b/src/parser/datasets/subs/utxo.rs @@ -2,9 +2,11 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, InsertData, MinInitialStates}, - states::UTXOState, - structs::{BiMap, Config, MapKind}, + parser::{ + datasets::{AnyDataset, InsertData, MinInitialStates}, + states::UTXOState, + }, + structs::{BiMap, Config, MapKind, MapPath}, }; #[derive(Allocative, Iterable)] @@ -16,15 +18,15 @@ pub struct UTXOSubDataset { impl UTXOSubDataset { pub fn import( - parent_path: &str, + path: &MapPath, name: &Option, config: &Config, ) -> color_eyre::Result { let f = |s: &str| { if let Some(name) = name { - format!("{parent_path}/{name}/{s}") + path.join(&format!("{name}/{s}")) } else { - format!("{parent_path}/{s}") + path.join(s) } }; diff --git a/parser/src/datasets/transaction.rs b/src/parser/datasets/transaction.rs similarity index 97% rename from parser/src/datasets/transaction.rs rename to src/parser/datasets/transaction.rs index 7ab3ae300..94704de63 100644 --- a/parser/src/datasets/transaction.rs +++ b/src/parser/datasets/transaction.rs @@ -2,12 +2,11 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::InsertData, - structs::{BiMap, Config, HeightMap, MapKind}, + parser::datasets::InsertData, + structs::{BiMap, Config, DateMap, HeightMap, MapKind}, utils::{ ONE_DAY_IN_S, ONE_MONTH_IN_DAYS, ONE_WEEK_IN_DAYS, ONE_YEAR_IN_DAYS, TARGET_BLOCKS_PER_DAY, }, - DateMap, }; use super::{AnyDataset, ComputeData, MinInitialStates}; @@ -52,8 +51,8 @@ pub struct TransactionDataset { } impl TransactionDataset { - pub fn import(parent_path: &str, config: &Config) -> color_eyre::Result { - let f = |s: &str| format!("{parent_path}/{s}"); + pub fn import(config: &Config) -> color_eyre::Result { + let f = |s: &str| config.path_datasets().join(s); let mut s = Self { min_initial_states: MinInitialStates::default(), diff --git a/parser/src/datasets/utxo/dataset.rs b/src/parser/datasets/utxo/dataset.rs similarity index 96% rename from parser/src/datasets/utxo/dataset.rs rename to src/parser/datasets/utxo/dataset.rs index 96b878ddf..01087dbb5 100644 --- a/parser/src/datasets/utxo/dataset.rs +++ b/src/parser/datasets/utxo/dataset.rs @@ -2,9 +2,11 @@ use allocative::Allocative; use struct_iterable::Iterable; use crate::{ - datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates, SubDataset}, - states::UTXOCohortId, - structs::{BiMap, Config, Date, Height}, + parser::{ + datasets::{AnyDataset, ComputeData, InsertData, MinInitialStates, SubDataset}, + states::UTXOCohortId, + }, + structs::{BiMap, Config, Date, Height, MapPath}, }; #[derive(Allocative, Iterable)] @@ -18,7 +20,7 @@ pub struct UTXODataset { impl UTXODataset { pub fn import( - parent_path: &str, + parent_path: &MapPath, id: UTXOCohortId, config: &Config, ) -> color_eyre::Result { diff --git a/parser/src/datasets/utxo/mod.rs b/src/parser/datasets/utxo/mod.rs similarity index 94% rename from parser/src/datasets/utxo/mod.rs rename to src/parser/datasets/utxo/mod.rs index d9bc811bd..c9d0390b4 100644 --- a/parser/src/datasets/utxo/mod.rs +++ b/src/parser/datasets/utxo/mod.rs @@ -7,8 +7,8 @@ use rayon::prelude::*; use itertools::Itertools; use crate::{ - datasets::AnyDatasets, - states::{SplitByUTXOCohort, UTXOCohortId}, + parser::datasets::AnyDatasets, + parser::states::{SplitByUTXOCohort, UTXOCohortId}, structs::{BiMap, Config, Date, Height}, }; @@ -22,13 +22,15 @@ pub struct UTXODatasets { } impl UTXODatasets { - pub fn import(parent_path: &str, config: &Config) -> color_eyre::Result { + pub fn import(config: &Config) -> color_eyre::Result { let mut cohorts = SplitByUTXOCohort::>::default(); + let path_dataset = config.path_datasets(); + cohorts .as_vec() .into_par_iter() - .map(|(_, id)| (id, UTXODataset::import(parent_path, id, config))) + .map(|(_, id)| (id, UTXODataset::import(&path_dataset, id, config))) .collect::>() .into_iter() .try_for_each(|(id, dataset)| -> color_eyre::Result<()> { diff --git a/src/parser/mod.rs b/src/parser/mod.rs new file mode 100644 index 000000000..21acaa3ce --- /dev/null +++ b/src/parser/mod.rs @@ -0,0 +1,52 @@ +use std::{thread::sleep, time::Duration}; + +use biter::bitcoincore_rpc::{Client, RpcApi}; + +mod actions; +mod databases; +mod datasets; +mod price; +mod states; + +pub use actions::*; +pub use databases::*; +pub use datasets::*; +use log::info; +pub use states::*; + +use crate::structs::{Config, Exit}; + +pub fn main( + config: &Config, + rpc: &Client, + exit: &Exit, + mut databases: Databases, + mut datasets: Datasets, +) -> color_eyre::Result<()> { + loop { + let block_count = rpc.get_blockchain_info().unwrap().blocks as usize; + + info!("{block_count} blocks found."); + + iter_blocks( + config, + rpc, + block_count, + exit.clone(), + &mut databases, + &mut datasets, + )?; + + if let Some(delay) = config.delay { + sleep(Duration::from_secs(delay)) + } + + info!("Waiting for a new block..."); + + while block_count == rpc.get_blockchain_info().unwrap().blocks as usize { + sleep(Duration::from_secs(1)) + } + } + + // Ok(()) +} diff --git a/parser/src/price/binance.rs b/src/parser/price/binance.rs similarity index 92% rename from parser/src/price/binance.rs rename to src/parser/price/binance.rs index bbd988b13..3075f010d 100644 --- a/parser/src/price/binance.rs +++ b/src/parser/price/binance.rs @@ -1,26 +1,29 @@ #![allow(dead_code)] -use std::{collections::BTreeMap, fs, path::Path}; +use std::{collections::BTreeMap, fs}; use color_eyre::eyre::ContextCompat; use itertools::Itertools; +use log::info; use serde_json::Value; use crate::{ - io::{Json, INPUTS_FOLDER_PATH}, - structs::{Date, Timestamp, OHLC}, - utils::{log, retry}, + io::Json, + structs::{Config, Date, Timestamp, OHLC}, + utils::retry, }; pub struct Binance; impl Binance { - pub fn read_har_file() -> color_eyre::Result> { - log("binance: read har file"); + pub fn read_har_file(config: &Config) -> color_eyre::Result> { + info!("binance: read har file"); - fs::create_dir_all(INPUTS_FOLDER_PATH)?; + let path = config.path_inputs(); - let path_binance_har = Path::new(INPUTS_FOLDER_PATH).join("binance.har"); + fs::create_dir_all(&path)?; + + let path_binance_har = path.join("binance.har"); let json: BTreeMap = Json::import(&path_binance_har).unwrap_or_default(); @@ -104,7 +107,7 @@ impl Binance { } pub fn fetch_1mn_prices() -> color_eyre::Result> { - log("binance: fetch 1mn"); + info!("binance: fetch 1mn"); retry( |_| { @@ -151,7 +154,7 @@ impl Binance { } pub fn fetch_daily_prices() -> color_eyre::Result> { - log("binance: fetch 1d"); + info!("binance: fetch 1d"); retry( |_| { diff --git a/parser/src/price/kibo.rs b/src/parser/price/kibo.rs similarity index 94% rename from parser/src/price/kibo.rs rename to src/parser/price/kibo.rs index 51a48adc0..031fcf399 100644 --- a/parser/src/price/kibo.rs +++ b/src/parser/price/kibo.rs @@ -3,12 +3,12 @@ use std::{collections::BTreeMap, str::FromStr}; use chrono::NaiveDate; use color_eyre::eyre::ContextCompat; use itertools::Itertools; +use log::info; use serde_json::Value; use crate::{ - structs::{Date, DateMapChunkId, HeightMapChunkId, OHLC}, - utils::{log, retry}, - MapChunkId, + structs::{Date, DateMapChunkId, HeightMapChunkId, MapChunkId, OHLC}, + utils::retry, }; pub struct Kibo; @@ -28,7 +28,7 @@ impl Kibo { } pub fn fetch_height_prices(chunk_id: HeightMapChunkId) -> color_eyre::Result> { - log("kibo: fetch height prices"); + info!("kibo: fetch height prices"); retry( |try_index| { @@ -61,7 +61,7 @@ impl Kibo { } pub fn fetch_date_prices(chunk_id: DateMapChunkId) -> color_eyre::Result> { - log("kibo: fetch date prices"); + info!("kibo: fetch date prices"); retry( |try_index| { diff --git a/parser/src/price/kraken.rs b/src/parser/price/kraken.rs similarity index 97% rename from parser/src/price/kraken.rs rename to src/parser/price/kraken.rs index 7567964b1..dc7e403f4 100644 --- a/parser/src/price/kraken.rs +++ b/src/parser/price/kraken.rs @@ -1,18 +1,19 @@ use std::collections::BTreeMap; use color_eyre::eyre::ContextCompat; +use log::info; use serde_json::Value; use crate::{ structs::{Date, Timestamp, OHLC}, - utils::{log, retry}, + utils::retry, }; pub struct Kraken; impl Kraken { pub fn fetch_1mn_prices() -> color_eyre::Result> { - log("kraken: fetch 1mn"); + info!("kraken: fetch 1mn"); retry( |_| { @@ -66,7 +67,7 @@ impl Kraken { } pub fn fetch_daily_prices() -> color_eyre::Result> { - log("fetch kraken daily"); + info!("fetch kraken daily"); retry( |_| { diff --git a/parser/src/price/mod.rs b/src/parser/price/mod.rs similarity index 100% rename from parser/src/price/mod.rs rename to src/parser/price/mod.rs diff --git a/src/parser/states/_trait.rs b/src/parser/states/_trait.rs new file mode 100644 index 000000000..b8b400cc3 --- /dev/null +++ b/src/parser/states/_trait.rs @@ -0,0 +1,39 @@ +use std::{ + fmt::Debug, + fs, io, + path::{Path, PathBuf}, +}; + +use bincode::{Decode, Encode}; +use serde::{de::DeserializeOwned, Serialize}; + +use crate::{io::Serialization, structs::Config}; + +// https://github.com/djkoloski/rust_serialization_benchmark +pub trait AnyState +where + Self: Debug + Encode + Decode + Serialize + DeserializeOwned, +{ + fn name<'a>() -> &'a str; + + fn path(config: &Config) -> PathBuf { + config.path_states().join(Self::name()) + } + + fn reset(&mut self, config: &Config) -> color_eyre::Result<(), io::Error> { + self.clear(); + fs::remove_file(Self::path(config)) + } + + fn import(config: &Config) -> color_eyre::Result { + let path = Self::path(config); + fs::create_dir_all(&path)?; + Serialization::Binary.import(&path) + } + + fn export(&self, config: &Config) -> color_eyre::Result<()> { + Serialization::Binary.export(Path::new(&Self::path(config)), self) + } + + fn clear(&mut self); +} diff --git a/parser/src/states/cohorts_states/address/cohort_durable_states.rs b/src/parser/states/cohorts_states/address/cohort_durable_states.rs similarity index 97% rename from parser/src/states/cohorts_states/address/cohort_durable_states.rs rename to src/parser/states/cohorts_states/address/cohort_durable_states.rs index 55c367ce8..8bcbb201b 100644 --- a/parser/src/states/cohorts_states/address/cohort_durable_states.rs +++ b/src/parser/states/cohorts_states/address/cohort_durable_states.rs @@ -3,7 +3,7 @@ use std::ops::AddAssign; use allocative::Allocative; use crate::{ - states::{DurableStates, IsZero, OneShotStates, PriceToValue, UnrealizedState}, + parser::states::{DurableStates, IsZero, OneShotStates, PriceToValue, UnrealizedState}, structs::{Amount, Price}, }; diff --git a/parser/src/states/cohorts_states/address/cohort_id.rs b/src/parser/states/cohorts_states/address/cohort_id.rs similarity index 100% rename from parser/src/states/cohorts_states/address/cohort_id.rs rename to src/parser/states/cohorts_states/address/cohort_id.rs diff --git a/parser/src/states/cohorts_states/address/cohorts_durable_states.rs b/src/parser/states/cohorts_states/address/cohorts_durable_states.rs similarity index 99% rename from parser/src/states/cohorts_states/address/cohorts_durable_states.rs rename to src/parser/states/cohorts_states/address/cohorts_durable_states.rs index 932143461..0dece4f9d 100644 --- a/parser/src/states/cohorts_states/address/cohorts_durable_states.rs +++ b/src/parser/states/cohorts_states/address/cohorts_durable_states.rs @@ -5,7 +5,7 @@ use derive_deref::{Deref, DerefMut}; use rayon::prelude::*; use crate::{ - databases::AddressIndexToAddressData, + parser::databases::AddressIndexToAddressData, structs::{AddressData, AddressRealizedData, Amount, Price}, }; diff --git a/parser/src/states/cohorts_states/address/cohorts_input_states.rs b/src/parser/states/cohorts_states/address/cohorts_input_states.rs similarity index 97% rename from parser/src/states/cohorts_states/address/cohorts_input_states.rs rename to src/parser/states/cohorts_states/address/cohorts_input_states.rs index 5f6ef9afa..0da4376c5 100644 --- a/parser/src/states/cohorts_states/address/cohorts_input_states.rs +++ b/src/parser/states/cohorts_states/address/cohorts_input_states.rs @@ -1,7 +1,7 @@ use derive_deref::{Deref, DerefMut}; use crate::{ - states::InputState, + parser::states::InputState, structs::{AddressRealizedData, Amount, LiquidityClassification}, }; diff --git a/parser/src/states/cohorts_states/address/cohorts_one_shot_states.rs b/src/parser/states/cohorts_states/address/cohorts_one_shot_states.rs similarity index 81% rename from parser/src/states/cohorts_states/address/cohorts_one_shot_states.rs rename to src/parser/states/cohorts_states/address/cohorts_one_shot_states.rs index 042e44ea4..bda560fa6 100644 --- a/parser/src/states/cohorts_states/address/cohorts_one_shot_states.rs +++ b/src/parser/states/cohorts_states/address/cohorts_one_shot_states.rs @@ -1,6 +1,6 @@ use derive_deref::{Deref, DerefMut}; -use crate::states::OneShotStates; +use crate::parser::states::OneShotStates; use super::SplitByAddressCohort; diff --git a/parser/src/states/cohorts_states/address/cohorts_output_states.rs b/src/parser/states/cohorts_states/address/cohorts_output_states.rs similarity index 97% rename from parser/src/states/cohorts_states/address/cohorts_output_states.rs rename to src/parser/states/cohorts_states/address/cohorts_output_states.rs index d0a682129..155ef98d2 100644 --- a/parser/src/states/cohorts_states/address/cohorts_output_states.rs +++ b/src/parser/states/cohorts_states/address/cohorts_output_states.rs @@ -1,7 +1,7 @@ use derive_deref::{Deref, DerefMut}; use crate::{ - states::OutputState, + parser::states::OutputState, structs::{AddressRealizedData, Amount, LiquidityClassification}, }; diff --git a/parser/src/states/cohorts_states/address/cohorts_realized_states.rs b/src/parser/states/cohorts_states/address/cohorts_realized_states.rs similarity index 98% rename from parser/src/states/cohorts_states/address/cohorts_realized_states.rs rename to src/parser/states/cohorts_states/address/cohorts_realized_states.rs index 524cc31f6..22d2a228b 100644 --- a/parser/src/states/cohorts_states/address/cohorts_realized_states.rs +++ b/src/parser/states/cohorts_states/address/cohorts_realized_states.rs @@ -1,7 +1,7 @@ use derive_deref::{Deref, DerefMut}; use crate::{ - states::RealizedState, + parser::states::RealizedState, structs::{AddressRealizedData, LiquidityClassification, Price}, }; diff --git a/parser/src/states/cohorts_states/address/mod.rs b/src/parser/states/cohorts_states/address/mod.rs similarity index 100% rename from parser/src/states/cohorts_states/address/mod.rs rename to src/parser/states/cohorts_states/address/mod.rs diff --git a/parser/src/states/cohorts_states/address/split_by_address_cohort.rs b/src/parser/states/cohorts_states/address/split_by_address_cohort.rs similarity index 100% rename from parser/src/states/cohorts_states/address/split_by_address_cohort.rs rename to src/parser/states/cohorts_states/address/split_by_address_cohort.rs diff --git a/parser/src/states/cohorts_states/any/capitalization_state.rs b/src/parser/states/cohorts_states/any/capitalization_state.rs similarity index 100% rename from parser/src/states/cohorts_states/any/capitalization_state.rs rename to src/parser/states/cohorts_states/any/capitalization_state.rs diff --git a/parser/src/states/cohorts_states/any/durable_states.rs b/src/parser/states/cohorts_states/any/durable_states.rs similarity index 100% rename from parser/src/states/cohorts_states/any/durable_states.rs rename to src/parser/states/cohorts_states/any/durable_states.rs diff --git a/parser/src/states/cohorts_states/any/input_state.rs b/src/parser/states/cohorts_states/any/input_state.rs similarity index 100% rename from parser/src/states/cohorts_states/any/input_state.rs rename to src/parser/states/cohorts_states/any/input_state.rs diff --git a/parser/src/states/cohorts_states/any/mod.rs b/src/parser/states/cohorts_states/any/mod.rs similarity index 100% rename from parser/src/states/cohorts_states/any/mod.rs rename to src/parser/states/cohorts_states/any/mod.rs diff --git a/parser/src/states/cohorts_states/any/one_shot_states.rs b/src/parser/states/cohorts_states/any/one_shot_states.rs similarity index 100% rename from parser/src/states/cohorts_states/any/one_shot_states.rs rename to src/parser/states/cohorts_states/any/one_shot_states.rs diff --git a/parser/src/states/cohorts_states/any/output_state.rs b/src/parser/states/cohorts_states/any/output_state.rs similarity index 100% rename from parser/src/states/cohorts_states/any/output_state.rs rename to src/parser/states/cohorts_states/any/output_state.rs diff --git a/parser/src/states/cohorts_states/any/price_paid_state.rs b/src/parser/states/cohorts_states/any/price_paid_state.rs similarity index 100% rename from parser/src/states/cohorts_states/any/price_paid_state.rs rename to src/parser/states/cohorts_states/any/price_paid_state.rs diff --git a/parser/src/states/cohorts_states/any/price_to_value.rs b/src/parser/states/cohorts_states/any/price_to_value.rs similarity index 100% rename from parser/src/states/cohorts_states/any/price_to_value.rs rename to src/parser/states/cohorts_states/any/price_to_value.rs diff --git a/parser/src/states/cohorts_states/any/realized_state.rs b/src/parser/states/cohorts_states/any/realized_state.rs similarity index 100% rename from parser/src/states/cohorts_states/any/realized_state.rs rename to src/parser/states/cohorts_states/any/realized_state.rs diff --git a/parser/src/states/cohorts_states/any/supply_state.rs b/src/parser/states/cohorts_states/any/supply_state.rs similarity index 100% rename from parser/src/states/cohorts_states/any/supply_state.rs rename to src/parser/states/cohorts_states/any/supply_state.rs diff --git a/parser/src/states/cohorts_states/any/unrealized_state.rs b/src/parser/states/cohorts_states/any/unrealized_state.rs similarity index 100% rename from parser/src/states/cohorts_states/any/unrealized_state.rs rename to src/parser/states/cohorts_states/any/unrealized_state.rs diff --git a/parser/src/states/cohorts_states/any/utxo_state.rs b/src/parser/states/cohorts_states/any/utxo_state.rs similarity index 100% rename from parser/src/states/cohorts_states/any/utxo_state.rs rename to src/parser/states/cohorts_states/any/utxo_state.rs diff --git a/parser/src/states/cohorts_states/mod.rs b/src/parser/states/cohorts_states/mod.rs similarity index 100% rename from parser/src/states/cohorts_states/mod.rs rename to src/parser/states/cohorts_states/mod.rs diff --git a/parser/src/states/cohorts_states/utxo/cohort_durable_states.rs b/src/parser/states/cohorts_states/utxo/cohort_durable_states.rs similarity index 97% rename from parser/src/states/cohorts_states/utxo/cohort_durable_states.rs rename to src/parser/states/cohorts_states/utxo/cohort_durable_states.rs index af47446e4..13ccce07a 100644 --- a/parser/src/states/cohorts_states/utxo/cohort_durable_states.rs +++ b/src/parser/states/cohorts_states/utxo/cohort_durable_states.rs @@ -1,7 +1,7 @@ use allocative::Allocative; use crate::{ - states::{DurableStates, OneShotStates, PriceToValue, UnrealizedState}, + parser::states::{DurableStates, OneShotStates, PriceToValue, UnrealizedState}, structs::{Amount, Price}, }; diff --git a/parser/src/states/cohorts_states/utxo/cohort_filter.rs b/src/parser/states/cohorts_states/utxo/cohort_filter.rs similarity index 100% rename from parser/src/states/cohorts_states/utxo/cohort_filter.rs rename to src/parser/states/cohorts_states/utxo/cohort_filter.rs diff --git a/parser/src/states/cohorts_states/utxo/cohort_filters.rs b/src/parser/states/cohorts_states/utxo/cohort_filters.rs similarity index 100% rename from parser/src/states/cohorts_states/utxo/cohort_filters.rs rename to src/parser/states/cohorts_states/utxo/cohort_filters.rs diff --git a/parser/src/states/cohorts_states/utxo/cohort_id.rs b/src/parser/states/cohorts_states/utxo/cohort_id.rs similarity index 100% rename from parser/src/states/cohorts_states/utxo/cohort_id.rs rename to src/parser/states/cohorts_states/utxo/cohort_id.rs diff --git a/parser/src/states/cohorts_states/utxo/cohorts_durable_states.rs b/src/parser/states/cohorts_states/utxo/cohorts_durable_states.rs similarity index 99% rename from parser/src/states/cohorts_states/utxo/cohorts_durable_states.rs rename to src/parser/states/cohorts_states/utxo/cohorts_durable_states.rs index 16cafddcf..2ba92dbf2 100644 --- a/parser/src/states/cohorts_states/utxo/cohorts_durable_states.rs +++ b/src/parser/states/cohorts_states/utxo/cohorts_durable_states.rs @@ -3,7 +3,7 @@ use derive_deref::{Deref, DerefMut}; use rayon::prelude::*; use crate::{ - states::DateDataVec, + parser::states::DateDataVec, structs::{Amount, BlockData, Price, SentData, Timestamp}, }; diff --git a/parser/src/states/cohorts_states/utxo/cohorts_one_shot_states.rs b/src/parser/states/cohorts_states/utxo/cohorts_one_shot_states.rs similarity index 81% rename from parser/src/states/cohorts_states/utxo/cohorts_one_shot_states.rs rename to src/parser/states/cohorts_states/utxo/cohorts_one_shot_states.rs index efd4db6a5..328fddf7a 100644 --- a/parser/src/states/cohorts_states/utxo/cohorts_one_shot_states.rs +++ b/src/parser/states/cohorts_states/utxo/cohorts_one_shot_states.rs @@ -1,6 +1,6 @@ use derive_deref::{Deref, DerefMut}; -use crate::states::OneShotStates; +use crate::parser::states::OneShotStates; use super::SplitByUTXOCohort; diff --git a/parser/src/states/cohorts_states/utxo/cohorts_sent_states.rs b/src/parser/states/cohorts_states/utxo/cohorts_sent_states.rs similarity index 98% rename from parser/src/states/cohorts_states/utxo/cohorts_sent_states.rs rename to src/parser/states/cohorts_states/utxo/cohorts_sent_states.rs index 9b0da8a05..f09d908f7 100644 --- a/parser/src/states/cohorts_states/utxo/cohorts_sent_states.rs +++ b/src/parser/states/cohorts_states/utxo/cohorts_sent_states.rs @@ -3,7 +3,7 @@ use std::{cmp::Ordering, collections::BTreeMap}; use derive_deref::{Deref, DerefMut}; use crate::{ - states::{DateDataVec, InputState, RealizedState}, + parser::states::{DateDataVec, InputState, RealizedState}, structs::{BlockPath, Price, SentData, Timestamp}, }; diff --git a/parser/src/states/cohorts_states/utxo/mod.rs b/src/parser/states/cohorts_states/utxo/mod.rs similarity index 100% rename from parser/src/states/cohorts_states/utxo/mod.rs rename to src/parser/states/cohorts_states/utxo/mod.rs diff --git a/parser/src/states/cohorts_states/utxo/split_by_utxo_cohort.rs b/src/parser/states/cohorts_states/utxo/split_by_utxo_cohort.rs similarity index 100% rename from parser/src/states/cohorts_states/utxo/split_by_utxo_cohort.rs rename to src/parser/states/cohorts_states/utxo/split_by_utxo_cohort.rs diff --git a/parser/src/states/counters.rs b/src/parser/states/counters.rs similarity index 100% rename from parser/src/states/counters.rs rename to src/parser/states/counters.rs diff --git a/parser/src/states/date_data_vec.rs b/src/parser/states/date_data_vec.rs similarity index 100% rename from parser/src/states/date_data_vec.rs rename to src/parser/states/date_data_vec.rs diff --git a/parser/src/states/mod.rs b/src/parser/states/mod.rs similarity index 58% rename from parser/src/states/mod.rs rename to src/parser/states/mod.rs index 878b6d672..f67cec892 100644 --- a/parser/src/states/mod.rs +++ b/src/parser/states/mod.rs @@ -11,8 +11,9 @@ use allocative::Allocative; pub use cohorts_states::*; use counters::*; use date_data_vec::*; +use log::info; -use crate::utils::log; +use crate::structs::Config; #[derive(Default, Allocative)] pub struct States { @@ -23,12 +24,10 @@ pub struct States { } impl States { - pub fn import() -> color_eyre::Result { - let date_data_vec_handle = thread::spawn(DateDataVec::import); + pub fn import(config: &Config) -> color_eyre::Result { + let date_data_vec = DateDataVec::import(config)?; - let address_counters = Counters::import()?; - - let date_data_vec = date_data_vec_handle.join().unwrap()?; + let address_counters = Counters::import(config)?; Ok(Self { address_cohorts_durable_states: None, @@ -38,24 +37,24 @@ impl States { }) } - pub fn reset(&mut self, include_addresses: bool) { - log("Reseting all states..."); + pub fn reset(&mut self, config: &Config, include_addresses: bool) { + info!("Reseting all states..."); - let _ = self.date_data_vec.reset(); + let _ = self.date_data_vec.reset(config); self.utxo_cohorts_durable_states = None; if include_addresses { - let _ = self.address_counters.reset(); + let _ = self.address_counters.reset(config); self.address_cohorts_durable_states = None; } } - pub fn export(&self) -> color_eyre::Result<()> { + pub fn export(&self, config: &Config) -> color_eyre::Result<()> { thread::scope(|s| { - s.spawn(|| self.address_counters.export().unwrap()); - s.spawn(|| self.date_data_vec.export().unwrap()); + s.spawn(|| self.address_counters.export(config).unwrap()); + s.spawn(|| self.date_data_vec.export(config).unwrap()); }); Ok(()) diff --git a/server/src/api/handlers/dataset.rs b/src/server/api/handlers/dataset.rs similarity index 80% rename from server/src/api/handlers/dataset.rs rename to src/server/api/handlers/dataset.rs index b0ab851f8..c1fa63cec 100644 --- a/server/src/api/handlers/dataset.rs +++ b/src/server/api/handlers/dataset.rs @@ -9,15 +9,17 @@ use color_eyre::{eyre::eyre, owo_colors::OwoColorize}; use reqwest::StatusCode; use serde::Deserialize; -use parser::{ - log, Date, DateMap, Height, HeightMap, Json, MapChunkId, COMPRESSED_BIN_EXTENSION, - HEIGHT_MAP_CHUNK_SIZE, JSON_EXTENSION, OHLC, -}; - use crate::{ - api::structs::{Chunk, Kind, Route}, - header_map::HeaderMapUtils, - AppState, + io::{Json, COMPRESSED_BIN_EXTENSION, JSON_EXTENSION}, + server::{ + api::{ + structs::{Chunk, Kind, Route}, + API_URL_PREFIX, + }, + header_map::HeaderMapUtils, + log_result, AppState, + }, + structs::{Date, DateMap, Height, HeightMap, MapChunkId, HEIGHT_MAP_CHUNK_SIZE, OHLC}, }; use super::{ @@ -37,11 +39,32 @@ pub async fn dataset_handler( query: Query, State(app_state): State, ) -> Response { - match _dataset_handler(headers, path, query, app_state) { - Ok(response) => response, + match _dataset_handler(headers, &path, &query, app_state) { + Ok(response) => { + log_result( + response.status(), + &format!( + "{API_URL_PREFIX}/{}{}{}", + path.0, + query.chunk.map_or("".to_string(), |chunk| format!( + "{}{chunk}", + "?chunk=".bright_black() + )), + query.all.map_or("".to_string(), |all| format!( + "{}{all}", + "?all=".bright_black() + )) + ), + ); + + response + } Err(error) => { - let mut response = - (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response(); + let code = StatusCode::INTERNAL_SERVER_ERROR; + + log_result(code, &format!("{API_URL_PREFIX}/{}", path.0)); + + let mut response = (code, error.to_string()).into_response(); response.headers_mut().insert_cors(); @@ -55,27 +78,14 @@ const HEIGHT_PREFIX: &str = "height-to-"; fn _dataset_handler( headers: HeaderMap, - Path(path): Path, - query: Query, + Path(path): &Path, + query: &Query, AppState { routes }: AppState, ) -> color_eyre::Result { if query.chunk.is_some() && query.all.is_some() { return Err(eyre!("chunk and all are exclusive")); } - log(&format!( - "{}{}{}", - path, - query.chunk.map_or("".to_string(), |chunk| format!( - "{}{chunk}", - "?chunk=".bright_black() - )), - query.all.map_or("".to_string(), |all| format!( - "{}{all}", - "?all=".bright_black() - )) - )); - let (kind, id, route) = if path.starts_with(DATE_PREFIX) { let id = convert_path_to_id(path.strip_prefix(DATE_PREFIX).unwrap()); let route = routes.date.get(&id); @@ -85,7 +95,7 @@ fn _dataset_handler( let route = routes.height.get(&id); (Kind::Height, id, route) } else { - let id = convert_path_to_id(&path); + let id = convert_path_to_id(path); let route = routes.last.get(&id); (Kind::Last, id, route) }; @@ -121,7 +131,7 @@ fn _dataset_handler( } } - let (date, response) = headers.check_if_modified_since(&route.file_path).unwrap(); + let (date, response) = headers.check_if_modified_since(&route.file_path)?; if let Some(response) = response { return Ok(response); @@ -163,7 +173,7 @@ fn process_datasets( kind: Kind, chunk: &mut Option, route: &mut Route, - query: Query, + query: &Query, datasets: BTreeMap, ) -> color_eyre::Result<()> where diff --git a/server/src/api/handlers/extension.rs b/src/server/api/handlers/extension.rs similarity index 100% rename from server/src/api/handlers/extension.rs rename to src/server/api/handlers/extension.rs diff --git a/server/src/api/handlers/fallback.rs b/src/server/api/handlers/fallback.rs similarity index 94% rename from server/src/api/handlers/fallback.rs rename to src/server/api/handlers/fallback.rs index 158261d07..3ce9c7618 100644 --- a/server/src/api/handlers/fallback.rs +++ b/src/server/api/handlers/fallback.rs @@ -1,7 +1,7 @@ use axum::{extract::State, http::HeaderMap, response::Response}; use reqwest::header::HOST; -use crate::AppState; +use crate::server::AppState; use super::response::{generic_to_reponse, update_reponse_headers}; diff --git a/server/src/api/handlers/mod.rs b/src/server/api/handlers/mod.rs similarity index 100% rename from server/src/api/handlers/mod.rs rename to src/server/api/handlers/mod.rs diff --git a/server/src/api/handlers/response.rs b/src/server/api/handlers/response.rs similarity index 96% rename from server/src/api/handlers/response.rs rename to src/server/api/handlers/response.rs index edac89415..449c73dd1 100644 --- a/server/src/api/handlers/response.rs +++ b/src/server/api/handlers/response.rs @@ -2,13 +2,15 @@ use std::fmt::Debug; use axum::response::{IntoResponse, Json, Response}; use bincode::Decode; -use parser::{Date, MapValue, SerializedBTreeMap, SerializedVec}; use serde::de::DeserializeOwned; use serde::Serialize; use crate::{ - api::structs::{Chunk, Kind, Route}, - header_map::HeaderMapUtils, + server::{ + api::structs::{Chunk, Kind, Route}, + header_map::HeaderMapUtils, + }, + structs::{Date, MapValue, SerializedBTreeMap, SerializedVec}, }; use super::extension::Extension; diff --git a/server/src/api/mod.rs b/src/server/api/mod.rs similarity index 50% rename from server/src/api/mod.rs rename to src/server/api/mod.rs index f1690fc30..5dfdc8871 100644 --- a/server/src/api/mod.rs +++ b/src/server/api/mod.rs @@ -1,19 +1,21 @@ use axum::{routing::get, Router}; use handlers::{dataset_handler, fallback}; -use crate::AppState; +use super::AppState; mod handlers; pub mod structs; +pub const API_URL_PREFIX: &str = "/api"; + pub trait ApiRoutes { fn add_api_routes(self) -> Self; } impl ApiRoutes for Router { fn add_api_routes(self) -> Self { - self.route("/api/*path", get(dataset_handler)) - .route("/api/", get(fallback)) - .route("/api", get(fallback)) + self.route(&format!("{API_URL_PREFIX}/*path"), get(dataset_handler)) + .route(&format!("{API_URL_PREFIX}/"), get(fallback)) + .route(API_URL_PREFIX, get(fallback)) } } diff --git a/server/src/api/structs/chunk.rs b/src/server/api/structs/chunk.rs similarity index 100% rename from server/src/api/structs/chunk.rs rename to src/server/api/structs/chunk.rs diff --git a/server/src/api/structs/kind.rs b/src/server/api/structs/kind.rs similarity index 100% rename from server/src/api/structs/kind.rs rename to src/server/api/structs/kind.rs diff --git a/server/src/api/structs/mod.rs b/src/server/api/structs/mod.rs similarity index 100% rename from server/src/api/structs/mod.rs rename to src/server/api/structs/mod.rs diff --git a/server/src/api/structs/paths.rs b/src/server/api/structs/paths.rs similarity index 88% rename from server/src/api/structs/paths.rs rename to src/server/api/structs/paths.rs index 1461e0138..9e707278f 100644 --- a/server/src/api/structs/paths.rs +++ b/src/server/api/structs/paths.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use derive_deref::{Deref, DerefMut}; use serde::Serialize; -use crate::Grouped; +use crate::server::Grouped; #[derive(Clone, Default, Deref, DerefMut, Debug, Serialize)] pub struct Paths(pub Grouped>); diff --git a/src/server/api/structs/routes.rs b/src/server/api/structs/routes.rs new file mode 100644 index 000000000..8a5caf4bc --- /dev/null +++ b/src/server/api/structs/routes.rs @@ -0,0 +1,159 @@ +use std::{ + collections::{BTreeMap, HashMap}, + path::PathBuf, +}; + +use derive_deref::{Deref, DerefMut}; +use itertools::Itertools; + +use crate::{ + io::Serialization, + server::{api::API_URL_PREFIX, Grouped}, + structs::Config, +}; + +use super::Paths; + +#[derive(Clone, Debug)] +pub struct Route { + pub url_path: String, + pub file_path: PathBuf, + pub values_type: String, + pub serialization: Serialization, +} + +#[derive(Clone, Default, Deref, DerefMut)] +pub struct Routes(pub Grouped>); + +const WEBSITE_TYPES_PATH: &str = "../website/scripts/types"; + +impl Routes { + pub fn build(paths_to_type: BTreeMap, config: &Config) -> Self { + let mut routes = Routes::default(); + + paths_to_type.into_iter().for_each(|(file_path, value)| { + let serialization = + Serialization::try_from(&file_path).unwrap_or(Serialization::Binary); + + let file_path_ser = file_path.to_str().unwrap().to_owned(); + let split_key = file_path_ser.replace( + &format!("{}/", config.path_datasets().to_str().unwrap()), + "", + ); + let split_key = + split_key.replace(&format!("{}/", config.path_price().to_str().unwrap()), ""); + let mut split_key = split_key.split('/').collect_vec(); + let last = split_key.pop().unwrap().to_owned(); + let last = last.split('.').next().unwrap(); + + // Use case for: "../datasets/last": "Value", + if split_key.is_empty() { + split_key.push("last"); + } + + let map_key = split_key.iter().join("_"); + + let url_path = split_key.iter().join("-"); + + let values_type = value.to_owned(); + + match last { + "date" => { + routes.date.insert( + map_key, + Route { + url_path: format!("date-to-{url_path}"), + file_path, + values_type, + serialization, + }, + ); + } + "height" => { + routes.height.insert( + map_key, + Route { + url_path: format!("height-to-{url_path}"), + file_path, + values_type, + serialization, + }, + ); + } + "last" => { + routes.last.insert( + map_key, + Route { + url_path, + file_path, + values_type, + serialization, + }, + ); + } + _ => { + dbg!(&file_path, value, &last, &split_key); + panic!("") + } + } + }); + + routes + } + + pub fn generate_dts_file(&self) { + let map_to_type = |name: &str, map: &HashMap| -> String { + let paths = map + .values() + .map(|route| format!("\"{}\"", route.url_path)) + .join(" | "); + + format!("export type {}Path = {};\n", name, paths) + }; + + let date_type = map_to_type("Date", &self.date); + + let height_type = map_to_type("Height", &self.height); + + let last_type = map_to_type("Last", &self.last); + + // fs::write( + // format!("{WEBSITE_TYPES_PATH}/paths.d.ts"), + // format!("// This file is auto generated by the server\n// Manual changes are forbidden\n\n{date_type}\n{height_type}\n{last_type}"), + // ) + // .unwrap(); + } + + pub fn to_full_paths(&self, host: String) -> Paths { + let url = { + let scheme = if host.contains("0.0.0.0") || host.contains("localhost") { + "http" + } else { + "https" + }; + + format!("{scheme}://{host}") + }; + + let transform = |map: &HashMap| -> BTreeMap { + map.iter() + .map(|(key, route)| { + ( + key.to_owned(), + format!("{url}{API_URL_PREFIX}/{}", route.url_path.to_owned()), + ) + }) + .collect() + }; + + let date_paths = transform(&self.date); + let height_paths = transform(&self.height); + let last_paths = transform(&self.last); + + Paths(Grouped { + date: date_paths, + height: height_paths, + last: last_paths, + }) + } +} diff --git a/server/src/header_map.rs b/src/server/header_map.rs similarity index 98% rename from server/src/header_map.rs rename to src/server/header_map.rs index f1af0527b..145f42796 100644 --- a/server/src/header_map.rs +++ b/src/server/header_map.rs @@ -6,7 +6,7 @@ use axum::{ response::IntoResponse, }; use chrono::{DateTime, Timelike, Utc}; -use parser::log; +use log::info; use reqwest::{ header::{HOST, IF_MODIFIED_SINCE}, StatusCode, @@ -163,7 +163,7 @@ impl HeaderMapUtils for HeaderMap { "png" => self.insert_content_type_image_png(), "webmanifest" => self.insert_content_type_application_manifest_json(), extension => { - log(&format!("Extension unsupported: {extension}")); + info!("Extension unsupported: {extension}"); panic!() } } diff --git a/server/src/main.rs b/src/server/mod.rs similarity index 55% rename from server/src/main.rs rename to src/server/mod.rs index 3aa94b4f6..4d0de972b 100644 --- a/server/src/main.rs +++ b/src/server/mod.rs @@ -1,13 +1,17 @@ -use std::sync::Arc; +use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; use api::{structs::Routes, ApiRoutes}; -use axum::{serve, Router}; -use parser::{log, reset_logs}; +use axum::{routing::get, serve, Router}; +use color_eyre::owo_colors::OwoColorize; +use log::{error, info}; +use reqwest::StatusCode; use serde::Serialize; use tokio::net::TcpListener; use tower_http::compression::CompressionLayer; use website::WebsiteRoutes; +use crate::structs::Config; + mod api; mod header_map; mod website; @@ -24,13 +28,11 @@ pub struct AppState { routes: Arc, } -#[tokio::main] -async fn main() -> color_eyre::Result<()> { - color_eyre::install()?; - - reset_logs(); - - let routes = Routes::build(); +pub async fn main( + paths_to_type: BTreeMap, + config: &Config, +) -> color_eyre::Result<()> { + let routes = Routes::build(paths_to_type, config); routes.generate_dts_file(); @@ -47,6 +49,7 @@ async fn main() -> color_eyre::Result<()> { let router = Router::new() .add_api_routes() .add_website_routes() + .route("/version", get(env!("CARGO_PKG_VERSION"))) .with_state(state) .layer(compression_layer); @@ -61,10 +64,19 @@ async fn main() -> color_eyre::Result<()> { port += 1; } - log(&format!("Starting server on port {port}...")); + info!("Starting server on port {port}..."); let listener = listener.unwrap(); serve(listener, router).await?; Ok(()) } + +pub fn log_result(code: StatusCode, path: &str) { + match code { + StatusCode::INTERNAL_SERVER_ERROR => error!("{} {}", code.as_u16().red(), path), + StatusCode::NOT_MODIFIED => info!("{} {}", code.as_u16().bright_black(), path), + StatusCode::OK => info!("{} {}", code.as_u16().green(), path), + _ => error!("{} {}", code.as_u16().red(), path), + } +} diff --git a/server/src/website/handlers/_minify.rs b/src/server/website/handlers/_minify.rs similarity index 100% rename from server/src/website/handlers/_minify.rs rename to src/server/website/handlers/_minify.rs diff --git a/server/src/website/handlers/file.rs b/src/server/website/handlers/file.rs similarity index 53% rename from server/src/website/handlers/file.rs rename to src/server/website/handlers/file.rs index 6f1e4f636..3c6159a53 100644 --- a/server/src/website/handlers/file.rs +++ b/src/server/website/handlers/file.rs @@ -9,54 +9,77 @@ use axum::{ http::HeaderMap, response::{IntoResponse, Response}, }; -use parser::log; +use log::{error, info}; use reqwest::StatusCode; -use crate::header_map::HeaderMapUtils; +use crate::server::{header_map::HeaderMapUtils, log_result}; use super::minify_js; -const WEBSITE_PATH: &str = "../website/"; +const WEBSITE_PATH: &str = "./src/website/"; pub async fn file_handler(headers: HeaderMap, path: extract::Path) -> Response { - let mut path = path.0.replace("..", "").replace("\\", ""); - - if path.ends_with("Cargo.toml") { - path = "../server/Cargo.toml".to_owned(); - } - - let mut path = str_to_path(&path); - - if !path.exists() { - if path.extension().is_some() { - let mut response: Response = ( - StatusCode::INTERNAL_SERVER_ERROR, - "File doesn't exist".to_string(), - ) - .into_response(); - - response.headers_mut().insert_cors(); - - return response; - } else { - path = str_to_path("index.html"); - } - } - - path_to_response(headers, &path) + any_handler(headers, Some(path)) } pub async fn index_handler(headers: HeaderMap) -> Response { - path_to_response(headers, &str_to_path("index.html")) + any_handler(headers, None) } -fn path_to_response(headers: HeaderMap, path: &Path) -> Response { - log(&path.to_str().unwrap().replace(WEBSITE_PATH, "")); +fn any_handler(headers: HeaderMap, path: Option>) -> Response { + let response = if let Some(path) = path.as_ref() { + let path = path.0.replace("..", "").replace("\\", ""); - let (date, response) = headers.check_if_modified_since(path).unwrap(); + let mut path = str_to_path(&path); + + if !path.exists() { + if path.extension().is_some() { + let mut response: Response = ( + StatusCode::INTERNAL_SERVER_ERROR, + "File doesn't exist".to_string(), + ) + .into_response(); + + response.headers_mut().insert_cors(); + + return response; + } else { + path = str_to_path("index.html"); + } + } + + path_to_response(&headers, &path) + } else { + path_to_response(&headers, &str_to_path("index.html")) + }; + + log_result( + response.status(), + &format!("/{}", path.map_or("".to_owned(), |p| p.0)), + ); + + response +} + +fn path_to_response(headers: &HeaderMap, path: &Path) -> Response { + match _path_to_response(headers, path) { + Ok(response) => response, + Err(error) => { + let mut response = + (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response(); + + response.headers_mut().insert_cors(); + + response + } + } +} + +fn _path_to_response(headers: &HeaderMap, path: &Path) -> color_eyre::Result { + let (date, response) = headers.check_if_modified_since(path)?; if let Some(response) = response { - return response; + return Ok(response); } let mut response; @@ -74,9 +97,9 @@ fn path_to_response(headers: HeaderMap, path: &Path) -> Response { response = Response::new(content.into()); } else { let content = fs::read(path).unwrap_or_else(|error| { - log(&error.to_string()); + error!("{error}"); let path = path.to_str().unwrap(); - log(&format!("Can't read file {path}")); + info!("Can't read file {path}"); panic!("") }); @@ -108,7 +131,7 @@ fn path_to_response(headers: HeaderMap, path: &Path) -> Response { headers.insert_last_modified(date); - response + Ok(response) } fn str_to_path(path: &str) -> PathBuf { diff --git a/server/src/website/handlers/minify.rs b/src/server/website/handlers/minify.rs similarity index 100% rename from server/src/website/handlers/minify.rs rename to src/server/website/handlers/minify.rs diff --git a/server/src/website/handlers/mod.rs b/src/server/website/handlers/mod.rs similarity index 100% rename from server/src/website/handlers/mod.rs rename to src/server/website/handlers/mod.rs diff --git a/server/src/website/mod.rs b/src/server/website/mod.rs similarity index 94% rename from server/src/website/mod.rs rename to src/server/website/mod.rs index 77cd1a1c9..fcb31516e 100644 --- a/server/src/website/mod.rs +++ b/src/server/website/mod.rs @@ -4,7 +4,7 @@ mod handlers; use handlers::{file_handler, index_handler}; -use crate::AppState; +use super::AppState; pub trait WebsiteRoutes { fn add_website_routes(self) -> Self; diff --git a/parser/src/structs/address.rs b/src/structs/address.rs similarity index 97% rename from parser/src/structs/address.rs rename to src/structs/address.rs index 7fc0255d5..b0e7930a2 100644 --- a/parser/src/structs/address.rs +++ b/src/structs/address.rs @@ -1,4 +1,4 @@ -use bitcoin_hashes::{hash160, Hash}; +use bitcoin_hashes::hash160; use biter::bitcoin::TxOut; use super::{AddressType, Counter, U8x19, U8x31}; @@ -56,7 +56,7 @@ impl Address { let hash = hash160::Hash::hash(pk); - let (prefix, rest) = Self::split_slice(&hash[..]); + let (prefix, rest) = Self::split_slice(&hash.as_byte_array()[..]); Self::P2PK((prefix, rest.into())) } else if script.is_p2pkh() { diff --git a/parser/src/structs/address_data.rs b/src/structs/address_data.rs similarity index 100% rename from parser/src/structs/address_data.rs rename to src/structs/address_data.rs diff --git a/parser/src/structs/address_liquidity.rs b/src/structs/address_liquidity.rs similarity index 100% rename from parser/src/structs/address_liquidity.rs rename to src/structs/address_liquidity.rs diff --git a/parser/src/structs/address_realized_data.rs b/src/structs/address_realized_data.rs similarity index 100% rename from parser/src/structs/address_realized_data.rs rename to src/structs/address_realized_data.rs diff --git a/parser/src/structs/address_size.rs b/src/structs/address_size.rs similarity index 100% rename from parser/src/structs/address_size.rs rename to src/structs/address_size.rs diff --git a/parser/src/structs/address_split.rs b/src/structs/address_split.rs similarity index 100% rename from parser/src/structs/address_split.rs rename to src/structs/address_split.rs diff --git a/parser/src/structs/address_type.rs b/src/structs/address_type.rs similarity index 100% rename from parser/src/structs/address_type.rs rename to src/structs/address_type.rs diff --git a/parser/src/structs/amount.rs b/src/structs/amount.rs similarity index 100% rename from parser/src/structs/amount.rs rename to src/structs/amount.rs diff --git a/parser/src/structs/any_map.rs b/src/structs/any_map.rs similarity index 53% rename from parser/src/structs/any_map.rs rename to src/structs/any_map.rs index 7f816b1c3..455a5bdfc 100644 --- a/parser/src/structs/any_map.rs +++ b/src/structs/any_map.rs @@ -2,23 +2,26 @@ use std::path::{Path, PathBuf}; use serde_json::Value; -use super::MapKind; +use super::{MapKind, MapPath}; pub trait AnyMap { fn path(&self) -> &Path; - fn path_last(&self) -> &Option; + fn path_last(&self) -> &Option; fn last_value(&self) -> Option; fn t_name(&self) -> &str; - fn exported_path_with_t_name(&self) -> Vec<(&Path, &str)> { - let t_name = self.t_name(); + fn get_paths_to_type(&self) -> Vec<(PathBuf, String)> { + let t_name = self.t_name().to_string(); if let Some(path_last) = self.path_last() { - vec![(self.path(), t_name), (path_last, t_name)] + vec![ + (self.path().to_owned(), t_name.clone()), + (path_last.unwrap().to_owned(), t_name), + ] } else { - vec![(self.path(), t_name)] + vec![(self.path().to_owned(), t_name)] } } diff --git a/parser/src/structs/array.rs b/src/structs/array.rs similarity index 100% rename from parser/src/structs/array.rs rename to src/structs/array.rs diff --git a/parser/src/structs/bi_map.rs b/src/structs/bi_map.rs similarity index 98% rename from parser/src/structs/bi_map.rs rename to src/structs/bi_map.rs index dd271ec14..87030f5b9 100644 --- a/parser/src/structs/bi_map.rs +++ b/src/structs/bi_map.rs @@ -8,7 +8,7 @@ use allocative::Allocative; use crate::utils::{LossyFrom, TARGET_BLOCKS_PER_DAY}; use super::{ - AnyDateMap, AnyHeightMap, AnyMap, Date, DateMap, Height, HeightMap, MapKind, MapValue, + AnyDateMap, AnyHeightMap, AnyMap, Date, DateMap, Height, HeightMap, MapKind, MapPath, MapValue, }; #[derive(Allocative)] @@ -24,14 +24,14 @@ impl BiMap where Value: MapValue, { - pub fn new_bin(version: u32, kind: MapKind, path: &str) -> Self { + pub fn new_bin(version: u32, kind: MapKind, path: &MapPath) -> Self { Self { height: HeightMap::_new_bin(version, kind, path, true), date: DateMap::_new_bin(version, kind, path, false), } } - pub fn new_json(version: u32, kind: MapKind, path: &str) -> Self { + pub fn new_json(version: u32, kind: MapKind, path: &MapPath) -> Self { Self { height: HeightMap::new_json(version, kind, path, true), date: DateMap::new_json(version, kind, path, false), diff --git a/parser/src/structs/block_data.rs b/src/structs/block_data.rs similarity index 100% rename from parser/src/structs/block_data.rs rename to src/structs/block_data.rs diff --git a/parser/src/structs/block_path.rs b/src/structs/block_path.rs similarity index 100% rename from parser/src/structs/block_path.rs rename to src/structs/block_path.rs diff --git a/parser/src/structs/config.rs b/src/structs/config.rs similarity index 51% rename from parser/src/structs/config.rs rename to src/structs/config.rs index db7287c24..9ad22a1c0 100644 --- a/parser/src/structs/config.rs +++ b/src/structs/config.rs @@ -6,16 +6,21 @@ use std::{ use biter::bitcoincore_rpc::Auth; use clap::Parser; use color_eyre::eyre::eyre; +use log::info; use serde::{Deserialize, Serialize}; -use crate::log; +use super::MapPath; #[derive(Parser, Debug, Clone, Default, Serialize, Deserialize, PartialEq)] #[command(version, about, long_about = None)] pub struct Config { /// Bitcoin data directory path, saved #[arg(long, value_name = "DIR")] - pub datadir: Option, + bitcoindir: Option, + + /// Kibo data directory path, saved + #[arg(long, value_name = "DIR")] + kibodir: Option, /// Bitcoin RPC ip, default: localhost, saved #[arg(long, value_name = "IP")] @@ -25,7 +30,7 @@ pub struct Config { #[arg(long, value_name = "PORT")] pub rpcport: Option, - /// Bitcoin RPC cookie file, saved + /// Bitcoin RPC cookie file, default: --bitcoindir/.cookie, saved #[arg(long, value_name = "PATH")] pub rpccookiefile: Option, @@ -44,7 +49,15 @@ pub struct Config { // Maximum ram you want the program to use in GB, default: 50% of total, not saved // #[arg(long, value_name = "GB")] // pub max_ram: Option, - /// Start a dry run, default: false, not saved + /// Enable or disable the parser, default: true, not saved + #[arg(long, value_name = "BOOL")] + parser: Option, + + /// Enable or disable the server, default: true, not saved + #[arg(long, value_name = "BOOL")] + server: Option, + + /// Start a dry run, default: true, not saved #[arg(long, value_name = "BOOL")] dry_run: Option, @@ -55,25 +68,25 @@ pub struct Config { /// Recompute all computed datasets, default: false, not saved #[arg(long, value_name = "BOOL")] recompute_computed: Option, - - /// Start the program by defragmenting all databases to reduce their footprint, default: false, not saved - #[arg(long, value_name = "BOOL")] - first_defragment: Option, } impl Config { - const PATH: &'static str = "./config.toml"; - pub fn import() -> color_eyre::Result { - let mut config_saved = fs::read_to_string(Self::PATH) - .map_or(Config::default(), |contents| { - toml::from_str(&contents).unwrap_or_default() - }); + let path = Self::path_dot_kibo(); + let _ = fs::create_dir_all(&path); + + let path = path.join("config.toml"); + + let mut config_saved = Self::read(&path); let mut config_args = Config::parse(); - if let Some(datadir) = config_args.datadir.take() { - config_saved.datadir = Some(datadir); + if let Some(bitcoindir) = config_args.bitcoindir.take() { + config_saved.bitcoindir = Some(bitcoindir); + } + + if let Some(kibodir) = config_args.kibodir.take() { + config_saved.kibodir = Some(kibodir); } if let Some(rpcconnect) = config_args.rpcconnect.take() { @@ -110,31 +123,30 @@ impl Config { config.check(); - config.write()?; + config.write(&path)?; + config.parser = config_args.parser.take(); + config.server = config_args.server.take(); config.dry_run = config_args.dry_run.take(); config.record_ram_usage = config_args.record_ram_usage.take(); config.recompute_computed = config_args.recompute_computed.take(); - config.first_defragment = config_args.first_defragment.take(); - log("---"); - log("Configuration:"); - log(&format!("datadir: {:?}", config.datadir)); - log(&format!("rpcconnect: {:?}", config.rpcconnect)); - log(&format!("rpcport: {:?}", config.rpcport)); - log(&format!("rpccookiefile: {:?}", config.rpccookiefile)); - log(&format!("rpcuser: {:?}", config.rpcuser)); - log(&format!("rpcpassword: {:?}", config.rpcpassword)); - log(&format!("delay: {:?}", config.delay)); - // log(&format!("max_ram: {:?}", config.max_ram)); - log(&format!("dry_run: {:?}", config.dry_run)); - log(&format!("record_ram_usage: {:?}", config.record_ram_usage)); - log(&format!( - "recompute_computed: {:?}", - config.recompute_computed - )); - log(&format!("first_defragment: {:?}", config.first_defragment)); - log("---"); + info!("Configuration {{"); + info!(" bitcoindir: {:?}", config.bitcoindir); + info!(" kibodir: {:?}", config.kibodir); + info!(" rpcconnect: {:?}", config.rpcconnect); + info!(" rpcport: {:?}", config.rpcport); + info!(" rpccookiefile: {:?}", config.rpccookiefile); + info!(" rpcuser: {:?}", config.rpcuser); + info!(" rpcpassword: {:?}", config.rpcpassword); + info!(" delay: {:?}", config.delay); + // info!(" max_ram: {:?}", config.max_ram); + info!(" parser: {:?}", config.parser()); + info!(" server: {:?}", config.server()); + info!(" dry_run: {:?}", config.dry_run()); + info!(" record_ram_usage: {:?}", config.record_ram_usage()); + info!(" recompute_computed: {:?}", config.recompute_computed()); + info!("}}"); if config_args != Config::default() { dbg!(config_args); @@ -145,14 +157,31 @@ impl Config { } fn check(&self) { - if self.datadir.is_none() { + if self.bitcoindir.is_none() { println!( - "You need to set the --datadir parameter at least once to run the parser.\nRun the program with '-h' for help." + "You need to set the --bitcoindir parameter at least once to run the parser.\nRun the program with '-h' for help." + ); + std::process::exit(1); + } else if !self.path_bitcoindir().is_dir() { + println!( + "Given --bitcoindir parameter doesn't seem to be a valid directory path.\nRun the program with '-h' for help." ); std::process::exit(1); } - let path = Path::new(self.datadir.as_ref().unwrap()); + if self.kibodir.is_none() { + println!( + "You need to set the --kibodir parameter at least once to run the parser.\nRun the program with '-h' for help." + ); + std::process::exit(1); + } else if !self.path_kibodir().is_dir() { + println!( + "Given --kibodir parameter doesn't seem to be a valid directory path.\nRun the program with '-h' for help." + ); + std::process::exit(1); + } + + let path = Path::new(self.bitcoindir.as_ref().unwrap()); if !path.is_dir() { println!("Expect path '{:#?}' to be a directory.", path); std::process::exit(1); @@ -166,12 +195,18 @@ impl Config { } } - fn write(&self) -> std::io::Result<()> { - fs::write(Self::PATH, toml::to_string(self).unwrap()) + fn read(path: &Path) -> Self { + fs::read_to_string(path).map_or(Config::default(), |contents| { + toml::from_str(&contents).unwrap_or_default() + }) + } + + fn write(&self, path: &Path) -> std::io::Result<()> { + fs::write(path, toml::to_string(self).unwrap()) } pub fn to_rpc_auth(&self) -> color_eyre::Result { - let cookie = Path::new(self.datadir.as_ref().unwrap()).join(".cookie"); + let cookie = Path::new(self.bitcoindir.as_ref().unwrap()).join(".cookie"); if cookie.is_file() { Ok(Auth::CookieFile(cookie)) @@ -205,11 +240,52 @@ impl Config { self.recompute_computed.is_some_and(|b| b) } - pub fn first_defragment(&self) -> bool { - self.first_defragment.is_some_and(|b| b) + pub fn path_bitcoindir(&self) -> PathBuf { + PathBuf::from(self.bitcoindir.as_ref().unwrap()) } - pub fn disable_defragment(&mut self) { - self.first_defragment.take(); + fn path_kibodir(&self) -> PathBuf { + PathBuf::from(self.kibodir.as_ref().unwrap()) + } + + pub fn path_datasets(&self) -> MapPath { + MapPath::from(self.path_kibodir().join("datasets")) + } + + pub fn path_datasets_last_values(&self) -> MapPath { + self.path_datasets().join("last.json") + } + + pub fn path_price(&self) -> MapPath { + MapPath::from(self.path_kibodir().join("price")) + } + + pub fn path_databases(&self) -> PathBuf { + self.path_kibodir().join("databases") + } + + pub fn path_states(&self) -> PathBuf { + self.path_kibodir().join("states") + } + + pub fn path_inputs(&self) -> PathBuf { + self.path_kibodir().join("inputs") + } + + fn path_dot_kibo() -> PathBuf { + let home = std::env::var("HOME").unwrap(); + Path::new(&home).join(".kibo") + } + + pub fn path_log() -> PathBuf { + Self::path_dot_kibo().join("log") + } + + pub fn parser(&self) -> bool { + self.parser.is_none_or(|b| b) + } + + pub fn server(&self) -> bool { + self.server.is_none_or(|b| b) } } diff --git a/parser/src/structs/counter.rs b/src/structs/counter.rs similarity index 100% rename from parser/src/structs/counter.rs rename to src/structs/counter.rs diff --git a/parser/src/structs/date.rs b/src/structs/date.rs similarity index 92% rename from parser/src/structs/date.rs rename to src/structs/date.rs index ec36f4c52..c2d51dd51 100644 --- a/parser/src/structs/date.rs +++ b/src/structs/date.rs @@ -44,10 +44,6 @@ impl Date { Self(chrono::offset::Utc::now().date_naive()) } - pub fn yesterday() -> Self { - Self(Self::today().checked_sub_days(Days::new(1)).unwrap()) - } - pub fn difference_in_days_between(&self, older: Self) -> u32 { (**self - *older).num_days() as u32 } @@ -74,17 +70,13 @@ impl Date { } } + pub fn is_january(&self) -> bool { + self.month() == 1 + } + pub fn is_first_of_month(&self) -> bool { self.day() == 1 } - - pub fn is_first_of_january(&self) -> bool { - self.is_first_of_month() && self.month() == 1 - } - - pub fn is_first_of_june(&self) -> bool { - self.is_first_of_month() && self.month() == 6 - } } impl MapKey for Date { diff --git a/parser/src/structs/date_data.rs b/src/structs/date_data.rs similarity index 100% rename from parser/src/structs/date_data.rs rename to src/structs/date_data.rs diff --git a/parser/src/structs/date_map.rs b/src/structs/date_map.rs similarity index 94% rename from parser/src/structs/date_map.rs rename to src/structs/date_map.rs index 76e3c6bde..37e853efe 100644 --- a/parser/src/structs/date_map.rs +++ b/src/structs/date_map.rs @@ -1,8 +1,8 @@ use std::iter::Sum; -use crate::{Date, HeightMap}; - -use super::{AnyMap, DateMapChunkId, GenericMap, Height, MapValue, SerializedBTreeMap}; +use super::{ + AnyMap, Date, DateMapChunkId, GenericMap, Height, HeightMap, MapValue, SerializedBTreeMap, +}; pub type DateMap = GenericMap>; diff --git a/parser/src/structs/date_map_chunk_id.rs b/src/structs/date_map_chunk_id.rs similarity index 95% rename from parser/src/structs/date_map_chunk_id.rs rename to src/structs/date_map_chunk_id.rs index ca3fdac32..dbc8eb4fa 100644 --- a/parser/src/structs/date_map_chunk_id.rs +++ b/src/structs/date_map_chunk_id.rs @@ -3,9 +3,7 @@ use std::path::Path; use allocative::Allocative; use chrono::Datelike; -use crate::Date; - -use super::MapChunkId; +use super::{Date, MapChunkId}; #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Allocative)] pub struct DateMapChunkId(i32); diff --git a/parser/src/structs/empty_address_data.rs b/src/structs/empty_address_data.rs similarity index 100% rename from parser/src/structs/empty_address_data.rs rename to src/structs/empty_address_data.rs diff --git a/parser/src/structs/epoch.rs b/src/structs/epoch.rs similarity index 100% rename from parser/src/structs/epoch.rs rename to src/structs/epoch.rs diff --git a/parser/src/structs/exit.rs b/src/structs/exit.rs similarity index 92% rename from parser/src/structs/exit.rs rename to src/structs/exit.rs index 5122ef361..00408a42e 100644 --- a/parser/src/structs/exit.rs +++ b/src/structs/exit.rs @@ -8,7 +8,7 @@ use std::{ time::Duration, }; -use super::super::log; +use log::info; #[derive(Default, Clone)] pub struct Exit { @@ -29,12 +29,12 @@ impl Exit { let blocked = move || _blocked.load(Ordering::SeqCst); ctrlc::set_handler(move || { - log("Exitting..."); + info!("Exitting..."); active.store(true, Ordering::SeqCst); if blocked() { - log("Waiting to exit safely"); + info!("Waiting to exit safely"); while blocked() { sleep(Duration::from_millis(50)); diff --git a/parser/src/structs/generic_map.rs b/src/structs/generic_map.rs similarity index 95% rename from parser/src/structs/generic_map.rs rename to src/structs/generic_map.rs index a384f6619..8c1e52244 100644 --- a/parser/src/structs/generic_map.rs +++ b/src/structs/generic_map.rs @@ -15,12 +15,11 @@ use ordered_float::OrderedFloat; use serde::{de::DeserializeOwned, Serialize}; use crate::{ - log, + io::Serialization, utils::{get_percentile, LossyFrom}, - Serialization, }; -use super::{AnyMap, MapValue}; +use super::{AnyMap, MapPath, MapValue}; #[derive(Debug, Clone, Copy, Allocative, PartialEq, Eq)] pub enum MapKind { @@ -79,8 +78,8 @@ pub struct GenericMap { version: u32, kind: MapKind, - path_all: PathBuf, - path_last: Option, + path_all: MapPath, + path_last: Option, chunks_in_memory: usize, @@ -100,15 +99,15 @@ where Key: MapKey, Serialized: MapSerialized, { - pub fn new_bin(version: u32, kind: MapKind, path: &str) -> Self { + pub fn new_bin(version: u32, kind: MapKind, path: &MapPath) -> Self { Self::new(version, kind, path, Serialization::Binary, 1, true) } - pub fn _new_bin(version: u32, kind: MapKind, path: &str, export_last: bool) -> Self { + pub fn _new_bin(version: u32, kind: MapKind, path: &MapPath, export_last: bool) -> Self { Self::new(version, kind, path, Serialization::Binary, 1, export_last) } - pub fn new_json(version: u32, kind: MapKind, path: &str, export_last: bool) -> Self { + pub fn new_json(version: u32, kind: MapKind, path: &MapPath, export_last: bool) -> Self { Self::new( version, kind, @@ -122,7 +121,7 @@ where fn new( version: u32, kind: MapKind, - path: &str, + path: &MapPath, serialization: Serialization, chunks_in_memory: usize, export_last: bool, @@ -131,15 +130,16 @@ where panic!("Should always have at least the latest chunk in memory"); } - let path = path.replace(['-', '_', ' '], "/"); + let path_all = path.join(Key::map_name()); - let path_all = PathBuf::from(format!("{path}/{}", Key::map_name())); - - fs::create_dir_all(&path_all).unwrap(); + fs::create_dir_all(&*path_all).unwrap_or_else(|_| { + dbg!(&path_all); + panic!() + }); let path_last = { if export_last { - Some(PathBuf::from(format!("{path}/last"))) + Some(path.join("last")) } else { None } @@ -187,9 +187,9 @@ where .initial_last_key .and_then(|last_key| last_key.to_first_unsafe()); - if s.initial_first_unsafe_key.is_none() { - log(&format!("Missing dataset: {path}/{}", Key::map_name())); - } + // if s.initial_first_unsafe_key.is_none() { + // log(&format!("Missing dataset: {path:?}/{}", Key::map_name())); + // } s } @@ -312,7 +312,7 @@ where &self.path_all } - fn path_last(&self) -> &Option { + fn path_last(&self) -> &Option { &self.path_last } @@ -367,15 +367,15 @@ where panic!(); }); - let path = self.path_all.join(chunk_id.to_name()); + let path = self.path_all.join(&chunk_id.to_name()); - self.serialization.export(Path::new(&path), serialized)?; + self.serialization.export(&path, serialized)?; // Export last if index == len - 1 { if let Some(path_last) = self.path_last.as_ref() { self.serialization - .export(Path::new(path_last), serialized.last().unwrap())?; + .export(path_last, serialized.last().unwrap())?; } } @@ -418,22 +418,22 @@ where Key: MapKey, Serialized: MapSerialized, { - pub fn sum_keys(&mut self, keys: &[Key]) -> Value - where - Value: Sum, - { - keys.iter() - .map(|key| self.get_or_import(key).unwrap()) - .sum::() - } + // pub fn sum_keys(&mut self, keys: &[Key]) -> Value + // where + // Value: Sum, + // { + // keys.iter() + // .map(|key| self.get_or_import(key).unwrap()) + // .sum::() + // } - pub fn average_keys(&mut self, keys: &[Key]) -> f32 - where - Value: Sum, - f32: LossyFrom, - { - f32::lossy_from(self.sum_keys(keys)) / keys.len() as f32 - } + // pub fn average_keys(&mut self, keys: &[Key]) -> f32 + // where + // Value: Sum, + // f32: LossyFrom, + // { + // f32::lossy_from(self.sum_keys(keys)) / keys.len() as f32 + // } pub fn multi_insert(&mut self, keys: &[Key], mut callback: F) where diff --git a/parser/src/structs/height.rs b/src/structs/height.rs similarity index 91% rename from parser/src/structs/height.rs rename to src/structs/height.rs index b3d811eff..17a228275 100644 --- a/parser/src/structs/height.rs +++ b/src/structs/height.rs @@ -9,9 +9,7 @@ use biter::NUMBER_OF_UNSAFE_BLOCKS; use derive_deref::{Deref, DerefMut}; use serde::{Deserialize, Serialize}; -use crate::HEIGHT_MAP_CHUNK_SIZE; - -use super::{HeightMapChunkId, MapKey}; +use super::{HeightMapChunkId, MapKey, HEIGHT_MAP_CHUNK_SIZE}; #[derive( Debug, @@ -47,11 +45,11 @@ impl Height { **self < (block_count - NUMBER_OF_UNSAFE_BLOCKS) as u32 } - pub fn iter_range_inclusive(first: Height, last: Height) -> impl Iterator { - let range = (*first)..=(*last); + // pub fn iter_range_inclusive(first: Height, last: Height) -> impl Iterator { + // let range = (*first)..=(*last); - range.into_iter().map(Height::new) - } + // range.into_iter().map(Height::new) + // } } impl PartialEq for Height { diff --git a/parser/src/structs/height_map.rs b/src/structs/height_map.rs similarity index 97% rename from parser/src/structs/height_map.rs rename to src/structs/height_map.rs index bd2f36a73..f768f96b0 100644 --- a/parser/src/structs/height_map.rs +++ b/src/structs/height_map.rs @@ -2,9 +2,7 @@ use std::{iter::Sum, ops::RangeInclusive}; use itertools::Itertools; -use crate::SerializedVec; - -use super::{AnyMap, GenericMap, Height, HeightMapChunkId, MapValue}; +use super::{AnyMap, GenericMap, Height, HeightMapChunkId, MapValue, SerializedVec}; pub const HEIGHT_MAP_CHUNK_SIZE: u32 = 10_000; diff --git a/parser/src/structs/height_map_chunk_id.rs b/src/structs/height_map_chunk_id.rs similarity index 94% rename from parser/src/structs/height_map_chunk_id.rs rename to src/structs/height_map_chunk_id.rs index 6f662529d..06d3856ce 100644 --- a/parser/src/structs/height_map_chunk_id.rs +++ b/src/structs/height_map_chunk_id.rs @@ -3,9 +3,7 @@ use std::path::Path; use allocative::Allocative; use derive_deref::{Deref, DerefMut}; -use crate::HEIGHT_MAP_CHUNK_SIZE; - -use super::{Height, MapChunkId}; +use super::{Height, MapChunkId, HEIGHT_MAP_CHUNK_SIZE}; #[derive( Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Allocative, Deref, DerefMut, diff --git a/parser/src/structs/liquidity.rs b/src/structs/liquidity.rs similarity index 100% rename from parser/src/structs/liquidity.rs rename to src/structs/liquidity.rs diff --git a/src/structs/map_path.rs b/src/structs/map_path.rs new file mode 100644 index 000000000..ebf056374 --- /dev/null +++ b/src/structs/map_path.rs @@ -0,0 +1,24 @@ +use std::path::PathBuf; + +use allocative::Allocative; +use derive_deref::{Deref, DerefMut}; + +#[derive(Debug, Clone, Deref, DerefMut, Allocative)] +pub struct MapPath(PathBuf); + +impl MapPath { + pub fn join(&self, path: &str) -> Self { + let path = path.replace(['-', '_', ' '], "/"); + Self(self.0.join(path)) + } + + pub fn unwrap(&self) -> &PathBuf { + &self.0 + } +} + +impl From for MapPath { + fn from(value: PathBuf) -> Self { + Self(value) + } +} diff --git a/parser/src/structs/map_value.rs b/src/structs/map_value.rs similarity index 100% rename from parser/src/structs/map_value.rs rename to src/structs/map_value.rs diff --git a/parser/src/structs/mod.rs b/src/structs/mod.rs similarity index 97% rename from parser/src/structs/mod.rs rename to src/structs/mod.rs index 1278f5844..f1974120b 100644 --- a/parser/src/structs/mod.rs +++ b/src/structs/mod.rs @@ -25,6 +25,7 @@ mod height; mod height_map; mod height_map_chunk_id; mod liquidity; +mod map_path; mod map_value; mod ohlc; mod partial_txout_data; @@ -63,6 +64,7 @@ pub use height::*; pub use height_map::*; pub use height_map_chunk_id::*; pub use liquidity::*; +pub use map_path::*; pub use map_value::*; pub use ohlc::*; pub use partial_txout_data::*; diff --git a/parser/src/structs/ohlc.rs b/src/structs/ohlc.rs similarity index 100% rename from parser/src/structs/ohlc.rs rename to src/structs/ohlc.rs diff --git a/parser/src/structs/partial_txout_data.rs b/src/structs/partial_txout_data.rs similarity index 100% rename from parser/src/structs/partial_txout_data.rs rename to src/structs/partial_txout_data.rs diff --git a/parser/src/structs/price.rs b/src/structs/price.rs similarity index 100% rename from parser/src/structs/price.rs rename to src/structs/price.rs diff --git a/parser/src/structs/ram.rs b/src/structs/ram.rs similarity index 100% rename from parser/src/structs/ram.rs rename to src/structs/ram.rs diff --git a/parser/src/structs/sent_data.rs b/src/structs/sent_data.rs similarity index 100% rename from parser/src/structs/sent_data.rs rename to src/structs/sent_data.rs diff --git a/parser/src/structs/serialized_btreemap.rs b/src/structs/serialized_btreemap.rs similarity index 98% rename from parser/src/structs/serialized_btreemap.rs rename to src/structs/serialized_btreemap.rs index 2f1723ff1..97ec47ad5 100644 --- a/parser/src/structs/serialized_btreemap.rs +++ b/src/structs/serialized_btreemap.rs @@ -4,7 +4,7 @@ use allocative::Allocative; use bincode::{Decode, Encode}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use crate::Serialization; +use crate::io::Serialization; use super::{DateMap, MapChunkId, MapKey, MapSerialized, MapValue}; diff --git a/parser/src/structs/serialized_vec.rs b/src/structs/serialized_vec.rs similarity index 98% rename from parser/src/structs/serialized_vec.rs rename to src/structs/serialized_vec.rs index 3d61a9738..aa02f51d3 100644 --- a/parser/src/structs/serialized_vec.rs +++ b/src/structs/serialized_vec.rs @@ -4,7 +4,7 @@ use allocative::Allocative; use bincode::{Decode, Encode}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use crate::Serialization; +use crate::io::Serialization; use super::{HeightMap, MapChunkId, MapKey, MapSerialized, MapValue}; diff --git a/parser/src/structs/timestamp.rs b/src/structs/timestamp.rs similarity index 91% rename from parser/src/structs/timestamp.rs rename to src/structs/timestamp.rs index 04ce1b1ad..6e8455a23 100644 --- a/parser/src/structs/timestamp.rs +++ b/src/structs/timestamp.rs @@ -2,7 +2,7 @@ use std::ops::Sub; use allocative::Allocative; use bincode::{Decode, Encode}; -use chrono::{Datelike, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc}; +use chrono::{NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc}; use derive_deref::{Deref, DerefMut}; use serde::{Deserialize, Serialize}; @@ -48,10 +48,6 @@ impl Timestamp { ) } - pub fn to_year(self) -> u32 { - self.to_date().year() as u32 - } - pub fn to_floored_seconds(self) -> Self { let date_time = Utc.timestamp_opt(i64::from(self.0), 0).unwrap(); diff --git a/parser/src/structs/tx_data.rs b/src/structs/tx_data.rs similarity index 100% rename from parser/src/structs/tx_data.rs rename to src/structs/tx_data.rs diff --git a/parser/src/structs/txout_index.rs b/src/structs/txout_index.rs similarity index 100% rename from parser/src/structs/txout_index.rs rename to src/structs/txout_index.rs diff --git a/parser/src/utils/consts.rs b/src/utils/consts.rs similarity index 100% rename from parser/src/utils/consts.rs rename to src/utils/consts.rs diff --git a/parser/src/utils/flamegraph.rs b/src/utils/flamegraph.rs similarity index 84% rename from parser/src/utils/flamegraph.rs rename to src/utils/flamegraph.rs index 2a40ab398..2e96ce6e3 100644 --- a/parser/src/utils/flamegraph.rs +++ b/src/utils/flamegraph.rs @@ -2,10 +2,15 @@ use std::{fs, path::PathBuf}; use chrono::Local; -use crate::{databases::Databases, datasets::AllDatasets, states::States, structs::Height}; +use crate::{ + parser::{Databases, Datasets, States}, + structs::Height, +}; + +// use crate::{databases::Databases, datasets::AllDatasets, states::States, structs::Height}; pub fn generate_allocation_files( - datasets: &AllDatasets, + datasets: &Datasets, databases: &Databases, states: &States, last_height: Height, diff --git a/src/utils/log.rs b/src/utils/log.rs new file mode 100644 index 000000000..4679d3339 --- /dev/null +++ b/src/utils/log.rs @@ -0,0 +1,82 @@ +use std::{ + fmt::Display, + fs::{self, OpenOptions}, + io::Write, +}; + +use chrono::Local; +use color_eyre::owo_colors::OwoColorize; +use env_logger::Env; + +use crate::structs::Config; + +#[inline(always)] +pub fn init_log() { + let _ = fs::remove_file(Config::path_log()); + + let file = Box::new( + OpenOptions::new() + .create(true) + .append(true) + .open(Config::path_log()) + .unwrap(), + ); + + env_logger::Builder::from_env( + Env::default().default_filter_or(format!("{}=info", env!("CARGO_PKG_NAME"))), + ) + .format(move |buf, record| { + let date_time = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); + let level = record.level().as_str().to_lowercase(); + let level = format!("{:5}", level); + let target = record.target(); + let dash = "-"; + let args = record.args(); + + let _ = write( + file.try_clone().unwrap(), + &date_time, + target, + &level, + dash, + args, + ); + + let colored_date_time = date_time.bright_black(); + let colored_level = match level.chars().next().unwrap() { + 'e' => level.red().to_string(), + 'w' => level.yellow().to_string(), + 'i' => level.green().to_string(), + 'd' => level.blue().to_string(), + 't' => level.cyan().to_string(), + _ => panic!(), + }; + let colored_dash = dash.bright_black(); + + write( + buf, + colored_date_time, + target, + colored_level, + colored_dash, + args, + ) + }) + .init(); +} + +fn write( + mut buf: impl Write, + date_time: impl Display, + _target: impl Display, + level: impl Display, + dash: impl Display, + args: impl Display, +) -> Result<(), std::io::Error> { + writeln!(buf, "{} {} {} {}", date_time, dash, level, args) + // writeln!( + // buf, + // "{} {} {} {} {}", + // date_time, _target, level, dash, args + // ) +} diff --git a/parser/src/utils/lossy.rs b/src/utils/lossy.rs similarity index 100% rename from parser/src/utils/lossy.rs rename to src/utils/lossy.rs diff --git a/parser/src/utils/mod.rs b/src/utils/mod.rs similarity index 93% rename from parser/src/utils/mod.rs rename to src/utils/mod.rs index 18c6f8d80..0ed6755e9 100644 --- a/parser/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -13,5 +13,4 @@ pub use log::*; pub use lossy::*; pub use percentile::*; pub use retry::*; -pub use rpc::*; pub use time::*; diff --git a/parser/src/utils/percentile.rs b/src/utils/percentile.rs similarity index 100% rename from parser/src/utils/percentile.rs rename to src/utils/percentile.rs diff --git a/parser/src/utils/retry.rs b/src/utils/retry.rs similarity index 100% rename from parser/src/utils/retry.rs rename to src/utils/retry.rs diff --git a/parser/src/utils/rpc.rs b/src/utils/rpc.rs similarity index 61% rename from parser/src/utils/rpc.rs rename to src/utils/rpc.rs index 4e65bd8aa..a25275d27 100644 --- a/parser/src/utils/rpc.rs +++ b/src/utils/rpc.rs @@ -1,8 +1,14 @@ use biter::bitcoincore_rpc::Client; -use crate::Config; +use crate::structs::Config; -pub fn create_rpc(config: &Config) -> color_eyre::Result { +impl From<&Config> for Client { + fn from(config: &Config) -> Self { + create_rpc(config).unwrap() + } +} + +fn create_rpc(config: &Config) -> color_eyre::Result { Ok(Client::new( &format!( "http://{}:{}", diff --git a/parser/src/utils/time.rs b/src/utils/time.rs similarity index 66% rename from parser/src/utils/time.rs rename to src/utils/time.rs index ec5c090f1..f0001216d 100644 --- a/parser/src/utils/time.rs +++ b/src/utils/time.rs @@ -1,6 +1,6 @@ use std::time::Instant; -use crate::utils::log; +use log::info; pub fn time(name: &str, function: F) -> T where @@ -10,7 +10,7 @@ where let returned = function(); - log(&format!("{name}: {} seconds", time.elapsed().as_secs_f32())); + info!("{name}: {} seconds", time.elapsed().as_secs_f32()); returned } diff --git a/website/assets/fonts/satoshi/2024-09/font.var.woff2 b/src/website/assets/fonts/satoshi/2024-09/font.var.woff2 similarity index 100% rename from website/assets/fonts/satoshi/2024-09/font.var.woff2 rename to src/website/assets/fonts/satoshi/2024-09/font.var.woff2 diff --git a/website/assets/fonts/satoshi/FFL.txt b/src/website/assets/fonts/satoshi/FFL.txt similarity index 100% rename from website/assets/fonts/satoshi/FFL.txt rename to src/website/assets/fonts/satoshi/FFL.txt diff --git a/website/assets/pdfs/block/2022-report.pdf b/src/website/assets/pdfs/block/2022-report.pdf similarity index 100% rename from website/assets/pdfs/block/2022-report.pdf rename to src/website/assets/pdfs/block/2022-report.pdf diff --git a/website/assets/pdfs/braiins/building-bitcoin-in-rust.pdf b/src/website/assets/pdfs/braiins/building-bitcoin-in-rust.pdf similarity index 100% rename from website/assets/pdfs/braiins/building-bitcoin-in-rust.pdf rename to src/website/assets/pdfs/braiins/building-bitcoin-in-rust.pdf diff --git a/website/assets/pdfs/glassnode/cointime-economics.pdf b/src/website/assets/pdfs/glassnode/cointime-economics.pdf similarity index 100% rename from website/assets/pdfs/glassnode/cointime-economics.pdf rename to src/website/assets/pdfs/glassnode/cointime-economics.pdf diff --git a/website/assets/pdfs/multi-author/bcap_v1.0.pdf b/src/website/assets/pdfs/multi-author/bcap_v1.0.pdf similarity index 100% rename from website/assets/pdfs/multi-author/bcap_v1.0.pdf rename to src/website/assets/pdfs/multi-author/bcap_v1.0.pdf diff --git a/website/assets/pdfs/nakamoto-project/understanding-bitcoin-adoption-in-the-united-states.pdf b/src/website/assets/pdfs/nakamoto-project/understanding-bitcoin-adoption-in-the-united-states.pdf similarity index 100% rename from website/assets/pdfs/nakamoto-project/understanding-bitcoin-adoption-in-the-united-states.pdf rename to src/website/assets/pdfs/nakamoto-project/understanding-bitcoin-adoption-in-the-united-states.pdf diff --git a/website/assets/pdfs/nydig/protection-under-first-amendment.pdf b/src/website/assets/pdfs/nydig/protection-under-first-amendment.pdf similarity index 100% rename from website/assets/pdfs/nydig/protection-under-first-amendment.pdf rename to src/website/assets/pdfs/nydig/protection-under-first-amendment.pdf diff --git a/website/assets/pdfs/satoshi-nakamoto/whitepaper.pdf b/src/website/assets/pdfs/satoshi-nakamoto/whitepaper.pdf similarity index 100% rename from website/assets/pdfs/satoshi-nakamoto/whitepaper.pdf rename to src/website/assets/pdfs/satoshi-nakamoto/whitepaper.pdf diff --git a/website/assets/pdfs/square/2021-bitcoin-clean-energy-initiative.pdf b/src/website/assets/pdfs/square/2021-bitcoin-clean-energy-initiative.pdf similarity index 100% rename from website/assets/pdfs/square/2021-bitcoin-clean-energy-initiative.pdf rename to src/website/assets/pdfs/square/2021-bitcoin-clean-energy-initiative.pdf diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-icon-180.png b/src/website/assets/pwa/2024-11-20_09-41-25/apple-icon-180.png similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-icon-180.png rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-icon-180.png diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1125-2436.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1125-2436.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1125-2436.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1125-2436.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1136-640.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1136-640.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1136-640.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1136-640.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1170-2532.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1170-2532.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1170-2532.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1170-2532.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1179-2556.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1179-2556.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1179-2556.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1179-2556.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1242-2208.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1242-2208.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1242-2208.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1242-2208.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1242-2688.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1242-2688.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1242-2688.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1242-2688.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1284-2778.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1284-2778.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1284-2778.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1284-2778.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1290-2796.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1290-2796.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1290-2796.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1290-2796.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1334-750.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1334-750.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1334-750.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1334-750.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1488-2266.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1488-2266.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1488-2266.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1488-2266.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1536-2048.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1536-2048.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1536-2048.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1536-2048.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1620-2160.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1620-2160.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1620-2160.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1620-2160.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1640-2360.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1640-2360.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1640-2360.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1640-2360.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1668-2224.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1668-2224.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1668-2224.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1668-2224.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1668-2388.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1668-2388.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1668-2388.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1668-2388.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1792-828.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1792-828.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-1792-828.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-1792-828.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2048-1536.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2048-1536.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2048-1536.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2048-1536.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2048-2732.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2048-2732.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2048-2732.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2048-2732.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2160-1620.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2160-1620.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2160-1620.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2160-1620.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2208-1242.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2208-1242.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2208-1242.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2208-1242.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2224-1668.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2224-1668.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2224-1668.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2224-1668.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2266-1488.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2266-1488.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2266-1488.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2266-1488.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2360-1640.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2360-1640.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2360-1640.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2360-1640.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2388-1668.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2388-1668.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2388-1668.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2388-1668.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2436-1125.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2436-1125.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2436-1125.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2436-1125.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2532-1170.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2532-1170.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2532-1170.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2532-1170.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2556-1179.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2556-1179.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2556-1179.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2556-1179.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2688-1242.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2688-1242.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2688-1242.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2688-1242.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2732-2048.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2732-2048.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2732-2048.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2732-2048.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2778-1284.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2778-1284.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2778-1284.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2778-1284.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2796-1290.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2796-1290.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-2796-1290.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-2796-1290.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-640-1136.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-640-1136.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-640-1136.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-640-1136.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-750-1334.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-750-1334.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-750-1334.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-750-1334.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/apple-splash-828-1792.jpg b/src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-828-1792.jpg similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/apple-splash-828-1792.jpg rename to src/website/assets/pwa/2024-11-20_09-41-25/apple-splash-828-1792.jpg diff --git a/website/assets/pwa/2024-11-20_09-41-25/favicon-196.png b/src/website/assets/pwa/2024-11-20_09-41-25/favicon-196.png similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/favicon-196.png rename to src/website/assets/pwa/2024-11-20_09-41-25/favicon-196.png diff --git a/website/assets/pwa/2024-11-20_09-41-25/index.html b/src/website/assets/pwa/2024-11-20_09-41-25/index.html similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/index.html rename to src/website/assets/pwa/2024-11-20_09-41-25/index.html diff --git a/website/assets/pwa/2024-11-20_09-41-25/manifest-icon-192.maskable.png b/src/website/assets/pwa/2024-11-20_09-41-25/manifest-icon-192.maskable.png similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/manifest-icon-192.maskable.png rename to src/website/assets/pwa/2024-11-20_09-41-25/manifest-icon-192.maskable.png diff --git a/website/assets/pwa/2024-11-20_09-41-25/manifest-icon-512.maskable.png b/src/website/assets/pwa/2024-11-20_09-41-25/manifest-icon-512.maskable.png similarity index 100% rename from website/assets/pwa/2024-11-20_09-41-25/manifest-icon-512.maskable.png rename to src/website/assets/pwa/2024-11-20_09-41-25/manifest-icon-512.maskable.png diff --git a/website/assets/pwa/index.html b/src/website/assets/pwa/index.html similarity index 100% rename from website/assets/pwa/index.html rename to src/website/assets/pwa/index.html diff --git a/website/generate-icons.sh b/src/website/generate-icons.sh similarity index 100% rename from website/generate-icons.sh rename to src/website/generate-icons.sh diff --git a/website/index.html b/src/website/index.html similarity index 99% rename from website/index.html rename to src/website/index.html index b649580b9..93166e466 100644 --- a/website/index.html +++ b/src/website/index.html @@ -513,6 +513,7 @@ input { border: 0; width: 100%; + text-align: left; &:focus-visible { outline: none; @@ -834,32 +835,6 @@ } } - aside { - > header { - button { - margin: -0.5rem 0; - - &:first-child { - margin-left: -0.65rem; - } - - &:last-child { - margin-right: -0.65rem; - } - - &[data-highlight] { - * { - color: var(--orange) !important; - } - } - - > svg { - margin: 0.5rem; - } - } - } - } - #share-div { padding: 1.5rem; backdrop-filter: blur(12px); diff --git a/website/jsconfig.json b/src/website/jsconfig.json similarity index 100% rename from website/jsconfig.json rename to src/website/jsconfig.json diff --git a/website/manifest.webmanifest b/src/website/manifest.webmanifest similarity index 100% rename from website/manifest.webmanifest rename to src/website/manifest.webmanifest diff --git a/website/misc/tailwindTo550.js b/src/website/misc/tailwindTo550.js similarity index 100% rename from website/misc/tailwindTo550.js rename to src/website/misc/tailwindTo550.js diff --git a/website/packages/lean-qr/README.md b/src/website/packages/lean-qr/README.md similarity index 100% rename from website/packages/lean-qr/README.md rename to src/website/packages/lean-qr/README.md diff --git a/website/packages/lean-qr/v2.3.4/script.js b/src/website/packages/lean-qr/v2.3.4/script.js similarity index 100% rename from website/packages/lean-qr/v2.3.4/script.js rename to src/website/packages/lean-qr/v2.3.4/script.js diff --git a/website/packages/lean-qr/v2.3.4/types.d.ts b/src/website/packages/lean-qr/v2.3.4/types.d.ts similarity index 100% rename from website/packages/lean-qr/v2.3.4/types.d.ts rename to src/website/packages/lean-qr/v2.3.4/types.d.ts diff --git a/website/packages/lightweight-charts/NOTICE.md b/src/website/packages/lightweight-charts/NOTICE.md similarity index 100% rename from website/packages/lightweight-charts/NOTICE.md rename to src/website/packages/lightweight-charts/NOTICE.md diff --git a/website/packages/lightweight-charts/README.md b/src/website/packages/lightweight-charts/README.md similarity index 100% rename from website/packages/lightweight-charts/README.md rename to src/website/packages/lightweight-charts/README.md diff --git a/website/packages/lightweight-charts/types.d.ts b/src/website/packages/lightweight-charts/types.d.ts similarity index 100% rename from website/packages/lightweight-charts/types.d.ts rename to src/website/packages/lightweight-charts/types.d.ts diff --git a/website/packages/lightweight-charts/v4.2.0/script.js b/src/website/packages/lightweight-charts/v4.2.0/script.js similarity index 100% rename from website/packages/lightweight-charts/v4.2.0/script.js rename to src/website/packages/lightweight-charts/v4.2.0/script.js diff --git a/website/packages/lightweight-charts/v4.2.0/types.d.ts b/src/website/packages/lightweight-charts/v4.2.0/types.d.ts similarity index 100% rename from website/packages/lightweight-charts/v4.2.0/types.d.ts rename to src/website/packages/lightweight-charts/v4.2.0/types.d.ts diff --git a/website/packages/lightweight-charts/wrapper.js b/src/website/packages/lightweight-charts/wrapper.js similarity index 100% rename from website/packages/lightweight-charts/wrapper.js rename to src/website/packages/lightweight-charts/wrapper.js diff --git a/website/packages/solid-signals/2024-11-02/script.js b/src/website/packages/solid-signals/2024-11-02/script.js similarity index 100% rename from website/packages/solid-signals/2024-11-02/script.js rename to src/website/packages/solid-signals/2024-11-02/script.js diff --git a/website/packages/solid-signals/2024-11-02/types/core/constants.d.ts b/src/website/packages/solid-signals/2024-11-02/types/core/constants.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/core/constants.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/core/constants.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/core/core.d.ts b/src/website/packages/solid-signals/2024-11-02/types/core/core.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/core/core.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/core/core.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/core/effect.d.ts b/src/website/packages/solid-signals/2024-11-02/types/core/effect.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/core/effect.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/core/effect.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/core/error.d.ts b/src/website/packages/solid-signals/2024-11-02/types/core/error.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/core/error.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/core/error.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/core/flags.d.ts b/src/website/packages/solid-signals/2024-11-02/types/core/flags.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/core/flags.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/core/flags.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/core/index.d.ts b/src/website/packages/solid-signals/2024-11-02/types/core/index.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/core/index.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/core/index.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/core/owner.d.ts b/src/website/packages/solid-signals/2024-11-02/types/core/owner.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/core/owner.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/core/owner.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/core/scheduler.d.ts b/src/website/packages/solid-signals/2024-11-02/types/core/scheduler.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/core/scheduler.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/core/scheduler.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/index.d.ts b/src/website/packages/solid-signals/2024-11-02/types/index.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/index.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/index.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/map.d.ts b/src/website/packages/solid-signals/2024-11-02/types/map.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/map.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/map.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/signals.d.ts b/src/website/packages/solid-signals/2024-11-02/types/signals.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/signals.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/signals.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/store/index.d.ts b/src/website/packages/solid-signals/2024-11-02/types/store/index.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/store/index.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/store/index.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/store/store.d.ts b/src/website/packages/solid-signals/2024-11-02/types/store/store.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/store/store.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/store/store.d.ts diff --git a/website/packages/solid-signals/2024-11-02/types/utils.d.ts b/src/website/packages/solid-signals/2024-11-02/types/utils.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-02/types/utils.d.ts rename to src/website/packages/solid-signals/2024-11-02/types/utils.d.ts diff --git a/website/packages/solid-signals/2024-11-08/script.js b/src/website/packages/solid-signals/2024-11-08/script.js similarity index 100% rename from website/packages/solid-signals/2024-11-08/script.js rename to src/website/packages/solid-signals/2024-11-08/script.js diff --git a/website/packages/solid-signals/2024-11-08/types/core/constants.d.ts b/src/website/packages/solid-signals/2024-11-08/types/core/constants.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/core/constants.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/core/constants.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/core/core.d.ts b/src/website/packages/solid-signals/2024-11-08/types/core/core.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/core/core.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/core/core.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/core/effect.d.ts b/src/website/packages/solid-signals/2024-11-08/types/core/effect.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/core/effect.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/core/effect.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/core/error.d.ts b/src/website/packages/solid-signals/2024-11-08/types/core/error.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/core/error.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/core/error.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/core/flags.d.ts b/src/website/packages/solid-signals/2024-11-08/types/core/flags.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/core/flags.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/core/flags.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/core/index.d.ts b/src/website/packages/solid-signals/2024-11-08/types/core/index.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/core/index.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/core/index.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/core/owner.d.ts b/src/website/packages/solid-signals/2024-11-08/types/core/owner.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/core/owner.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/core/owner.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/core/scheduler.d.ts b/src/website/packages/solid-signals/2024-11-08/types/core/scheduler.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/core/scheduler.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/core/scheduler.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/index.d.ts b/src/website/packages/solid-signals/2024-11-08/types/index.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/index.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/index.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/map.d.ts b/src/website/packages/solid-signals/2024-11-08/types/map.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/map.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/map.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/signals.d.ts b/src/website/packages/solid-signals/2024-11-08/types/signals.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/signals.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/signals.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/store/index.d.ts b/src/website/packages/solid-signals/2024-11-08/types/store/index.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/store/index.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/store/index.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/store/store.d.ts b/src/website/packages/solid-signals/2024-11-08/types/store/store.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/store/store.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/store/store.d.ts diff --git a/website/packages/solid-signals/2024-11-08/types/utils.d.ts b/src/website/packages/solid-signals/2024-11-08/types/utils.d.ts similarity index 100% rename from website/packages/solid-signals/2024-11-08/types/utils.d.ts rename to src/website/packages/solid-signals/2024-11-08/types/utils.d.ts diff --git a/website/packages/solid-signals/README.md b/src/website/packages/solid-signals/README.md similarity index 100% rename from website/packages/solid-signals/README.md rename to src/website/packages/solid-signals/README.md diff --git a/website/packages/solid-signals/types.d.ts b/src/website/packages/solid-signals/types.d.ts similarity index 100% rename from website/packages/solid-signals/types.d.ts rename to src/website/packages/solid-signals/types.d.ts diff --git a/website/packages/solid-signals/wrapper.js b/src/website/packages/solid-signals/wrapper.js similarity index 100% rename from website/packages/solid-signals/wrapper.js rename to src/website/packages/solid-signals/wrapper.js diff --git a/website/packages/ufuzzy/README.md b/src/website/packages/ufuzzy/README.md similarity index 100% rename from website/packages/ufuzzy/README.md rename to src/website/packages/ufuzzy/README.md diff --git a/website/packages/ufuzzy/v1.0.14/script.js b/src/website/packages/ufuzzy/v1.0.14/script.js similarity index 100% rename from website/packages/ufuzzy/v1.0.14/script.js rename to src/website/packages/ufuzzy/v1.0.14/script.js diff --git a/website/packages/ufuzzy/v1.0.14/types.d.ts b/src/website/packages/ufuzzy/v1.0.14/types.d.ts similarity index 100% rename from website/packages/ufuzzy/v1.0.14/types.d.ts rename to src/website/packages/ufuzzy/v1.0.14/types.d.ts diff --git a/website/robots.txt b/src/website/robots.txt similarity index 100% rename from website/robots.txt rename to src/website/robots.txt diff --git a/website/scripts/chart.js b/src/website/scripts/chart.js similarity index 100% rename from website/scripts/chart.js rename to src/website/scripts/chart.js diff --git a/website/scripts/live-price.js b/src/website/scripts/live-price.js similarity index 100% rename from website/scripts/live-price.js rename to src/website/scripts/live-price.js diff --git a/website/scripts/main.js b/src/website/scripts/main.js similarity index 99% rename from website/scripts/main.js rename to src/website/scripts/main.js index 17edce5b5..9e3f3d7b7 100644 --- a/website/scripts/main.js +++ b/src/website/scripts/main.js @@ -1311,10 +1311,10 @@ function initFrameSelectors() { function setAsideParent() { const { clientWidth } = window.document.documentElement; const { aside, body, main } = elements; - if (clientWidth >= consts.MEDIUM_WIDTH) { - aside.parentElement !== body && body.append(aside); - } else { - aside.parentElement !== main && main.append(aside); + if (clientWidth >= consts.MEDIUM_WIDTH && aside.parentElement !== body) { + body.append(aside); + } else if (aside.parentElement !== main) { + main.append(aside); } } diff --git a/website/scripts/moscow-time.js b/src/website/scripts/moscow-time.js similarity index 100% rename from website/scripts/moscow-time.js rename to src/website/scripts/moscow-time.js diff --git a/website/scripts/options.js b/src/website/scripts/options.js similarity index 100% rename from website/scripts/options.js rename to src/website/scripts/options.js diff --git a/website/scripts/service-worker.js b/src/website/scripts/service-worker.js similarity index 100% rename from website/scripts/service-worker.js rename to src/website/scripts/service-worker.js diff --git a/website/scripts/simulation.js b/src/website/scripts/simulation.js similarity index 100% rename from website/scripts/simulation.js rename to src/website/scripts/simulation.js diff --git a/website/scripts/types/self.d.ts b/src/website/scripts/types/self.d.ts similarity index 100% rename from website/scripts/types/self.d.ts rename to src/website/scripts/types/self.d.ts diff --git a/website/styles/chart.css b/src/website/styles/chart.css similarity index 100% rename from website/styles/chart.css rename to src/website/styles/chart.css diff --git a/website/styles/live-price.css b/src/website/styles/live-price.css similarity index 100% rename from website/styles/live-price.css rename to src/website/styles/live-price.css diff --git a/website/styles/moscow-time.css b/src/website/styles/moscow-time.css similarity index 100% rename from website/styles/moscow-time.css rename to src/website/styles/moscow-time.css diff --git a/website/styles/simulation.css b/src/website/styles/simulation.css similarity index 100% rename from website/styles/simulation.css rename to src/website/styles/simulation.css diff --git a/website/tsconfig.json b/src/website/tsconfig.json similarity index 100% rename from website/tsconfig.json rename to src/website/tsconfig.json From c883ed19d6430827b2258e018c0c69bfd36e0f8e Mon Sep 17 00:00:00 2001 From: nym21 Date: Fri, 13 Dec 2024 19:57:08 +0100 Subject: [PATCH 04/63] biter: readme: update --- crates/biter/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/biter/README.md b/crates/biter/README.md index 13c3097e6..57bee26a6 100644 --- a/crates/biter/README.md +++ b/crates/biter/README.md @@ -1,4 +1,4 @@ -# Biter +# biter Biter (Bitcoin Block Iterator) is a very fast and simple Rust library which reads raw block files (*blkXXXXX.dat*) from Bitcoin Core Node and creates an iterator over all the requested blocks in sequential order (0, 1, 2, ...). @@ -55,7 +55,7 @@ Peak memory should be around 500MB. ## Comparaison -| | [biter](https://crates.io/crates/biter) | [bitcoin-explorer (depreciated)](https://crates.io/crates/bitcoin-explorer) | [blocks_iterator](https://crates.io/crates/blocks_iterator) | +| | [biter](https://crates.io/crates/biter) | [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 ✅ | From 50c77b51db8214680f548ee9efcf7ba8ebcf6388 Mon Sep 17 00:00:00 2001 From: nym21 Date: Sat, 14 Dec 2024 14:55:44 +0100 Subject: [PATCH 05/63] snkrj: move database struct to its own crate --- CHANGELOG.md | 3 +- Cargo.lock | 9 +- Cargo.toml | 2 +- crates/biter/LICENSE.md | 2 +- crates/snkrj/Cargo.lock | 242 +++++++++++++++++ crates/snkrj/Cargo.toml | 12 + crates/snkrj/LICENSE.md | 21 ++ crates/snkrj/README.md | 37 +++ crates/snkrj/src/lib.rs | 252 ++++++++++++++++++ crates/snkrj/src/main.rs | 29 ++ src/parser/actions/iter_blocks.rs | 4 +- src/parser/actions/parse.rs | 28 +- src/parser/databases/_database.rs | 229 ---------------- src/parser/databases/_trait.rs | 3 +- .../address_index_to_address_data.rs | 18 +- .../address_index_to_empty_address_data.rs | 18 +- .../databases/address_to_address_index.rs | 38 ++- src/parser/databases/mod.rs | 3 +- src/parser/databases/txid_to_tx_data.rs | 53 +--- .../databases/txout_index_to_address_index.rs | 16 +- src/parser/databases/txout_index_to_amount.rs | 16 +- src/structs/address_data.rs | 2 +- src/structs/amount.rs | 2 +- src/structs/array.rs | 2 +- src/structs/date.rs | 4 + src/structs/empty_address_data.rs | 2 +- src/structs/tx_data.rs | 2 +- src/structs/txout_index.rs | 2 +- 28 files changed, 702 insertions(+), 349 deletions(-) create mode 100644 crates/snkrj/Cargo.lock create mode 100644 crates/snkrj/Cargo.toml create mode 100644 crates/snkrj/LICENSE.md create mode 100644 crates/snkrj/README.md create mode 100644 crates/snkrj/src/lib.rs create mode 100644 crates/snkrj/src/main.rs delete mode 100644 src/parser/databases/_database.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 97b4a75e0..339cc3a3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,12 @@ # v0.6.0 | WIP - Merged parser and server crates into a single project (and thus executable) -- Started using `log` and `env_logger` crates +- Started using `log` and `env_logger` crates instead of custom code - Improved logs - Added `--server BOOL` and `--parser BOOL` parameters (both are true by default) - Automated databases defragmention (and removed parameter) - Fixed input being unfocused right after being focused in Brave browser +- Moved Sanakirja database wrapper to its own crate: `snkrj` # [v0.5.0](https://github.com/kibo-money/kibo/tree/eea56d394bf92c62c81da8b78b8c47ea730683f5) | [873199](https://mempool.space/block/0000000000000000000270925aa6a565be92e13164565a3f7994ca1966e48050) - 2024/12/04 diff --git a/Cargo.lock b/Cargo.lock index cd0f6c81f..e72dc3740 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1524,9 +1524,9 @@ dependencies = [ "regex", "reqwest", "rlimit", - "sanakirja", "serde", "serde_json", + "snkrj", "struct_iterable", "swc", "swc_common", @@ -2621,6 +2621,13 @@ dependencies = [ "version_check", ] +[[package]] +name = "snkrj" +version = "0.1.0" +dependencies = [ + "sanakirja", +] + [[package]] name = "socket2" version = "0.5.7" diff --git a/Cargo.toml b/Cargo.toml index bd7c6bde3..19d50bc1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ rayon = "1.10.0" regex = "1.11.1" reqwest = { version = "0.12.9", features = ["blocking", "json"] } rlimit = "0.10.2" -sanakirja = "1.4.3" +snkrj = { path = "./crates/snkrj" } serde = { version = "1.0.216", features = ["derive"] } serde_json = "1.0.133" struct_iterable = { path = "./crates/iterable" } diff --git a/crates/biter/LICENSE.md b/crates/biter/LICENSE.md index 690c7b5da..3fd127e58 100644 --- a/crates/biter/LICENSE.md +++ b/crates/biter/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Biter +Copyright (c) 2024 biter Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/snkrj/Cargo.lock b/crates/snkrj/Cargo.lock new file mode 100644 index 000000000..458967873 --- /dev/null +++ b/crates/snkrj/Cargo.lock @@ -0,0 +1,242 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "libc" +version = "0.2.168" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "sanakirja" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81aaf70d064e2122209f04d01fd91e8908e7a327b516236e1cbc0c3f34ac6d11" +dependencies = [ + "fs2", + "log", + "memmap2", + "parking_lot", + "sanakirja-core", + "serde", + "thiserror", +] + +[[package]] +name = "sanakirja-core" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8376db34ae3eac6e7bd91168bc638450073b708ce9fb46940de676f552238bf5" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "snkrj" +version = "0.1.0" +dependencies = [ + "sanakirja", +] + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/crates/snkrj/Cargo.toml b/crates/snkrj/Cargo.toml new file mode 100644 index 000000000..864b234b6 --- /dev/null +++ b/crates/snkrj/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "snkrj" +description = "A simple wrapper around Sanakirja aatabase that acts as a very fast on disk BTreeMap" +version = "0.1.0" +license = "MIT" +repository = "https://github.com/kibo-money/kibo/tree/main/crates/snkrj" +keywords = ["database", "sanakirja", "btreemap"] +categories = ["database"] +edition = "2021" + +[dependencies] +sanakirja = "1.4.3" diff --git a/crates/snkrj/LICENSE.md b/crates/snkrj/LICENSE.md new file mode 100644 index 000000000..aac235dcb --- /dev/null +++ b/crates/snkrj/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 snkrj + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/snkrj/README.md b/crates/snkrj/README.md new file mode 100644 index 000000000..1803d180c --- /dev/null +++ b/crates/snkrj/README.md @@ -0,0 +1,37 @@ +# snkrj + +A simple wrapper around Sanakirja aatabase that acts as a very fast on disk BTreeMap. + +## Example + +```rust +use snkrj::{AnyDatabase, Database}; + +fn main() { + let path = std::env::temp_dir().join("./db"); + + let database: Database = Database::open(path.clone()).unwrap(); + let _ = database.destroy(); + + let mut database: Database = Database::open(path.clone()).unwrap(); + database.insert(64, 128); + database.export(false).unwrap(); + + let mut database: Database = Database::open(path).unwrap(); + database.insert(1, 2); + database.insert(128, 256); + println!("iter_ram:"); + database.iter_ram().for_each(|pair| { + println!("{:?}", pair); + }); + println!("iter_disk:"); + database.iter_disk().for_each(|pair| { + println!("{:?}", pair.unwrap()); + }); + println!("iter_ram_then_disk:"); + database.iter_ram_then_disk().for_each(|pair| { + println!("{:?}", pair); + }); + database.export(false).unwrap(); +} +``` diff --git a/crates/snkrj/src/lib.rs b/crates/snkrj/src/lib.rs new file mode 100644 index 000000000..a99598595 --- /dev/null +++ b/crates/snkrj/src/lib.rs @@ -0,0 +1,252 @@ +// https://docs.rs/sanakirja/latest/sanakirja/index.html +// https://pijul.org/posts/2021-02-06-rethinking-sanakirja/ + +use std::{ + collections::{BTreeMap, BTreeSet}, + fmt::Debug, + fs, io, mem, + path::PathBuf, + result::Result, +}; + +use sanakirja::btree::{page, Db_}; +pub use sanakirja::*; + +/// +/// A simple wrapper around Sanakirja aatabase that acts as a very fast on disk BTreeMap. +/// +/// The state of the tree is uncommited until `.export()` is called during which it is unsafe to stop the program. +/// +pub struct Database +where + Key: Ord + Clone + Debug + Storable, + Value: Storable + PartialEq, +{ + path: PathBuf, + puts: BTreeMap, + dels: BTreeSet, + db: Db_>, + txn: MutTxn, +} + +const ROOT_DB: usize = 0; +const PAGE_SIZE: u64 = 4096; + +impl Database +where + Key: Ord + Clone + Debug + Storable, + Value: Storable + PartialEq, +{ + /// Open a database without a lock file where only one instance is safe to open. + pub fn open(path: PathBuf) -> Result { + let env = unsafe { Env::new_nolock(&path, PAGE_SIZE, 1)? }; + + let mut txn = Env::mut_txn_begin(env)?; + + let db = txn + .root_db(ROOT_DB) + .unwrap_or_else(|| unsafe { btree::create_db_(&mut txn).unwrap() }); + + Ok(Self { + path, + puts: BTreeMap::default(), + dels: BTreeSet::default(), + db, + txn, + }) + } + + #[inline] + pub fn get(&self, key: &Key) -> Option<&Value> { + if let Some(cached_put) = self.get_from_ram(key) { + return Some(cached_put); + } + + self.get_from_disk(key) + } + + /// Get only from the uncommited tree (ram) without checking the database (disk) + #[inline] + pub fn get_from_ram(&self, key: &Key) -> Option<&Value> { + self.puts.get(key) + } + + /// Get mut only from the uncommited tree (ram) without checking the database (disk) + #[inline] + pub fn get_mut_from_ram(&mut self, key: &Key) -> Option<&mut Value> { + self.puts.get_mut(key) + } + + /// Get only from the database (disk) without checking the uncommited tree (ram) + #[inline] + pub fn get_from_disk(&self, key: &Key) -> Option<&Value> { + let option = btree::get(&self.txn, &self.db, key, None).unwrap(); + + if let Some((key_found, v)) = option { + if key == key_found { + return Some(v); + } + } + + None + } + + #[inline] + pub fn insert(&mut self, key: Key, value: Value) -> Option { + self.dels.remove(&key); + self.insert_to_ram(key, value) + } + + /// Insert without removing the key to the dels tree, so be sure that it hasn't added to the delete set + #[inline] + pub fn insert_to_ram(&mut self, key: Key, value: Value) -> Option { + self.puts.insert(key, value) + } + + #[inline] + pub fn update(&mut self, key: Key, value: Value) -> Option { + self.dels.insert(key.clone()); + self.puts.insert(key, value) + } + + #[inline] + pub fn remove(&mut self, key: &Key) -> Option { + self.remove_from_ram(key).or_else(|| { + self.remove_later_from_disk(key); + + None + }) + } + + /// Get only from the uncommited tree (ram) without checking the database (disk) + #[inline] + pub fn remove_from_ram(&mut self, key: &Key) -> Option { + self.puts.remove(key) + } + + /// Add the key only to the dels tree without checking if it's present in the puts tree, only use if you are positive that you neither added nor updated an entry with this key + #[inline] + pub fn remove_later_from_disk(&mut self, key: &Key) { + self.dels.insert(key.clone()); + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.iter_disk().next().is_none() + } + + /// Iterate over key/value pairs from the uncommited tree (ram) + #[inline] + pub fn iter_ram(&self) -> std::collections::btree_map::Iter<'_, Key, Value> { + self.puts.iter() + } + + /// Iterate over key/value pairs from the database (disk) + #[inline] + pub fn iter_disk( + &self, + ) -> btree::Iter<'_, MutTxn, Key, Value, page::Page> { + btree::iter(&self.txn, &self.db, None).unwrap() + } + + /// Iterate over key/value pairs + #[inline] + pub fn iter_ram_then_disk(&self) -> impl Iterator { + self.iter_ram().chain(self.iter_disk().map(|r| r.unwrap())) + } + + /// Collect a **clone** of all uncommited key/value pairs (ram) + pub fn collect_ram(&self) -> BTreeMap + where + Value: Clone, + { + self.puts.clone() + } + + /// Collect a **clone** of all key/value pairs from the database (disk) + pub fn collect_disk(&self) -> BTreeMap + where + Value: Clone, + { + self.iter_disk() + .map(|r| r.unwrap()) + .map(|(key, value)| (key.clone(), value.clone())) + .collect::<_>() + } +} + +pub trait AnyDatabase { + #[allow(unused)] + fn export(self, defragment: bool) -> Result<(), Error>; + fn boxed_export(self: Box, defragment: bool) -> Result<(), Error>; + #[allow(unused)] + fn destroy(self) -> io::Result<()>; +} + +impl AnyDatabase for Database +where + Key: Ord + Clone + Debug + Storable, + Value: Storable + PartialEq + Clone, +{ + /// Flush all puts and dels from the ram to disk with an option to defragment the database to save some disk space + /// + /// /!\ Do not kill the program while this function is runnning /!\ + fn export(self, defragment: bool) -> Result<(), Error> { + Box::new(self).boxed_export(defragment) + } + + /// Flush all puts and dels from the ram to disk with an option to defragment the database to save some disk space + /// + /// /!\ Do not kill the program while this function is runnning /!\ + fn boxed_export(mut self: Box, defragment: bool) -> Result<(), Error> { + if defragment { + let mut btree = self.as_ref().collect_disk(); + + let path = self.path.to_owned(); + self.dels.iter().for_each(|key| { + btree.remove(key); + }); + btree.append(&mut self.puts); + + self.destroy()?; + + *self = Self::open(path).unwrap(); + + if !self.is_empty() { + panic!() + } + + self.puts = btree; + } + + if self.dels.is_empty() && self.puts.is_empty() { + return Ok(()); + } + + mem::take(&mut self.dels) + .into_iter() + .try_for_each(|key| -> Result<(), Error> { + btree::del(&mut self.txn, &mut self.db, &key, None)?; + Ok(()) + })?; + + mem::take(&mut self.puts).into_iter().try_for_each( + |(key, value)| -> Result<(), Error> { + btree::put(&mut self.txn, &mut self.db, &key, &value)?; + Ok(()) + }, + )?; + + self.txn.set_root(ROOT_DB, self.db.db.into()); + + self.txn.commit() + } + + fn destroy(self) -> io::Result<()> { + let path = self.path.to_owned(); + + drop(self); + + fs::remove_file(&path) + } +} diff --git a/crates/snkrj/src/main.rs b/crates/snkrj/src/main.rs new file mode 100644 index 000000000..da3c39d23 --- /dev/null +++ b/crates/snkrj/src/main.rs @@ -0,0 +1,29 @@ +use snkrj::{AnyDatabase, Database}; + +fn main() { + let path = std::env::temp_dir().join("./db"); + + let database: Database = Database::open(path.clone()).unwrap(); + let _ = database.destroy(); + + let mut database: Database = Database::open(path.clone()).unwrap(); + database.insert(64, 128); + database.export(false).unwrap(); + + let mut database: Database = Database::open(path).unwrap(); + database.insert(1, 2); + database.insert(128, 256); + println!("iter_ram:"); + database.iter_ram().for_each(|pair| { + println!("{:?}", pair); + }); + println!("iter_disk:"); + database.iter_disk().for_each(|pair| { + println!("{:?}", pair.unwrap()); + }); + println!("iter_ram_then_disk:"); + database.iter_ram_then_disk().for_each(|pair| { + println!("{:?}", pair); + }); + database.export(false).unwrap(); +} diff --git a/src/parser/actions/iter_blocks.rs b/src/parser/actions/iter_blocks.rs index 84e5a4af4..fcd41d254 100644 --- a/src/parser/actions/iter_blocks.rs +++ b/src/parser/actions/iter_blocks.rs @@ -213,7 +213,9 @@ pub fn iter_blocks( let defragment = is_safe && next_date_opt.is_some_and(|date| { - date.year() >= 2020 && date.is_january() && date.is_first_of_month() + (date.year() >= 2020 && date.is_january() + || date.year() >= 2022 && date.is_june()) + && date.is_first_of_month() }); export(ExportedData { diff --git a/src/parser/actions/parse.rs b/src/parser/actions/parse.rs index 648bfb53e..39e5207e8 100644 --- a/src/parser/actions/parse.rs +++ b/src/parser/actions/parse.rs @@ -241,7 +241,7 @@ pub fn parse( databases .txout_index_to_amount - .unsafe_insert(txout_index, amount); + .insert_to_ram(txout_index, amount); if compute_addresses { let address = address.unwrap(); @@ -253,7 +253,7 @@ pub fn parse( if let Some(address_index) = address_index_opt.or_else(|| { databases .address_to_address_index - .unsafe_get_from_puts(&address) + .get_from_ram(&address) .cloned() }) { let address_data = address_index_to_address_data @@ -301,7 +301,7 @@ pub fn parse( databases .txout_index_to_address_index - .unsafe_insert(txout_index, address_index); + .insert_to_ram(txout_index, address_index); } }); @@ -336,9 +336,7 @@ pub fn parse( .or_else(|| { is_tx_data_from_cached_puts = true; - databases - .txid_to_tx_data - .unsafe_get_mut_from_puts(&input_txid) + databases.txid_to_tx_data.get_mut_from_ram(&input_txid) }); // Can be none because 0 sats inputs happen @@ -534,7 +532,7 @@ pub fn parse( if remove_tx_data_from_cached_puts { // Pre remove tx_datas that are empty and weren't yet added to the database to avoid having it was in there or not (and thus avoid useless operations) - databases.txid_to_tx_data.remove_from_puts(&input_txid) + databases.txid_to_tx_data.remove_from_ram(&input_txid) } ControlFlow::Continue(()) @@ -558,7 +556,7 @@ pub fn parse( txid_to_tx_data.into_iter().for_each(|(txid, tx_data)| { if let Some(tx_data) = tx_data { if tx_data.is_empty() { - databases.txid_to_tx_data.remove_from_db(txid); + databases.txid_to_tx_data.remove_later_from_disk(txid); } else { databases.txid_to_tx_data.update(txid, tx_data); } @@ -738,14 +736,14 @@ pub fn parse( address_index_to_address_data.unwrap().into_iter().for_each( |(address_index, address_data)| { if address_data.is_empty() { - databases.address_index_to_empty_address_data.unsafe_insert( + databases.address_index_to_empty_address_data.insert_to_ram( address_index, EmptyAddressData::from_non_empty(&address_data), ); } else { databases .address_index_to_address_data - .unsafe_insert(address_index, address_data); + .insert_to_ram(address_index, address_data); } }, ) @@ -901,7 +899,7 @@ fn prepare_inputs<'a>( let mut tx_datas = txid_to_tx_data .par_iter() - .map(|(txid, _)| txid_to_tx_data_db.unsafe_get(txid)) + .map(|(txid, _)| txid_to_tx_data_db.get(txid)) .collect::>(); txid_to_tx_data.values_mut().rev().for_each(|tx_data_opt| { @@ -992,20 +990,20 @@ fn compute_address_index_to_address_data( .par_iter_mut() .for_each(|(address_index, address_data)| { if let Some(_address_data) = - address_index_to_address_data_db.unsafe_get_from_cache(address_index) + address_index_to_address_data_db.get_from_ram(address_index) { _address_data.clone_into(address_data); } else if let Some(empty_address_data) = - address_index_to_empty_address_data_db.unsafe_get_from_cache(address_index) + address_index_to_empty_address_data_db.get_from_ram(address_index) { *address_data = AddressData::from_empty(empty_address_data); } else if let Some(_address_data) = - address_index_to_address_data_db.unsafe_get_from_db(address_index) + address_index_to_address_data_db.get_from_disk(address_index) { _address_data.clone_into(address_data); } else { let empty_address_data = address_index_to_empty_address_data_db - .unsafe_get_from_db(address_index) + .get_from_disk(address_index) .unwrap(); *address_data = AddressData::from_empty(empty_address_data); diff --git a/src/parser/databases/_database.rs b/src/parser/databases/_database.rs deleted file mode 100644 index bd61f1078..000000000 --- a/src/parser/databases/_database.rs +++ /dev/null @@ -1,229 +0,0 @@ -// https://docs.rs/sanakirja/latest/sanakirja/index.html -// https://pijul.org/posts/2021-02-06-rethinking-sanakirja/ - -use std::{ - collections::{BTreeMap, BTreeSet}, - fmt::Debug, - fs, io, mem, - path::PathBuf, -}; - -use allocative::Allocative; - -use sanakirja::{ - btree::{self, page, Db_, Iter}, - Commit, Env, Error, MutTxn, RootDb, Storable, -}; - -/// -/// Simple wrapper around Sanakirja Database with cached puts and dels for safe use outside exports. -/// -/// There is no `cached_gets` since it's much cheaper and faster to do a parallel search first using `unsafe_get` than caching "gets" along the way. -/// -#[derive(Allocative)] -#[allocative(bound = "Key: Allocative, Value: Allocative")] -pub struct Database -where - Key: Ord + Clone + Debug + Storable, - Value: Storable + PartialEq, -{ - path: PathBuf, - cached_puts: BTreeMap, - cached_dels: BTreeSet, - #[allocative(skip)] - db: Db_>, - #[allocative(skip)] - txn: MutTxn, -} - -const ROOT_DB: usize = 0; -const PAGE_SIZE: u64 = 4096; - -impl Database -where - Key: Ord + Clone + Debug + Storable, - Value: Storable + PartialEq, -{ - pub fn open(path: PathBuf) -> color_eyre::Result { - let env = unsafe { Env::new_nolock(&path, PAGE_SIZE, 1)? }; - - let mut txn = Env::mut_txn_begin(env)?; - - let db = txn - .root_db(ROOT_DB) - .unwrap_or_else(|| unsafe { btree::create_db_(&mut txn).unwrap() }); - - Ok(Self { - path, - cached_puts: BTreeMap::default(), - cached_dels: BTreeSet::default(), - db, - txn, - }) - } - - #[inline] - pub fn iter(&self) -> Iter<'_, MutTxn, Key, Value, page::Page> { - btree::iter(&self.txn, &self.db, None).unwrap() - } - - pub fn collect(&self) -> BTreeMap - where - Value: Clone, - { - self.iter() - .map(|r| r.unwrap()) - .map(|(key, value)| (key.clone(), value.clone())) - .collect::<_>() - } - - #[inline] - pub fn get(&self, key: &Key) -> Option<&Value> { - if let Some(cached_put) = self.get_from_puts(key) { - return Some(cached_put); - } - - self.db_get(key) - } - - #[inline] - pub fn db_get(&self, key: &Key) -> Option<&Value> { - let option = btree::get(&self.txn, &self.db, key, None).unwrap(); - - if let Some((key_found, v)) = option { - if key == key_found { - return Some(v); - } - } - - None - } - - #[inline] - pub fn get_from_puts(&self, key: &Key) -> Option<&Value> { - self.cached_puts.get(key) - } - - #[inline] - pub fn get_mut_from_puts(&mut self, key: &Key) -> Option<&mut Value> { - self.cached_puts.get_mut(key) - } - - #[inline] - pub fn remove(&mut self, key: &Key) -> Option { - self.remove_from_puts(key).or_else(|| { - self.db_remove(key); - - None - }) - } - - #[inline] - pub fn db_remove(&mut self, key: &Key) { - self.cached_dels.insert(key.clone()); - } - - #[inline] - pub fn update(&mut self, key: Key, value: Value) -> Option { - self.cached_dels.insert(key.clone()); - self.cached_puts.insert(key, value) - } - - #[inline] - pub fn is_empty(&self) -> bool { - self.iter().next().is_none() - } - - #[inline] - pub fn remove_from_puts(&mut self, key: &Key) -> Option { - self.cached_puts.remove(key) - } - - #[inline] - pub fn insert(&mut self, key: Key, value: Value) -> Option { - self.cached_dels.remove(&key); - self.unsafe_insert(key, value) - } - - #[inline] - pub fn unsafe_insert(&mut self, key: Key, value: Value) -> Option { - self.cached_puts.insert(key, value) - } - - fn db_multi_put(&mut self, tree: BTreeMap) -> Result<(), Error> { - tree.into_iter() - .try_for_each(|(key, value)| -> Result<(), Error> { - btree::put(&mut self.txn, &mut self.db, &key, &value)?; - Ok(()) - }) - } - - fn db_multi_del(&mut self, tree: BTreeSet) -> Result<(), Error> { - tree.into_iter().try_for_each(|key| -> Result<(), Error> { - btree::del(&mut self.txn, &mut self.db, &key, None)?; - Ok(()) - }) - } -} - -pub trait AnyDatabase { - #[allow(unused)] - fn export(self, defragment: bool) -> color_eyre::Result<(), Error>; - fn boxed_export(self: Box, defragment: bool) -> color_eyre::Result<(), Error>; - #[allow(unused)] - fn destroy(self) -> io::Result<()>; -} - -impl AnyDatabase for Database -where - Key: Ord + Clone + Debug + Storable, - Value: Storable + PartialEq + Clone, -{ - fn export(self, defragment: bool) -> color_eyre::Result<(), Error> { - Box::new(self).boxed_export(defragment) - } - - fn boxed_export(mut self: Box, defragment: bool) -> color_eyre::Result<(), Error> { - if defragment { - let mut btree = self.as_ref().collect(); - - let path = self.path.to_owned(); - self.cached_dels.iter().for_each(|key| { - btree.remove(key); - }); - btree.append(&mut self.cached_puts); - - self.destroy()?; - - *self = Self::open(path).unwrap(); - - if !self.is_empty() { - panic!() - } - - self.cached_puts = btree; - } - - if self.cached_dels.is_empty() && self.cached_puts.is_empty() { - return Ok(()); - } - - let cached_dels = mem::take(&mut self.cached_dels); - self.db_multi_del(cached_dels)?; - - let cached_puts = mem::take(&mut self.cached_puts); - self.db_multi_put(cached_puts)?; - - self.txn.set_root(ROOT_DB, self.db.db.into()); - - self.txn.commit() - } - - fn destroy(self) -> io::Result<()> { - let path = self.path.to_owned(); - - drop(self); - - fs::remove_file(&path) - } -} diff --git a/src/parser/databases/_trait.rs b/src/parser/databases/_trait.rs index f238c82b1..5da93dd8a 100644 --- a/src/parser/databases/_trait.rs +++ b/src/parser/databases/_trait.rs @@ -1,10 +1,11 @@ use std::{fs, io, path::Path}; use log::info; +use snkrj::AnyDatabase; use crate::structs::{Config, Date, Height}; -use super::{AnyDatabase, Metadata}; +use super::Metadata; pub trait AnyDatabaseGroup where diff --git a/src/parser/databases/address_index_to_address_data.rs b/src/parser/databases/address_index_to_address_data.rs index 82c90acf0..a56137b7f 100644 --- a/src/parser/databases/address_index_to_address_data.rs +++ b/src/parser/databases/address_index_to_address_data.rs @@ -8,6 +8,7 @@ use std::{ use allocative::Allocative; use itertools::Itertools; use rayon::prelude::*; +use snkrj::{AnyDatabase, Database as _Database}; use crate::{ parser::states::AddressCohortsDurableStates, @@ -15,7 +16,7 @@ use crate::{ utils::time, }; -use super::{AnyDatabase, AnyDatabaseGroup, Database as _Database, Metadata}; +use super::{AnyDatabaseGroup, Metadata}; type Key = u32; type Value = AddressData; @@ -25,6 +26,7 @@ type Database = _Database; pub struct AddressIndexToAddressData { path: PathBuf, pub metadata: Metadata, + #[allocative(skip)] pub map: BTreeMap, } @@ -45,10 +47,10 @@ impl DerefMut for AddressIndexToAddressData { pub const ADDRESS_INDEX_DB_MAX_SIZE: usize = 250_000; impl AddressIndexToAddressData { - pub fn unsafe_insert(&mut self, key: Key, value: Value) -> Option { + pub fn insert_to_ram(&mut self, key: Key, value: Value) -> Option { self.metadata.called_insert(); - self.open_db(&key).unsafe_insert(key, value) + self.open_db(&key).insert_to_ram(key, value) } pub fn remove(&mut self, key: &Key) -> Option { @@ -59,16 +61,16 @@ impl AddressIndexToAddressData { /// Doesn't check if the database is open contrary to `safe_get` which does and opens if needed /// Though it makes it easy to use with rayon. - pub fn unsafe_get_from_cache(&self, key: &Key) -> Option<&Value> { + pub fn get_from_ram(&self, key: &Key) -> Option<&Value> { let db_index = Self::db_index(key); - self.get(&db_index).unwrap().get_from_puts(key) + self.get(&db_index).unwrap().get_from_ram(key) } - pub fn unsafe_get_from_db(&self, key: &Key) -> Option<&Value> { + pub fn get_from_disk(&self, key: &Key) -> Option<&Value> { let db_index = Self::db_index(key); - self.get(&db_index).unwrap().db_get(key) + self.get(&db_index).unwrap().get_from_disk(key) } pub fn open_db(&mut self, key: &Key) -> &mut Database { @@ -99,7 +101,7 @@ impl AddressIndexToAddressData { let mut s = AddressCohortsDurableStates::default(); database - .iter() + .iter_disk() .map(|r| r.unwrap().1) .for_each(|address_data| s.increment(address_data).unwrap()); diff --git a/src/parser/databases/address_index_to_empty_address_data.rs b/src/parser/databases/address_index_to_empty_address_data.rs index 923ae4850..df940c2f3 100644 --- a/src/parser/databases/address_index_to_empty_address_data.rs +++ b/src/parser/databases/address_index_to_empty_address_data.rs @@ -7,12 +7,11 @@ use std::{ use allocative::Allocative; use itertools::Itertools; +use snkrj::{AnyDatabase, Database as _Database}; use crate::structs::{Config, EmptyAddressData}; -use super::{ - AnyDatabase, AnyDatabaseGroup, Database as _Database, Metadata, ADDRESS_INDEX_DB_MAX_SIZE, -}; +use super::{AnyDatabaseGroup, Metadata, ADDRESS_INDEX_DB_MAX_SIZE}; type Key = u32; type Value = EmptyAddressData; @@ -22,6 +21,7 @@ type Database = _Database; pub struct AddressIndexToEmptyAddressData { path: PathBuf, pub metadata: Metadata, + #[allocative(skip)] map: BTreeMap, } @@ -40,10 +40,10 @@ impl DerefMut for AddressIndexToEmptyAddressData { } impl AddressIndexToEmptyAddressData { - pub fn unsafe_insert(&mut self, key: Key, value: Value) -> Option { + pub fn insert_to_ram(&mut self, key: Key, value: Value) -> Option { self.metadata.called_insert(); - self.open_db(&key).unsafe_insert(key, value) + self.open_db(&key).insert_to_ram(key, value) } pub fn remove(&mut self, key: &Key) -> Option { @@ -54,13 +54,13 @@ impl AddressIndexToEmptyAddressData { /// Doesn't check if the database is open contrary to `safe_get` which does and opens if needed /// Though it makes it easy to use with rayon. - pub fn unsafe_get_from_cache(&self, key: &Key) -> Option<&Value> { + pub fn get_from_ram(&self, key: &Key) -> Option<&Value> { let db_index = Self::db_index(key); - self.get(&db_index).and_then(|db| db.get_from_puts(key)) + self.get(&db_index).and_then(|db| db.get_from_ram(key)) } - pub fn unsafe_get_from_db(&self, key: &Key) -> Option<&Value> { + pub fn get_from_disk(&self, key: &Key) -> Option<&Value> { let db_index = Self::db_index(key); self.get(&db_index) @@ -68,7 +68,7 @@ impl AddressIndexToEmptyAddressData { dbg!(&self.map.keys(), &key, &db_index); panic!() }) - .db_get(key) + .get_from_disk(key) } pub fn open_db(&mut self, key: &Key) -> &mut Database { diff --git a/src/parser/databases/address_to_address_index.rs b/src/parser/databases/address_to_address_index.rs index 35d9b7e30..01518a9fe 100644 --- a/src/parser/databases/address_to_address_index.rs +++ b/src/parser/databases/address_to_address_index.rs @@ -6,10 +6,11 @@ use std::{ use allocative::Allocative; use itertools::Itertools; +use snkrj::{AnyDatabase, Database}; use crate::structs::{Address, Config, U8x19, U8x31}; -use super::{AnyDatabase, AnyDatabaseGroup, Database, Metadata}; +use super::{AnyDatabaseGroup, Metadata}; type Value = u32; type U8x19Database = Database; @@ -33,16 +34,27 @@ pub struct AddressToAddressIndex { path: PathBuf, pub metadata: Metadata, + #[allocative(skip)] p2pk: BTreeMap, + #[allocative(skip)] p2pkh: BTreeMap, + #[allocative(skip)] p2sh: BTreeMap, + #[allocative(skip)] p2wpkh: BTreeMap, + #[allocative(skip)] p2wsh: BTreeMap, + #[allocative(skip)] p2tr: BTreeMap, + #[allocative(skip)] op_return: Option, + #[allocative(skip)] push_only: Option, + #[allocative(skip)] unknown: Option, + #[allocative(skip)] empty: Option, + #[allocative(skip)] multisig: Option, } @@ -103,19 +115,19 @@ impl AddressToAddressIndex { } } - pub fn unsafe_get_from_puts(&self, address: &Address) -> Option<&Value> { + pub fn get_from_ram(&self, address: &Address) -> Option<&Value> { match address { - Address::Empty(key) => self.empty.as_ref().unwrap().get_from_puts(key), - Address::Unknown(key) => self.unknown.as_ref().unwrap().get_from_puts(key), - Address::OpReturn(key) => self.op_return.as_ref().unwrap().get_from_puts(key), - Address::PushOnly(key) => self.push_only.as_ref().unwrap().get_from_puts(key), - Address::MultiSig(key) => self.multisig.as_ref().unwrap().get_from_puts(key), - Address::P2PK((prefix, key)) => self.p2pk.get(prefix).unwrap().get_from_puts(key), - Address::P2PKH((prefix, key)) => self.p2pkh.get(prefix).unwrap().get_from_puts(key), - Address::P2SH((prefix, key)) => self.p2sh.get(prefix).unwrap().get_from_puts(key), - Address::P2WPKH((prefix, key)) => self.p2wpkh.get(prefix).unwrap().get_from_puts(key), - Address::P2WSH((prefix, key)) => self.p2wsh.get(prefix).unwrap().get_from_puts(key), - Address::P2TR((prefix, key)) => self.p2tr.get(prefix).unwrap().get_from_puts(key), + Address::Empty(key) => self.empty.as_ref().unwrap().get_from_ram(key), + Address::Unknown(key) => self.unknown.as_ref().unwrap().get_from_ram(key), + Address::OpReturn(key) => self.op_return.as_ref().unwrap().get_from_ram(key), + Address::PushOnly(key) => self.push_only.as_ref().unwrap().get_from_ram(key), + Address::MultiSig(key) => self.multisig.as_ref().unwrap().get_from_ram(key), + Address::P2PK((prefix, key)) => self.p2pk.get(prefix).unwrap().get_from_ram(key), + Address::P2PKH((prefix, key)) => self.p2pkh.get(prefix).unwrap().get_from_ram(key), + Address::P2SH((prefix, key)) => self.p2sh.get(prefix).unwrap().get_from_ram(key), + Address::P2WPKH((prefix, key)) => self.p2wpkh.get(prefix).unwrap().get_from_ram(key), + Address::P2WSH((prefix, key)) => self.p2wsh.get(prefix).unwrap().get_from_ram(key), + Address::P2TR((prefix, key)) => self.p2tr.get(prefix).unwrap().get_from_ram(key), } } diff --git a/src/parser/databases/mod.rs b/src/parser/databases/mod.rs index 9b32a982c..3370d914a 100644 --- a/src/parser/databases/mod.rs +++ b/src/parser/databases/mod.rs @@ -1,6 +1,5 @@ use allocative::Allocative; -mod _database; mod _trait; mod address_index_to_address_data; mod address_index_to_empty_address_data; @@ -10,7 +9,6 @@ mod txid_to_tx_data; mod txout_index_to_address_index; mod txout_index_to_amount; -pub use _database::*; use _trait::*; pub use address_index_to_address_data::*; pub use address_index_to_empty_address_data::*; @@ -19,6 +17,7 @@ use itertools::Itertools; use log::info; use metadata::*; use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use snkrj::AnyDatabase; pub use txid_to_tx_data::*; pub use txout_index_to_address_index::*; pub use txout_index_to_amount::*; diff --git a/src/parser/databases/txid_to_tx_data.rs b/src/parser/databases/txid_to_tx_data.rs index 14ee58555..399c760d9 100644 --- a/src/parser/databases/txid_to_tx_data.rs +++ b/src/parser/databases/txid_to_tx_data.rs @@ -1,17 +1,17 @@ use std::{ collections::BTreeMap, fs, mem, - ops::{Deref, DerefMut}, path::{Path, PathBuf}, }; use allocative::Allocative; use biter::bitcoin::Txid; use itertools::Itertools; +use snkrj::{AnyDatabase, Database as _Database}; use crate::structs::{Config, TxData, U8x31}; -use super::{AnyDatabase, AnyDatabaseGroup, Database as _Database, Metadata}; +use super::{AnyDatabaseGroup, Metadata}; type Key = U8x31; type Value = TxData; @@ -21,23 +21,10 @@ type Database = _Database; pub struct TxidToTxData { path: PathBuf, pub metadata: Metadata, + #[allocative(skip)] map: BTreeMap, } -impl Deref for TxidToTxData { - type Target = BTreeMap; - - fn deref(&self) -> &Self::Target { - &self.map - } -} - -impl DerefMut for TxidToTxData { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.map - } -} - impl TxidToTxData { pub fn insert(&mut self, txid: &Txid, tx_index: Value) -> Option { self.metadata.called_insert(); @@ -47,53 +34,41 @@ impl TxidToTxData { self.open_db(txid).insert(txid_key, tx_index) } - // pub fn safe_get(&mut self, txid: &Txid) -> Option<&Value> { - // let txid_key = Self::txid_to_key(txid); - // self.open_db(txid).get(&txid_key) - // } - /// Doesn't check if the database is open contrary to `safe_get` which does and opens if needed. /// Though it makes it easy to use with rayon - pub fn unsafe_get(&self, txid: &Txid) -> Option<&Value> { + pub fn get(&self, txid: &Txid) -> Option<&Value> { let txid_key = Self::txid_to_key(txid); let db_index = Self::db_index(txid); - self.get(&db_index).unwrap().get(&txid_key) + self.map.get(&db_index).unwrap().get(&txid_key) } - // pub fn unsafe_get_from_puts(&self, txid: &Txid) -> Option<&Value> { - // let txid_key = Self::txid_to_key(txid); - - // let db_index = Self::db_index(txid); - - // self.get(&db_index).unwrap().get_from_puts(&txid_key) - // } - - pub fn unsafe_get_mut_from_puts(&mut self, txid: &Txid) -> Option<&mut Value> { + pub fn get_mut_from_ram(&mut self, txid: &Txid) -> Option<&mut Value> { let txid_key = Self::txid_to_key(txid); let db_index = Self::db_index(txid); - self.get_mut(&db_index) + self.map + .get_mut(&db_index) .unwrap() - .get_mut_from_puts(&txid_key) + .get_mut_from_ram(&txid_key) } - pub fn remove_from_db(&mut self, txid: &Txid) { + pub fn remove_later_from_disk(&mut self, txid: &Txid) { self.metadata.called_remove(); let txid_key = Self::txid_to_key(txid); - self.open_db(txid).db_remove(&txid_key); + self.open_db(txid).remove_later_from_disk(&txid_key); } - pub fn remove_from_puts(&mut self, txid: &Txid) { + pub fn remove_from_ram(&mut self, txid: &Txid) { self.metadata.called_remove(); let txid_key = Self::txid_to_key(txid); - self.open_db(txid).remove_from_puts(&txid_key); + self.open_db(txid).remove_from_ram(&txid_key); } pub fn update(&mut self, txid: &Txid, tx_data: TxData) { @@ -111,7 +86,7 @@ impl TxidToTxData { #[inline(always)] pub fn _open_db(&mut self, db_index: u16) -> &mut Database { let path = self.path.to_owned(); - self.entry(db_index).or_insert_with(|| { + self.map.entry(db_index).or_insert_with(|| { let path = path.join(db_index.to_string()); Database::open(path).unwrap() }) diff --git a/src/parser/databases/txout_index_to_address_index.rs b/src/parser/databases/txout_index_to_address_index.rs index 5db9f4484..4e060ddfc 100644 --- a/src/parser/databases/txout_index_to_address_index.rs +++ b/src/parser/databases/txout_index_to_address_index.rs @@ -7,10 +7,11 @@ use std::{ use allocative::Allocative; use itertools::Itertools; +use snkrj::{AnyDatabase, Database as _Database}; use crate::structs::{Config, TxoutIndex}; -use super::{AnyDatabase, AnyDatabaseGroup, Database as _Database, Metadata}; +use super::{AnyDatabaseGroup, Metadata}; type Key = TxoutIndex; type Value = u32; @@ -20,6 +21,7 @@ type Database = _Database; pub struct TxoutIndexToAddressIndex { path: PathBuf, pub metadata: Metadata, + #[allocative(skip)] map: BTreeMap, } @@ -40,20 +42,12 @@ impl DerefMut for TxoutIndexToAddressIndex { const DB_MAX_SIZE: usize = 10_000_000_000; impl TxoutIndexToAddressIndex { - pub fn unsafe_insert(&mut self, key: Key, value: Value) -> Option { + pub fn insert_to_ram(&mut self, key: Key, value: Value) -> Option { self.metadata.called_insert(); - self.open_db(&key).unsafe_insert(key, value) + self.open_db(&key).insert_to_ram(key, value) } - // pub fn undo_insert(&mut self, key: &Key) -> Option { - // self.open_db(key).remove_from_puts(key).map(|v| { - // self.metadata.called_remove(); - - // v - // }) - // } - pub fn remove(&mut self, key: &Key) -> Option { self.metadata.called_remove(); diff --git a/src/parser/databases/txout_index_to_amount.rs b/src/parser/databases/txout_index_to_amount.rs index 0464b04c7..bc86a2c2e 100644 --- a/src/parser/databases/txout_index_to_amount.rs +++ b/src/parser/databases/txout_index_to_amount.rs @@ -7,10 +7,11 @@ use std::{ use allocative::Allocative; use itertools::Itertools; +use snkrj::{AnyDatabase, Database as _Database}; use crate::structs::{Amount, Config, TxoutIndex}; -use super::{AnyDatabase, AnyDatabaseGroup, Database as _Database, Metadata}; +use super::{AnyDatabaseGroup, Metadata}; type Key = TxoutIndex; type Value = Amount; @@ -20,6 +21,7 @@ type Database = _Database; pub struct TxoutIndexToAmount { path: PathBuf, pub metadata: Metadata, + #[allocative(skip)] map: BTreeMap, } @@ -40,20 +42,12 @@ impl DerefMut for TxoutIndexToAmount { const DB_MAX_SIZE: usize = 10_000_000_000; impl TxoutIndexToAmount { - pub fn unsafe_insert(&mut self, key: Key, value: Value) -> Option { + pub fn insert_to_ram(&mut self, key: Key, value: Value) -> Option { self.metadata.called_insert(); - self.open_db(&key).unsafe_insert(key, value) + self.open_db(&key).insert_to_ram(key, value) } - // pub fn undo_insert(&mut self, key: &Key) -> Option { - // self.open_db(key).remove_from_puts(key).map(|v| { - // self.metadata.called_remove(); - - // v - // }) - // } - pub fn remove(&mut self, key: &Key) -> Option { self.metadata.called_remove(); diff --git a/src/structs/address_data.rs b/src/structs/address_data.rs index 3d0968d0a..636819317 100644 --- a/src/structs/address_data.rs +++ b/src/structs/address_data.rs @@ -1,6 +1,6 @@ use allocative::Allocative; use color_eyre::eyre::eyre; -use sanakirja::{direct_repr, Storable, UnsizedStorable}; +use snkrj::{direct_repr, Storable, UnsizedStorable}; use super::{AddressType, Amount, EmptyAddressData, LiquidityClassification, Price}; diff --git a/src/structs/amount.rs b/src/structs/amount.rs index 6ff8ce1d4..8308a7d40 100644 --- a/src/structs/amount.rs +++ b/src/structs/amount.rs @@ -12,8 +12,8 @@ use bincode::{ }; use biter::bitcoin::Amount as BitcoinAmount; use derive_deref::{Deref, DerefMut}; -use sanakirja::{direct_repr, Storable, UnsizedStorable}; use serde::{Deserialize, Serialize}; +use snkrj::{direct_repr, Storable, UnsizedStorable}; use super::Height; diff --git a/src/structs/array.rs b/src/structs/array.rs index dbfb67592..8a915db6d 100644 --- a/src/structs/array.rs +++ b/src/structs/array.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use allocative::Allocative; use derive_deref::{Deref, DerefMut}; -use sanakirja::{direct_repr, Storable, UnsizedStorable}; +use snkrj::{direct_repr, Storable, UnsizedStorable}; #[derive( Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deref, DerefMut, Default, Copy, Allocative, diff --git a/src/structs/date.rs b/src/structs/date.rs index c2d51dd51..da448f6bb 100644 --- a/src/structs/date.rs +++ b/src/structs/date.rs @@ -74,6 +74,10 @@ impl Date { self.month() == 1 } + pub fn is_june(&self) -> bool { + self.month() == 6 + } + pub fn is_first_of_month(&self) -> bool { self.day() == 1 } diff --git a/src/structs/empty_address_data.rs b/src/structs/empty_address_data.rs index a7613efcb..48aaa7784 100644 --- a/src/structs/empty_address_data.rs +++ b/src/structs/empty_address_data.rs @@ -1,5 +1,5 @@ use allocative::Allocative; -use sanakirja::{direct_repr, Storable, UnsizedStorable}; +use snkrj::{direct_repr, Storable, UnsizedStorable}; use super::{AddressData, AddressType, Amount}; diff --git a/src/structs/tx_data.rs b/src/structs/tx_data.rs index 1a577e8be..162ed456f 100644 --- a/src/structs/tx_data.rs +++ b/src/structs/tx_data.rs @@ -1,5 +1,5 @@ use allocative::Allocative; -use sanakirja::{direct_repr, Storable, UnsizedStorable}; +use snkrj::{direct_repr, Storable, UnsizedStorable}; use super::BlockPath; diff --git a/src/structs/txout_index.rs b/src/structs/txout_index.rs index 6da7f588d..ebb36775f 100644 --- a/src/structs/txout_index.rs +++ b/src/structs/txout_index.rs @@ -1,6 +1,6 @@ use allocative::Allocative; use bincode::{Decode, Encode}; -use sanakirja::{direct_repr, Storable, UnsizedStorable}; +use snkrj::{direct_repr, Storable, UnsizedStorable}; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Encode, Decode, Allocative)] pub struct TxoutIndex { From 0b3329ca35fb167223ce328c46f5a5503d99e391 Mon Sep 17 00:00:00 2001 From: nym21 Date: Sat, 14 Dec 2024 14:56:55 +0100 Subject: [PATCH 06/63] git: fix ignore file --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index da713f859..1c9f9abdc 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,4 @@ docker/kibo paths.d.ts # Git -.git* +.git-* From e0a378cb81cde69c503f6dd442567acf8e0679d0 Mon Sep 17 00:00:00 2001 From: nym21 Date: Sat, 14 Dec 2024 17:39:42 +0100 Subject: [PATCH 07/63] biter: fix ? --- .gitignore | 6 -- Cargo.lock | 4 +- Cargo.toml | 6 +- README.md | 70 ++++-------------- crates/snkrj/Cargo.toml | 12 ---- {crates => src/crates}/biter/CHANGELOG.md | 0 {crates => src/crates}/biter/Cargo.lock | 0 {crates => src/crates}/biter/Cargo.toml | 4 +- {crates => src/crates}/biter/LICENSE.md | 0 {crates => src/crates}/biter/README.md | 0 .../biter/src/blk_index_to_blk_recap.rs | 14 ++-- .../crates}/biter/src/blk_metadata.rs | 0 .../biter/src/blk_metadata_and_block.rs | 0 {crates => src/crates}/biter/src/blk_recap.rs | 0 {crates => src/crates}/biter/src/lib.rs | 0 {crates => src/crates}/biter/src/main.rs | 4 +- {crates => src/crates}/biter/src/utils.rs | 0 {crates => src/crates}/iterable/Cargo.lock | 0 {crates => src/crates}/iterable/Cargo.toml | 0 {crates => src/crates}/iterable/README.md | 0 {crates => src/crates}/iterable/src/lib.rs | 0 .../struct_iterable_derive/Cargo.toml | 0 .../iterable/struct_iterable_derive/README.md | 0 .../struct_iterable_derive/src/lib.rs | 0 .../struct_iterable_internal/Cargo.toml | 0 .../struct_iterable_internal/README.md | 0 .../struct_iterable_internal/src/lib.rs | 0 {crates => src/crates}/snkrj/Cargo.lock | 0 src/crates/snkrj/Cargo.toml | 12 ++++ {crates => src/crates}/snkrj/LICENSE.md | 0 {crates => src/crates}/snkrj/README.md | 0 {crates => src/crates}/snkrj/src/lib.rs | 0 {crates => src/crates}/snkrj/src/main.rs | 0 src/parser/mod.rs | 2 +- src/structs/config.rs | 71 +++++++++++++------ src/utils/rpc.rs | 7 +- 36 files changed, 96 insertions(+), 116 deletions(-) delete mode 100644 crates/snkrj/Cargo.toml rename {crates => src/crates}/biter/CHANGELOG.md (100%) rename {crates => src/crates}/biter/Cargo.lock (100%) rename {crates => src/crates}/biter/Cargo.toml (85%) rename {crates => src/crates}/biter/LICENSE.md (100%) rename {crates => src/crates}/biter/README.md (100%) rename {crates => src/crates}/biter/src/blk_index_to_blk_recap.rs (90%) rename {crates => src/crates}/biter/src/blk_metadata.rs (100%) rename {crates => src/crates}/biter/src/blk_metadata_and_block.rs (100%) rename {crates => src/crates}/biter/src/blk_recap.rs (100%) rename {crates => src/crates}/biter/src/lib.rs (100%) rename {crates => src/crates}/biter/src/main.rs (89%) rename {crates => src/crates}/biter/src/utils.rs (100%) rename {crates => src/crates}/iterable/Cargo.lock (100%) rename {crates => src/crates}/iterable/Cargo.toml (100%) rename {crates => src/crates}/iterable/README.md (100%) rename {crates => src/crates}/iterable/src/lib.rs (100%) rename {crates => src/crates}/iterable/struct_iterable_derive/Cargo.toml (100%) rename {crates => src/crates}/iterable/struct_iterable_derive/README.md (100%) rename {crates => src/crates}/iterable/struct_iterable_derive/src/lib.rs (100%) rename {crates => src/crates}/iterable/struct_iterable_internal/Cargo.toml (100%) rename {crates => src/crates}/iterable/struct_iterable_internal/README.md (100%) rename {crates => src/crates}/iterable/struct_iterable_internal/src/lib.rs (100%) rename {crates => src/crates}/snkrj/Cargo.lock (100%) create mode 100644 src/crates/snkrj/Cargo.toml rename {crates => src/crates}/snkrj/LICENSE.md (100%) rename {crates => src/crates}/snkrj/README.md (100%) rename {crates => src/crates}/snkrj/src/lib.rs (100%) rename {crates => src/crates}/snkrj/src/main.rs (100%) diff --git a/.gitignore b/.gitignore index 1c9f9abdc..7df94636e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,6 @@ # Builds target -# Sync -.stfolder - # Copies *\ copy* @@ -32,6 +29,3 @@ docker/kibo # Types paths.d.ts - -# Git -.git-* diff --git a/Cargo.lock b/Cargo.lock index e72dc3740..4122381e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,7 +481,7 @@ dependencies = [ [[package]] name = "biter" -version = "0.2.1" +version = "0.2.2" dependencies = [ "bitcoin", "bitcoincore-rpc", @@ -2623,7 +2623,7 @@ dependencies = [ [[package]] name = "snkrj" -version = "0.1.0" +version = "0.1.1" dependencies = [ "sanakirja", ] diff --git a/Cargo.toml b/Cargo.toml index 19d50bc1f..b35e6c734 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ bincode = { git = "https://github.com/bincode-org/bincode.git", features = [ "serde", ] } bitcoin_hashes = { version = "0.15.0" } -biter = { path = "./crates/biter" } +biter = { path = "./src/crates/biter" } chrono = { version = "0.4.39", features = ["serde"] } clap = { version = "4.5.23", features = ["derive"] } color-eyre = "0.6.3" @@ -27,10 +27,10 @@ rayon = "1.10.0" regex = "1.11.1" reqwest = { version = "0.12.9", features = ["blocking", "json"] } rlimit = "0.10.2" -snkrj = { path = "./crates/snkrj" } +snkrj = { path = "./src/crates/snkrj" } serde = { version = "1.0.216", features = ["derive"] } serde_json = "1.0.133" -struct_iterable = { path = "./crates/iterable" } +struct_iterable = { path = "./src/crates/iterable" } swc = "9.0.0" swc_common = "5.0.0" tokio = { version = "1.42.0", features = ["full"] } diff --git a/README.md b/README.md index d602d55d8..d364690a2 100644 --- a/README.md +++ b/README.md @@ -63,17 +63,19 @@ Please open an issue if you want to add another instance ### Requirements - At least 16 GB of RAM -- 1 TB of free space (will use 70% of that without defragmentation and 40% after) + - Recommended: 32 GB +- A disk with 1 TB of free space (will use between 40% to 80% depending on several things) + - Recommended: Rated at 3 GB/s (Thunderbolt 4 speed) - A running instance of bitcoin-core with: - `-txindex=1` - `-blocksxor=0` - RPC credentials - Example: `bitcoind -datadir="$HOME/.bitcoin" -blocksonly -txindex=1 -blocksxor=0` - Git +- Unix based operating system (Mac OS or Linux) + - Ubuntu users need to install `open-ssl` via `sudo apt install libssl-dev pkg-config` -### Manual - -_Mac OS and Linux only, Windows is unsupported_ +### Build First we need to install Rust (https://www.rust-lang.org/tools/install) @@ -81,75 +83,33 @@ First we need to install Rust (https://www.rust-lang.org/tools/install) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` -If you already had Rust installed you could update it just in case +If you already had Rust installed you could update it ```bash rustup update ``` -> If you're on Ubuntu you'll probably also need to install `open-ssl` with -> -> ```bash -> sudo apt install libssl-dev pkg-config -> ``` - -Optionally, you can also install `cargo-watch` for the server to automatically restart it on file change, which will be triggered by new code and new datasets from the parser (https://github.com/watchexec/cargo-watch?tab=readme-ov-file#install) - -```bash -cargo install cargo-watch --locked -``` - -Then you need to choose a path where all files related to **kibō** will live +Then you need to choose a path where the project will reside and then clone it ```bash cd ??? -``` - -We can now clone the repository - -```bash git clone https://github.com/kibo-money/kibo.git +cd kibo ``` -In a new terminal, go to the `parser`'s folder of the repository +If it's your first time running kibo, it will need several information such as: -```bash -cd ???/kibo/parser -``` +- `--bitcoindir PATH`: path to bitcoin core data directory, `???/bitcoin` +- `--kibodir PATH`: path to kibo outputs, if you have enough space on your main disk `~/.kibo` is fine -Now we can finally start by running the parser, you need to use the `./run.sh` script instead of `cargo run -r` as we need to set various system variables for the program to run smoothly +Everything will be saved at `~/.kibo/config.toml`, which will allow you to simply run `cargo run -r` next time -For the first launch, the parser will need several information such as: - -- `--datadir`: which is bitcoin data directory path, prefer `$HOME` to `~` as the latter might not work -- `--outdir`: where all outputs will be saved, prefer `$HOME` to `~` as the latter might not work - -Optionally you can also specify: - -- `--rpccookiefile`: the path to the cookie file if not default -- `--rpcuser`: the username of the RPC credentials to talk to the bitcoin server if set -- `--rpcpassword`: the password of the RPC credentials if set -- `--rpcconnect`: if the bitcoin core server's IP is different than `localhost` -- `--rpcport`: if the port is different than `8332` - -Everything will be saved in a `config.toml` file, which will allow you to simply run `./run.sh` next time +If you need more options please run `cargo run -r --help` to see what parameters are available. Here's an example ```bash -./run.sh --datadir=$HOME/Developer/bitcoin --outdir=$HOME/.kibo/out -``` - -In a **new** terminal, go to the `server`'s folder of the repository - -```bash -cd ???/kibo/server -``` - -And start it also with the `run.sh` script instead of `cargo run -r` - -```bash -./run.sh +cargo run -r -- --bitcoindir=~/Developer/bitcoin --kibodir=~/.kibo ``` Then the easiest to let others access your server is to use `cloudflared` which will also cache requests. For more information go to: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/ diff --git a/crates/snkrj/Cargo.toml b/crates/snkrj/Cargo.toml deleted file mode 100644 index 864b234b6..000000000 --- a/crates/snkrj/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "snkrj" -description = "A simple wrapper around Sanakirja aatabase that acts as a very fast on disk BTreeMap" -version = "0.1.0" -license = "MIT" -repository = "https://github.com/kibo-money/kibo/tree/main/crates/snkrj" -keywords = ["database", "sanakirja", "btreemap"] -categories = ["database"] -edition = "2021" - -[dependencies] -sanakirja = "1.4.3" diff --git a/crates/biter/CHANGELOG.md b/src/crates/biter/CHANGELOG.md similarity index 100% rename from crates/biter/CHANGELOG.md rename to src/crates/biter/CHANGELOG.md diff --git a/crates/biter/Cargo.lock b/src/crates/biter/Cargo.lock similarity index 100% rename from crates/biter/Cargo.lock rename to src/crates/biter/Cargo.lock diff --git a/crates/biter/Cargo.toml b/src/crates/biter/Cargo.toml similarity index 85% rename from crates/biter/Cargo.toml rename to src/crates/biter/Cargo.toml index fb39c739d..e9ad3f63a 100644 --- a/crates/biter/Cargo.toml +++ b/src/crates/biter/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "biter" description = "A very fast Bitcoin block iterator" -version = "0.2.1" +version = "0.2.2" license = "MIT" -repository = "https://github.com/kibo-money/kibo/tree/main/crates/biter" +repository = "https://github.com/kibo-money/kibo/tree/main/src/crates/biter" keywords = ["bitcoin", "block", "iterator"] categories = ["cryptography::cryptocurrencies", "encoding"] edition = "2021" diff --git a/crates/biter/LICENSE.md b/src/crates/biter/LICENSE.md similarity index 100% rename from crates/biter/LICENSE.md rename to src/crates/biter/LICENSE.md diff --git a/crates/biter/README.md b/src/crates/biter/README.md similarity index 100% rename from crates/biter/README.md rename to src/crates/biter/README.md diff --git a/crates/biter/src/blk_index_to_blk_recap.rs b/src/crates/biter/src/blk_index_to_blk_recap.rs similarity index 90% rename from crates/biter/src/blk_index_to_blk_recap.rs rename to src/crates/biter/src/blk_index_to_blk_recap.rs index c3823447c..b0353261f 100644 --- a/crates/biter/src/blk_index_to_blk_recap.rs +++ b/src/crates/biter/src/blk_index_to_blk_recap.rs @@ -17,7 +17,7 @@ pub struct BlkIndexToBlkRecap { path: PathBuf, #[target] tree: BTreeMap, - last_safe_recap: Option, + last_safe_height: Option, } impl BlkIndexToBlkRecap { @@ -38,7 +38,7 @@ impl BlkIndexToBlkRecap { let mut this = Self { path, tree, - last_safe_recap: None, + last_safe_height: None, }; this.clean_outdated(blocks_dir); @@ -58,11 +58,11 @@ impl BlkIndexToBlkRecap { } }); - unprocessed_keys.iter().for_each(|blk_index| { - self.remove(blk_index); + unprocessed_keys.into_iter().for_each(|blk_index| { + self.remove(&blk_index); }); - self.last_safe_recap = self.last_entry().map(|e| e.get().clone()); + self.last_safe_height = self.iter().map(|(_, recap)| recap.height()).max(); } pub fn get_start_recap(&self, start: Option) -> Option<(usize, BlkRecap)> { @@ -108,8 +108,8 @@ impl BlkIndexToBlkRecap { } if self - .last_safe_recap - .map_or(true, |recap| recap.height() >= height) + .last_safe_height + .map_or(true, |safe_height| height >= safe_height) && (height % TARGET_BLOCKS_PER_MONTH) == 0 { self.export(); diff --git a/crates/biter/src/blk_metadata.rs b/src/crates/biter/src/blk_metadata.rs similarity index 100% rename from crates/biter/src/blk_metadata.rs rename to src/crates/biter/src/blk_metadata.rs diff --git a/crates/biter/src/blk_metadata_and_block.rs b/src/crates/biter/src/blk_metadata_and_block.rs similarity index 100% rename from crates/biter/src/blk_metadata_and_block.rs rename to src/crates/biter/src/blk_metadata_and_block.rs diff --git a/crates/biter/src/blk_recap.rs b/src/crates/biter/src/blk_recap.rs similarity index 100% rename from crates/biter/src/blk_recap.rs rename to src/crates/biter/src/blk_recap.rs diff --git a/crates/biter/src/lib.rs b/src/crates/biter/src/lib.rs similarity index 100% rename from crates/biter/src/lib.rs rename to src/crates/biter/src/lib.rs diff --git a/crates/biter/src/main.rs b/src/crates/biter/src/main.rs similarity index 89% rename from crates/biter/src/main.rs rename to src/crates/biter/src/main.rs index ed1da58cb..a8bea0066 100644 --- a/crates/biter/src/main.rs +++ b/src/crates/biter/src/main.rs @@ -11,8 +11,8 @@ fn main() { let auth = Auth::CookieFile(cookie); let rpc = Client::new(url, auth).unwrap(); - let start = Some(800_000); - let end = Some(855_000); + let start = Some(810078); + let end = None; biter::new(data_dir, start, end, rpc) .iter() diff --git a/crates/biter/src/utils.rs b/src/crates/biter/src/utils.rs similarity index 100% rename from crates/biter/src/utils.rs rename to src/crates/biter/src/utils.rs diff --git a/crates/iterable/Cargo.lock b/src/crates/iterable/Cargo.lock similarity index 100% rename from crates/iterable/Cargo.lock rename to src/crates/iterable/Cargo.lock diff --git a/crates/iterable/Cargo.toml b/src/crates/iterable/Cargo.toml similarity index 100% rename from crates/iterable/Cargo.toml rename to src/crates/iterable/Cargo.toml diff --git a/crates/iterable/README.md b/src/crates/iterable/README.md similarity index 100% rename from crates/iterable/README.md rename to src/crates/iterable/README.md diff --git a/crates/iterable/src/lib.rs b/src/crates/iterable/src/lib.rs similarity index 100% rename from crates/iterable/src/lib.rs rename to src/crates/iterable/src/lib.rs diff --git a/crates/iterable/struct_iterable_derive/Cargo.toml b/src/crates/iterable/struct_iterable_derive/Cargo.toml similarity index 100% rename from crates/iterable/struct_iterable_derive/Cargo.toml rename to src/crates/iterable/struct_iterable_derive/Cargo.toml diff --git a/crates/iterable/struct_iterable_derive/README.md b/src/crates/iterable/struct_iterable_derive/README.md similarity index 100% rename from crates/iterable/struct_iterable_derive/README.md rename to src/crates/iterable/struct_iterable_derive/README.md diff --git a/crates/iterable/struct_iterable_derive/src/lib.rs b/src/crates/iterable/struct_iterable_derive/src/lib.rs similarity index 100% rename from crates/iterable/struct_iterable_derive/src/lib.rs rename to src/crates/iterable/struct_iterable_derive/src/lib.rs diff --git a/crates/iterable/struct_iterable_internal/Cargo.toml b/src/crates/iterable/struct_iterable_internal/Cargo.toml similarity index 100% rename from crates/iterable/struct_iterable_internal/Cargo.toml rename to src/crates/iterable/struct_iterable_internal/Cargo.toml diff --git a/crates/iterable/struct_iterable_internal/README.md b/src/crates/iterable/struct_iterable_internal/README.md similarity index 100% rename from crates/iterable/struct_iterable_internal/README.md rename to src/crates/iterable/struct_iterable_internal/README.md diff --git a/crates/iterable/struct_iterable_internal/src/lib.rs b/src/crates/iterable/struct_iterable_internal/src/lib.rs similarity index 100% rename from crates/iterable/struct_iterable_internal/src/lib.rs rename to src/crates/iterable/struct_iterable_internal/src/lib.rs diff --git a/crates/snkrj/Cargo.lock b/src/crates/snkrj/Cargo.lock similarity index 100% rename from crates/snkrj/Cargo.lock rename to src/crates/snkrj/Cargo.lock diff --git a/src/crates/snkrj/Cargo.toml b/src/crates/snkrj/Cargo.toml new file mode 100644 index 000000000..2341d6537 --- /dev/null +++ b/src/crates/snkrj/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "snkrj" +description = "A simple wrapper around Sanakirja's database that acts as a very fast on disk BTreeMap" +version = "0.1.1" +license = "MIT" +repository = "https://github.com/kibo-money/kibo/tree/main/src/crates/snkrj" +keywords = ["database", "sanakirja", "btreemap"] +categories = ["database"] +edition = "2021" + +[dependencies] +sanakirja = "1.4.3" diff --git a/crates/snkrj/LICENSE.md b/src/crates/snkrj/LICENSE.md similarity index 100% rename from crates/snkrj/LICENSE.md rename to src/crates/snkrj/LICENSE.md diff --git a/crates/snkrj/README.md b/src/crates/snkrj/README.md similarity index 100% rename from crates/snkrj/README.md rename to src/crates/snkrj/README.md diff --git a/crates/snkrj/src/lib.rs b/src/crates/snkrj/src/lib.rs similarity index 100% rename from crates/snkrj/src/lib.rs rename to src/crates/snkrj/src/lib.rs diff --git a/crates/snkrj/src/main.rs b/src/crates/snkrj/src/main.rs similarity index 100% rename from crates/snkrj/src/main.rs rename to src/crates/snkrj/src/main.rs diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 21acaa3ce..b720f435c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -37,7 +37,7 @@ pub fn main( &mut datasets, )?; - if let Some(delay) = config.delay { + if let Some(delay) = config.delay() { sleep(Duration::from_secs(delay)) } diff --git a/src/structs/config.rs b/src/structs/config.rs index 9ad22a1c0..ed5acbedc 100644 --- a/src/structs/config.rs +++ b/src/structs/config.rs @@ -15,36 +15,36 @@ use super::MapPath; #[command(version, about, long_about = None)] pub struct Config { /// Bitcoin data directory path, saved - #[arg(long, value_name = "DIR")] + #[arg(long, value_name = "PATH")] bitcoindir: Option, - /// Kibo data directory path, saved - #[arg(long, value_name = "DIR")] + /// Kibo output directory path, saved + #[arg(long, value_name = "PATH")] kibodir: Option, /// Bitcoin RPC ip, default: localhost, saved #[arg(long, value_name = "IP")] - pub rpcconnect: Option, + rpcconnect: Option, /// Bitcoin RPC port, default: 8332, saved #[arg(long, value_name = "PORT")] - pub rpcport: Option, + rpcport: Option, /// Bitcoin RPC cookie file, default: --bitcoindir/.cookie, saved #[arg(long, value_name = "PATH")] - pub rpccookiefile: Option, + rpccookiefile: Option, /// Bitcoin RPC username, saved #[arg(long, value_name = "USERNAME")] - pub rpcuser: Option, + rpcuser: Option, /// Bitcoin RPC password, saved #[arg(long, value_name = "PASSWORD")] - pub rpcpassword: Option, + rpcpassword: Option, /// Delay between runs, default: 0, saved #[arg(long, value_name = "SECONDS")] - pub delay: Option, + delay: Option, // Maximum ram you want the program to use in GB, default: 50% of total, not saved // #[arg(long, value_name = "GB")] @@ -181,7 +181,7 @@ impl Config { std::process::exit(1); } - let path = Path::new(self.bitcoindir.as_ref().unwrap()); + let path = self.path_bitcoindir(); if !path.is_dir() { println!("Expect path '{:#?}' to be a directory.", path); std::process::exit(1); @@ -206,18 +206,10 @@ impl Config { } pub fn to_rpc_auth(&self) -> color_eyre::Result { - let cookie = Path::new(self.bitcoindir.as_ref().unwrap()).join(".cookie"); + let cookie = self.path_cookiefile(); if cookie.is_file() { Ok(Auth::CookieFile(cookie)) - } else if self - .rpccookiefile - .as_ref() - .is_some_and(|cookie| Path::new(cookie).is_file()) - { - Ok(Auth::CookieFile(PathBuf::from( - self.rpccookiefile.as_ref().unwrap(), - ))) } else if self.rpcuser.is_some() && self.rpcpassword.is_some() { Ok(Auth::UserPass( self.rpcuser.clone().unwrap(), @@ -228,6 +220,18 @@ impl Config { } } + pub fn rpcconnect(&self) -> Option<&String> { + self.rpcconnect.as_ref() + } + + pub fn rpcport(&self) -> Option { + self.rpcport + } + + pub fn delay(&self) -> Option { + self.delay + } + pub fn dry_run(&self) -> bool { self.dry_run.is_some_and(|b| b) } @@ -241,11 +245,36 @@ impl Config { } pub fn path_bitcoindir(&self) -> PathBuf { - PathBuf::from(self.bitcoindir.as_ref().unwrap()) + Self::fix_user_path(self.bitcoindir.as_ref().unwrap().as_ref()) } fn path_kibodir(&self) -> PathBuf { - PathBuf::from(self.kibodir.as_ref().unwrap()) + Self::fix_user_path(self.kibodir.as_ref().unwrap().as_ref()) + } + + fn path_cookiefile(&self) -> PathBuf { + self.rpccookiefile.as_ref().map_or_else( + || self.path_bitcoindir().join(".cookie"), + |p| Self::fix_user_path(p.as_str()), + ) + } + + fn fix_user_path(path: &str) -> PathBuf { + let fix = move |pattern: &str| { + if path.starts_with(pattern) { + let path = &path + .replace(&format!("{pattern}/"), "") + .replace(pattern, ""); + + let home = std::env::var("HOME").unwrap(); + + Some(Path::new(&home).join(path)) + } else { + None + } + }; + + fix("~").unwrap_or_else(|| fix("$HOME").unwrap_or_else(|| PathBuf::from(&path))) } pub fn path_datasets(&self) -> MapPath { diff --git a/src/utils/rpc.rs b/src/utils/rpc.rs index a25275d27..398989639 100644 --- a/src/utils/rpc.rs +++ b/src/utils/rpc.rs @@ -12,11 +12,8 @@ fn create_rpc(config: &Config) -> color_eyre::Result { Ok(Client::new( &format!( "http://{}:{}", - config - .rpcconnect - .as_ref() - .unwrap_or(&"localhost".to_owned()), - config.rpcport.unwrap_or(8332) + config.rpcconnect().unwrap_or(&"localhost".to_owned()), + config.rpcport().unwrap_or(8332) ), config.to_rpc_auth().unwrap(), )?) From 8fabbde13b3dc887f27fc32229e1f30be09f2e6f Mon Sep 17 00:00:00 2001 From: nym21 Date: Tue, 17 Dec 2024 10:39:28 +0100 Subject: [PATCH 08/63] global: small fixes --- CHANGELOG.md | 13 +++- src/crates/biter/Cargo.lock | 2 +- src/crates/snkrj/Cargo.lock | 2 +- src/parser/actions/export.rs | 34 +++++------ src/parser/actions/iter_blocks.rs | 26 +++----- src/parser/actions/min_height.rs | 2 + .../address_index_to_address_data.rs | 31 +++++----- .../datasets/_traits/min_initial_state.rs | 7 --- src/parser/states/_trait.rs | 5 +- src/parser/states/mod.rs | 4 +- src/server/api/structs/routes.rs | 32 +++++++--- src/structs/bi_map.rs | 2 +- src/structs/config.rs | 61 ++++++++++--------- src/structs/date.rs | 4 +- src/structs/instant.rs | 15 +++++ src/structs/mod.rs | 4 ++ src/structs/rpc.rs | 17 ++++++ src/utils/mod.rs | 1 - src/utils/rpc.rs | 20 ------ src/utils/time.rs | 6 +- src/website/index.html | 1 + src/website/scripts/main.js | 8 +-- src/website/scripts/options.js | 52 ++++++---------- 23 files changed, 179 insertions(+), 170 deletions(-) create mode 100644 src/structs/instant.rs create mode 100644 src/structs/rpc.rs delete mode 100644 src/utils/rpc.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 339cc3a3e..8c15669ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,14 +5,21 @@ # v0.6.0 | WIP -- Merged parser and server crates into a single project (and thus executable) +## Global + +- Merged parser and server crates into a single project (and thus executable), so now both will run at the same time with a single `cargo run -r` +- Added `--no-server` and `--no-parser` to disable each if needed +- Improved executable parameters - Started using `log` and `env_logger` crates instead of custom code - Improved logs -- Added `--server BOOL` and `--parser BOOL` parameters (both are true by default) -- Automated databases defragmention (and removed parameter) +- Automated databases defragmention (and removed parameter) to improve disk usage and parse speed - Fixed input being unfocused right after being focused in Brave browser - Moved Sanakirja database wrapper to its own crate: `snkrj` +## Git + +Added git tags for each version tough Markdown won't display formatted on Github so left the default text + # [v0.5.0](https://github.com/kibo-money/kibo/tree/eea56d394bf92c62c81da8b78b8c47ea730683f5) | [873199](https://mempool.space/block/0000000000000000000270925aa6a565be92e13164565a3f7994ca1966e48050) - 2024/12/04 ![Image of the kibō Web App version 0.5.0](https://github.com/kibo-money/kibo/blob/main/assets/v0.5.0.jpg) diff --git a/src/crates/biter/Cargo.lock b/src/crates/biter/Cargo.lock index b71079ba4..0de88bd25 100644 --- a/src/crates/biter/Cargo.lock +++ b/src/crates/biter/Cargo.lock @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "biter" -version = "0.2.1" +version = "0.2.2" dependencies = [ "bitcoin", "bitcoincore-rpc", diff --git a/src/crates/snkrj/Cargo.lock b/src/crates/snkrj/Cargo.lock index 458967873..7939c3538 100644 --- a/src/crates/snkrj/Cargo.lock +++ b/src/crates/snkrj/Cargo.lock @@ -177,7 +177,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snkrj" -version = "0.1.0" +version = "0.1.1" dependencies = [ "sanakirja", ] diff --git a/src/parser/actions/export.rs b/src/parser/actions/export.rs index f43d8e3be..314fb34c5 100644 --- a/src/parser/actions/export.rs +++ b/src/parser/actions/export.rs @@ -1,5 +1,3 @@ -use std::thread::{self}; - use log::info; use crate::{ @@ -38,27 +36,23 @@ pub fn export( exit.block(); - info!("Exporting..."); - if defragment { - info!("Will also defragment databases, please be patient it might take a while") - } + let text = if defragment { + "export and defragmentation" + } else { + "export" + }; + info!("Starting {text}"); - time("Total save time", || -> color_eyre::Result<()> { - time("Datasets saved", || datasets.export(config))?; + time(&format!("Finished {text}"), || -> color_eyre::Result<()> { + datasets.export(config)?; - thread::scope(|s| { - if let Some(databases) = databases { - s.spawn(|| { - time("Databases saved", || { - databases.export(height, date, defragment) - }) - }); - } + if let Some(databases) = databases { + databases.export(height, date, defragment)?; + } - if let Some(states) = states { - s.spawn(|| time("States saved", || states.export(config))); - } - }); + if let Some(states) = states { + states.export(config)?; + } Ok(()) })?; diff --git a/src/parser/actions/iter_blocks.rs b/src/parser/actions/iter_blocks.rs index fcd41d254..1e1963fdc 100644 --- a/src/parser/actions/iter_blocks.rs +++ b/src/parser/actions/iter_blocks.rs @@ -15,7 +15,7 @@ use crate::{ datasets::{ComputeData, Datasets}, states::{AddressCohortsDurableStates, States, UTXOCohortsDurableStates}, }, - structs::{Config, DateData, Exit, Height, MapKey, Timestamp}, + structs::{Config, DateData, DisplayInstant, Exit, Height, MapKey, Timestamp}, utils::{generate_allocation_files, time}, }; @@ -52,8 +52,6 @@ pub fn iter_blocks( let mut block_iter = block_receiver.iter(); 'parsing: loop { - let instant = Instant::now(); - let mut processed_heights = BTreeSet::new(); let mut processed_dates = BTreeSet::new(); @@ -64,6 +62,8 @@ pub fn iter_blocks( blocks_loop_date.take(); } + let instant = Instant::now(); + 'blocks: loop { let current_block_opt = next_block_opt.take().or_else(|| block_iter.next()); @@ -88,8 +88,6 @@ pub fn iter_blocks( // Always run for the first block of the loop if blocks_loop_date.is_none() { - info!("Processing {current_block_date} (height: {height})..."); - blocks_loop_date.replace(current_block_date); if states @@ -164,15 +162,18 @@ pub fn iter_blocks( blocks_loop_i += 1; if is_date_last_block { + info!( + "Processed {current_block_date} ({height} - {current_block_height}) {}", + instant.display() + ); + height += blocks_loop_i; let is_check_point = next_date_opt .as_ref() .map_or(true, |date| date.is_first_of_month()); - let ran_for_at_least_a_minute = instant.elapsed().as_secs() >= 60; - - if (is_check_point && ran_for_at_least_a_minute) + if (is_check_point && instant.elapsed().as_secs() >= 2) || height.is_close_to_end(approx_block_count) { break 'days; @@ -189,11 +190,6 @@ pub fn iter_blocks( // Don't remember why -1 let last_height = height - 1_u32; - info!( - "Parsing group took {} seconds (last height: {last_height})", - instant.elapsed().as_secs_f32(), - ); - if first_unsafe_heights.computed <= last_height { info!("Computing datasets..."); time("Computing datasets", || { @@ -214,7 +210,7 @@ pub fn iter_blocks( let defragment = is_safe && next_date_opt.is_some_and(|date| { (date.year() >= 2020 && date.is_january() - || date.year() >= 2022 && date.is_june()) + || date.year() >= 2022 && date.is_july()) && date.is_first_of_month() }); @@ -237,8 +233,6 @@ pub fn iter_blocks( } else { info!("Skipping export"); } - - println!(); } Ok(()) diff --git a/src/parser/actions/min_height.rs b/src/parser/actions/min_height.rs index eb767c08b..7166bdbb4 100644 --- a/src/parser/actions/min_height.rs +++ b/src/parser/actions/min_height.rs @@ -123,6 +123,8 @@ pub fn find_first_inserted_unsafe_height( // panic!(""); // } + if true {panic!()} + states.reset(config, include_addresses); databases.reset(include_addresses); diff --git a/src/parser/databases/address_index_to_address_data.rs b/src/parser/databases/address_index_to_address_data.rs index a56137b7f..fdcb09205 100644 --- a/src/parser/databases/address_index_to_address_data.rs +++ b/src/parser/databases/address_index_to_address_data.rs @@ -13,7 +13,6 @@ use snkrj::{AnyDatabase, Database as _Database}; use crate::{ parser::states::AddressCohortsDurableStates, structs::{AddressData, Config}, - utils::time, }; use super::{AnyDatabaseGroup, Metadata}; @@ -91,24 +90,24 @@ impl AddressIndexToAddressData { } pub fn compute_addres_cohorts_durable_states(&mut self) -> AddressCohortsDurableStates { - time("Iter through address_index_to_address_data", || { - self.open_all(); + // time("Iter through address_index_to_address_data", || { + self.open_all(); - // MUST CLEAR MAP, otherwise some weird things are happening later in the export I think - mem::take(&mut self.map) - .par_iter() - .map(|(_, database)| { - let mut s = AddressCohortsDurableStates::default(); + // MUST CLEAR MAP, otherwise some weird things are happening later in the export I think + mem::take(&mut self.map) + .par_iter() + .map(|(_, database)| { + let mut s = AddressCohortsDurableStates::default(); - database - .iter_disk() - .map(|r| r.unwrap().1) - .for_each(|address_data| s.increment(address_data).unwrap()); + database + .iter_disk() + .map(|r| r.unwrap().1) + .for_each(|address_data| s.increment(address_data).unwrap()); - s - }) - .sum() - }) + s + }) + .sum() + // }) } fn db_index(key: &Key) -> usize { diff --git a/src/parser/datasets/_traits/min_initial_state.rs b/src/parser/datasets/_traits/min_initial_state.rs index db747aea8..2fdfad411 100644 --- a/src/parser/datasets/_traits/min_initial_state.rs +++ b/src/parser/datasets/_traits/min_initial_state.rs @@ -45,13 +45,6 @@ enum Mode { } impl MinInitialState { - // pub fn consume(&mut self, other: Self) { - // self.first_unsafe_date = other.first_unsafe_date; - // self.first_unsafe_height = other.first_unsafe_height; - // self.last_date = other.last_date; - // self.last_height = other.last_height; - // } - fn compute_from_datasets(datasets: &dyn AnyDatasets, mode: Mode, config: &Config) -> Self { match mode { Mode::Inserted => { diff --git a/src/parser/states/_trait.rs b/src/parser/states/_trait.rs index b8b400cc3..c07277890 100644 --- a/src/parser/states/_trait.rs +++ b/src/parser/states/_trait.rs @@ -9,7 +9,6 @@ use serde::{de::DeserializeOwned, Serialize}; use crate::{io::Serialization, structs::Config}; -// https://github.com/djkoloski/rust_serialization_benchmark pub trait AnyState where Self: Debug + Encode + Decode + Serialize + DeserializeOwned, @@ -26,9 +25,7 @@ where } fn import(config: &Config) -> color_eyre::Result { - let path = Self::path(config); - fs::create_dir_all(&path)?; - Serialization::Binary.import(&path) + Serialization::Binary.import(&Self::path(config)) } fn export(&self, config: &Config) -> color_eyre::Result<()> { diff --git a/src/parser/states/mod.rs b/src/parser/states/mod.rs index f67cec892..1558b0005 100644 --- a/src/parser/states/mod.rs +++ b/src/parser/states/mod.rs @@ -1,4 +1,4 @@ -use std::thread; +use std::{fs, thread}; mod _trait; mod cohorts_states; @@ -25,6 +25,8 @@ pub struct States { impl States { pub fn import(config: &Config) -> color_eyre::Result { + fs::create_dir_all(config.path_states())?; + let date_data_vec = DateDataVec::import(config)?; let address_counters = Counters::import(config)?; diff --git a/src/server/api/structs/routes.rs b/src/server/api/structs/routes.rs index 8a5caf4bc..0afbd3b0c 100644 --- a/src/server/api/structs/routes.rs +++ b/src/server/api/structs/routes.rs @@ -1,5 +1,6 @@ use std::{ collections::{BTreeMap, HashMap}, + fs, path::PathBuf, }; @@ -31,17 +32,30 @@ impl Routes { pub fn build(paths_to_type: BTreeMap, config: &Config) -> Self { let mut routes = Routes::default(); - paths_to_type.into_iter().for_each(|(file_path, value)| { - let serialization = - Serialization::try_from(&file_path).unwrap_or(Serialization::Binary); + paths_to_type.into_iter().for_each(|(path, value)| { + let try_from_path = if path.is_file() { + path.clone() + } else { + fs::read_dir(&path) + .unwrap_or_else(|_| { + dbg!(&path); + panic!(); + }) + .map(|e| e.unwrap().path()) + .find(|e| e.is_file()) + .unwrap() + }; - let file_path_ser = file_path.to_str().unwrap().to_owned(); + let serialization = + Serialization::try_from(&try_from_path).unwrap_or(Serialization::Binary); + + let file_path_ser = path.to_str().unwrap().to_owned(); let split_key = file_path_ser.replace( &format!("{}/", config.path_datasets().to_str().unwrap()), "", ); let split_key = - split_key.replace(&format!("{}/", config.path_price().to_str().unwrap()), ""); + split_key.replace(&format!("{}/", config.path_kibodir().to_str().unwrap()), ""); let mut split_key = split_key.split('/').collect_vec(); let last = split_key.pop().unwrap().to_owned(); let last = last.split('.').next().unwrap(); @@ -63,7 +77,7 @@ impl Routes { map_key, Route { url_path: format!("date-to-{url_path}"), - file_path, + file_path: path, values_type, serialization, }, @@ -74,7 +88,7 @@ impl Routes { map_key, Route { url_path: format!("height-to-{url_path}"), - file_path, + file_path: path, values_type, serialization, }, @@ -85,14 +99,14 @@ impl Routes { map_key, Route { url_path, - file_path, + file_path: path, values_type, serialization, }, ); } _ => { - dbg!(&file_path, value, &last, &split_key); + dbg!(&path, value, &last, &split_key); panic!("") } } diff --git a/src/structs/bi_map.rs b/src/structs/bi_map.rs index 87030f5b9..733c06846 100644 --- a/src/structs/bi_map.rs +++ b/src/structs/bi_map.rs @@ -11,7 +11,7 @@ use super::{ AnyDateMap, AnyHeightMap, AnyMap, Date, DateMap, Height, HeightMap, MapKind, MapPath, MapValue, }; -#[derive(Allocative)] +#[derive(Allocative, Debug)] pub struct BiMap where Value: MapValue, diff --git a/src/structs/config.rs b/src/structs/config.rs index ed5acbedc..f10e3919c 100644 --- a/src/structs/config.rs +++ b/src/structs/config.rs @@ -1,5 +1,6 @@ use std::{ fs::{self}, + mem, path::{Path, PathBuf}, }; @@ -46,28 +47,30 @@ pub struct Config { #[arg(long, value_name = "SECONDS")] delay: Option, - // Maximum ram you want the program to use in GB, default: 50% of total, not saved - // #[arg(long, value_name = "GB")] - // pub max_ram: Option, - /// Enable or disable the parser, default: true, not saved - #[arg(long, value_name = "BOOL")] - parser: Option, + /// Disable the parser, not saved + #[serde(default)] + #[arg(long, default_value_t = false)] + no_parser: bool, - /// Enable or disable the server, default: true, not saved - #[arg(long, value_name = "BOOL")] - server: Option, + /// Disable the server, not saved + #[serde(default)] + #[arg(long, default_value_t = false)] + no_server: bool, - /// Start a dry run, default: true, not saved - #[arg(long, value_name = "BOOL")] - dry_run: Option, + /// Run without saving, not saved + #[serde(default)] + #[arg(long, default_value_t = false)] + dry_run: bool, - /// Record ram usage, default: false, not saved - #[arg(long, value_name = "BOOL")] - record_ram_usage: Option, + /// Record ram usage, not saved + #[serde(default)] + #[arg(long, default_value_t = false)] + record_ram_usage: bool, - /// Recompute all computed datasets, default: false, not saved - #[arg(long, value_name = "BOOL")] - recompute_computed: Option, + /// Recompute all computed datasets, not saved + #[serde(default)] + #[arg(long, default_value_t = false)] + recompute_computed: bool, } impl Config { @@ -125,11 +128,11 @@ impl Config { config.write(&path)?; - config.parser = config_args.parser.take(); - config.server = config_args.server.take(); - config.dry_run = config_args.dry_run.take(); - config.record_ram_usage = config_args.record_ram_usage.take(); - config.recompute_computed = config_args.recompute_computed.take(); + config.no_parser = mem::take(&mut config_args.no_parser); + config.no_server = mem::take(&mut config_args.no_server); + config.dry_run = mem::take(&mut config_args.dry_run); + config.record_ram_usage = mem::take(&mut config_args.record_ram_usage); + config.recompute_computed = mem::take(&mut config_args.recompute_computed); info!("Configuration {{"); info!(" bitcoindir: {:?}", config.bitcoindir); @@ -233,22 +236,22 @@ impl Config { } pub fn dry_run(&self) -> bool { - self.dry_run.is_some_and(|b| b) + self.dry_run } pub fn record_ram_usage(&self) -> bool { - self.record_ram_usage.is_some_and(|b| b) + self.record_ram_usage } pub fn recompute_computed(&self) -> bool { - self.recompute_computed.is_some_and(|b| b) + self.recompute_computed } pub fn path_bitcoindir(&self) -> PathBuf { Self::fix_user_path(self.bitcoindir.as_ref().unwrap().as_ref()) } - fn path_kibodir(&self) -> PathBuf { + pub fn path_kibodir(&self) -> PathBuf { Self::fix_user_path(self.kibodir.as_ref().unwrap().as_ref()) } @@ -311,10 +314,10 @@ impl Config { } pub fn parser(&self) -> bool { - self.parser.is_none_or(|b| b) + !self.no_parser } pub fn server(&self) -> bool { - self.server.is_none_or(|b| b) + !self.no_server } } diff --git a/src/structs/date.rs b/src/structs/date.rs index da448f6bb..fd3f0f77f 100644 --- a/src/structs/date.rs +++ b/src/structs/date.rs @@ -74,8 +74,8 @@ impl Date { self.month() == 1 } - pub fn is_june(&self) -> bool { - self.month() == 6 + pub fn is_july(&self) -> bool { + self.month() == 7 } pub fn is_first_of_month(&self) -> bool { diff --git a/src/structs/instant.rs b/src/structs/instant.rs new file mode 100644 index 000000000..9495f71eb --- /dev/null +++ b/src/structs/instant.rs @@ -0,0 +1,15 @@ +use std::time::Instant; + +use color_eyre::owo_colors::OwoColorize; + +pub trait DisplayInstant { + fn display(&self) -> String; +} + +impl DisplayInstant for Instant { + fn display(&self) -> String { + format!("{:.2}s", self.elapsed().as_secs_f32()) + .bright_black() + .to_string() + } +} diff --git a/src/structs/mod.rs b/src/structs/mod.rs index f1974120b..0a7043ce3 100644 --- a/src/structs/mod.rs +++ b/src/structs/mod.rs @@ -24,12 +24,14 @@ mod generic_map; mod height; mod height_map; mod height_map_chunk_id; +mod instant; mod liquidity; mod map_path; mod map_value; mod ohlc; mod partial_txout_data; mod price; +mod rpc; mod sent_data; mod serialized_btreemap; mod serialized_vec; @@ -63,12 +65,14 @@ pub use generic_map::*; pub use height::*; pub use height_map::*; pub use height_map_chunk_id::*; +pub use instant::*; pub use liquidity::*; pub use map_path::*; pub use map_value::*; pub use ohlc::*; pub use partial_txout_data::*; pub use price::*; +pub use rpc::*; pub use sent_data::*; pub use serialized_btreemap::*; pub use serialized_vec::*; diff --git a/src/structs/rpc.rs b/src/structs/rpc.rs new file mode 100644 index 000000000..f7aac8916 --- /dev/null +++ b/src/structs/rpc.rs @@ -0,0 +1,17 @@ +use biter::bitcoincore_rpc::Client; + +use crate::structs::Config; + +impl From<&Config> for Client { + fn from(config: &Config) -> Self { + Client::new( + &format!( + "http://{}:{}", + config.rpcconnect().unwrap_or(&"localhost".to_owned()), + config.rpcport().unwrap_or(8332) + ), + config.to_rpc_auth().unwrap(), + ) + .unwrap() + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 0ed6755e9..59742bfe6 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -4,7 +4,6 @@ mod log; mod lossy; mod percentile; mod retry; -mod rpc; mod time; pub use consts::*; diff --git a/src/utils/rpc.rs b/src/utils/rpc.rs deleted file mode 100644 index 398989639..000000000 --- a/src/utils/rpc.rs +++ /dev/null @@ -1,20 +0,0 @@ -use biter::bitcoincore_rpc::Client; - -use crate::structs::Config; - -impl From<&Config> for Client { - fn from(config: &Config) -> Self { - create_rpc(config).unwrap() - } -} - -fn create_rpc(config: &Config) -> color_eyre::Result { - Ok(Client::new( - &format!( - "http://{}:{}", - config.rpcconnect().unwrap_or(&"localhost".to_owned()), - config.rpcport().unwrap_or(8332) - ), - config.to_rpc_auth().unwrap(), - )?) -} diff --git a/src/utils/time.rs b/src/utils/time.rs index f0001216d..2c6df6103 100644 --- a/src/utils/time.rs +++ b/src/utils/time.rs @@ -2,7 +2,9 @@ use std::time::Instant; use log::info; -pub fn time(name: &str, function: F) -> T +use crate::structs::DisplayInstant; + +pub fn time(text: &str, function: F) -> T where F: FnOnce() -> T, { @@ -10,7 +12,7 @@ where let returned = function(); - info!("{name}: {} seconds", time.elapsed().as_secs_f32()); + info!("{text} {}", time.display()); returned } diff --git a/src/website/index.html b/src/website/index.html index 93166e466..a48b547c3 100644 --- a/src/website/index.html +++ b/src/website/index.html @@ -1518,6 +1518,7 @@ " >希望 + 希望.お金 Bitcoin is diff --git a/src/website/scripts/main.js b/src/website/scripts/main.js index 9e3f3d7b7..17edce5b5 100644 --- a/src/website/scripts/main.js +++ b/src/website/scripts/main.js @@ -1311,10 +1311,10 @@ function initFrameSelectors() { function setAsideParent() { const { clientWidth } = window.document.documentElement; const { aside, body, main } = elements; - if (clientWidth >= consts.MEDIUM_WIDTH && aside.parentElement !== body) { - body.append(aside); - } else if (aside.parentElement !== main) { - main.append(aside); + if (clientWidth >= consts.MEDIUM_WIDTH) { + aside.parentElement !== body && body.append(aside); + } else { + aside.parentElement !== main && main.append(aside); } } diff --git a/src/website/scripts/options.js b/src/website/scripts/options.js index 983f05e97..6d0c35637 100644 --- a/src/website/scripts/options.js +++ b/src/website/scripts/options.js @@ -1501,28 +1501,23 @@ function createPartialOptions(colors) { name: "Market", tree: [ { - name: "Price", - tree: [ + scale, + name: "Dollars Per Bitcoin", + title: "Dollars Per Bitcoin", + description: "", + unit: "US Dollars", + }, + { + scale, + name: "Satoshis Per Dollar", + title: "Satoshis Per Dollar", + description: "", + unit: "Satoshis", + bottom: [ { - scale, - name: "Dollars Per Bitcoin", - title: "Dollars Per Bitcoin", - description: "", - unit: "US Dollars", - }, - { - scale, - name: "Sats Per Dollar", - title: "Satoshis Per Dollar", - description: "", - unit: "Satoshis", - bottom: [ - { - title: "Sats", - datasetPath: `${scale}-to-sats-per-dollar`, - color: colors.bitcoin, - }, - ], + title: "Satoshis", + datasetPath: `${scale}-to-sats-per-dollar`, + color: colors.bitcoin, }, ], }, @@ -5128,18 +5123,9 @@ function createPartialOptions(colors) { url: () => window.location.href, }, { - name: "Socials", - tree: [ - { - name: "Bluesky", - url: () => "https://bsky.app/profile/kibo.money", - }, - { - name: "Nostr", - url: () => - "https://primal.net/p/npub1jagmm3x39lmwfnrtvxcs9ac7g300y3dusv9lgzhk2e4x5frpxlrqa73v44", - }, - ], + name: "Social", + url: () => + "https://primal.net/p/npub1jagmm3x39lmwfnrtvxcs9ac7g300y3dusv9lgzhk2e4x5frpxlrqa73v44", }, { name: "Developers", From 9dd87a48a693b89014b3f3cd6e43bdedc30c9ac1 Mon Sep 17 00:00:00 2001 From: nym21 Date: Sun, 22 Dec 2024 00:42:11 +0100 Subject: [PATCH 09/63] server: rework api side --- README.md | 3 + src/main.rs | 15 +- src/parser/actions/export.rs | 10 +- src/parser/actions/iter_blocks.rs | 6 +- src/parser/actions/min_height.rs | 7 - src/parser/databases/mod.rs | 9 +- src/parser/databases/txid_to_tx_data.rs | 2 +- .../datasets/_traits/min_initial_state.rs | 4 + src/parser/datasets/mod.rs | 82 ++-- src/parser/datasets/subs/recap.rs | 4 +- src/parser/mod.rs | 11 +- src/server/api/handlers/dataset.rs | 370 ++++++++++-------- src/server/api/handlers/fallback.rs | 19 - src/server/api/handlers/last_values.rs | 13 + src/server/api/handlers/mod.rs | 7 +- src/server/api/handlers/response.rs | 167 -------- src/server/api/mod.rs | 7 +- .../structs/{chunk.rs => chunk_metadata.rs} | 2 +- .../api/{handlers => structs}/extension.rs | 9 - src/server/api/structs/kind.rs | 55 ++- src/server/api/structs/mod.rs | 12 +- src/server/api/structs/paths.rs | 9 - src/server/api/structs/range.rs | 32 ++ src/server/api/structs/route.rs | 33 ++ src/server/api/structs/routes.rs | 173 ++------ src/server/header_map.rs | 1 + src/server/mod.rs | 33 +- src/server/website/handlers/file.rs | 6 +- src/structs/any_map.rs | 30 +- src/structs/config.rs | 8 +- src/structs/date_map.rs | 4 +- src/structs/date_map_chunk_id.rs | 10 +- src/structs/generic_map.rs | 63 ++- src/structs/height_map_chunk_id.rs | 12 +- src/structs/map_value.rs | 3 +- src/structs/mod.rs | 1 - src/structs/ohlc.rs | 12 + src/structs/serialized_btreemap.rs | 67 ++-- src/structs/serialized_vec.rs | 60 +-- src/structs/timestamp.rs | 8 +- 40 files changed, 633 insertions(+), 746 deletions(-) delete mode 100644 src/server/api/handlers/fallback.rs create mode 100644 src/server/api/handlers/last_values.rs delete mode 100644 src/server/api/handlers/response.rs rename src/server/api/structs/{chunk.rs => chunk_metadata.rs} (85%) rename src/server/api/{handlers => structs}/extension.rs (76%) delete mode 100644 src/server/api/structs/paths.rs create mode 100644 src/server/api/structs/range.rs create mode 100644 src/server/api/structs/route.rs diff --git a/README.md b/README.md index d364690a2..b7cfd1067 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,9 @@ Please open an issue if you want to add another instance - Git - Unix based operating system (Mac OS or Linux) - Ubuntu users need to install `open-ssl` via `sudo apt install libssl-dev pkg-config` + - Mac OS: + - Disable Spotlight or exclude the `--kibodir` folder from it + - Don't use Time Machine or exclude the `--kibodir` folder (especially needed for local snapshots) ### Build diff --git a/src/main.rs b/src/main.rs index 97bdef558..999e045f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,8 @@ mod server; mod structs; mod utils; -use parser::{Databases, Datasets}; +use parser::Datasets; +use server::api::structs::Routes; use structs::{Config, Exit}; use utils::init_log; @@ -31,31 +32,27 @@ fn main() -> color_eyre::Result<()> { let rpc = Client::from(&config); - let databases = Databases::import(&config); - - let datasets = Datasets::import(&config)?; - - let paths_to_type = datasets.get_paths_to_type(&config); + let routes = Routes::build(&Datasets::import(&config)?, &config); tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .unwrap() .block_on(async { - let config_clone = config.clone(); let run_parser = config.parser(); let run_server = config.server(); + let config_clone = config.clone(); let handle = tokio::spawn(async move { if run_server { - server::main(paths_to_type, &config_clone).await.unwrap(); + server::main(routes, config_clone).await.unwrap(); } else { info!("Skipping server"); } }); if run_parser { - parser::main(&config, &rpc, &exit, databases, datasets)?; + parser::main(&config, &rpc, &exit)?; } else { info!("Skipping parser"); } diff --git a/src/parser/actions/export.rs b/src/parser/actions/export.rs index 314fb34c5..5a82e71a3 100644 --- a/src/parser/actions/export.rs +++ b/src/parser/actions/export.rs @@ -37,14 +37,14 @@ pub fn export( exit.block(); let text = if defragment { - "export and defragmentation" + "Exporting and defragmenting..." } else { - "export" + "Exporting..." }; - info!("Starting {text}"); + info!("{text}"); - time(&format!("Finished {text}"), || -> color_eyre::Result<()> { - datasets.export(config)?; + time("Finished export", || -> color_eyre::Result<()> { + datasets.export(config, height)?; if let Some(databases) = databases { databases.export(height, date, defragment)?; diff --git a/src/parser/actions/iter_blocks.rs b/src/parser/actions/iter_blocks.rs index 1e1963fdc..909a37ac9 100644 --- a/src/parser/actions/iter_blocks.rs +++ b/src/parser/actions/iter_blocks.rs @@ -173,7 +173,7 @@ pub fn iter_blocks( .as_ref() .map_or(true, |date| date.is_first_of_month()); - if (is_check_point && instant.elapsed().as_secs() >= 2) + if (is_check_point && instant.elapsed().as_secs() >= 1) || height.is_close_to_end(approx_block_count) { break 'days; @@ -192,7 +192,7 @@ pub fn iter_blocks( if first_unsafe_heights.computed <= last_height { info!("Computing datasets..."); - time("Computing datasets", || { + time("Computed datasets", || { let dates = processed_dates.into_iter().collect_vec(); let heights = processed_heights.into_iter().collect_vec(); @@ -226,7 +226,7 @@ pub fn iter_blocks( })?; if config.record_ram_usage() { - time("Exporing allocation files", || { + time("Exporting allocation files", || { generate_allocation_files(datasets, databases, &states, last_height) })?; } diff --git a/src/parser/actions/min_height.rs b/src/parser/actions/min_height.rs index 7166bdbb4..e71b6ae50 100644 --- a/src/parser/actions/min_height.rs +++ b/src/parser/actions/min_height.rs @@ -118,13 +118,6 @@ pub fn find_first_inserted_unsafe_height( || min_initial_inserted_last_address_date.is_none() || min_initial_inserted_last_address_height.is_none(); - // if include_addresses { - // dbg!(include_addresses); - // panic!(""); - // } - - if true {panic!()} - states.reset(config, include_addresses); databases.reset(include_addresses); diff --git a/src/parser/databases/mod.rs b/src/parser/databases/mod.rs index 3370d914a..413790404 100644 --- a/src/parser/databases/mod.rs +++ b/src/parser/databases/mod.rs @@ -131,8 +131,8 @@ impl Databases { pub fn check_if_usable( &self, - min_initial_last_address_height: Option, - min_initial_last_address_date: Option, + last_address_height: Option, + last_address_date: Option, ) -> bool { let are_tx_databases_in_sync = self .txout_index_to_amount @@ -169,8 +169,7 @@ impl Databases { return false; } - // let are_address_datasets_farer_or_in_sync_with_address_databases = - min_initial_last_address_height >= self.address_to_address_index.metadata.last_height - && min_initial_last_address_date >= self.address_to_address_index.metadata.last_date + last_address_height >= self.address_to_address_index.metadata.last_height + && last_address_date >= self.address_to_address_index.metadata.last_date } } diff --git a/src/parser/databases/txid_to_tx_data.rs b/src/parser/databases/txid_to_tx_data.rs index 399c760d9..59717614c 100644 --- a/src/parser/databases/txid_to_tx_data.rs +++ b/src/parser/databases/txid_to_tx_data.rs @@ -84,7 +84,7 @@ impl TxidToTxData { } #[inline(always)] - pub fn _open_db(&mut self, db_index: u16) -> &mut Database { + fn _open_db(&mut self, db_index: u16) -> &mut Database { let path = self.path.to_owned(); self.map.entry(db_index).or_insert_with(|| { let path = path.join(db_index.to_string()); diff --git a/src/parser/datasets/_traits/min_initial_state.rs b/src/parser/datasets/_traits/min_initial_state.rs index 2fdfad411..d89674eb7 100644 --- a/src/parser/datasets/_traits/min_initial_state.rs +++ b/src/parser/datasets/_traits/min_initial_state.rs @@ -29,6 +29,10 @@ impl MinInitialStates { computed: MinInitialState::compute_from_datasets(datasets, Mode::Computed, config), } } + + pub fn min_last_height(&self) -> Option { + self.computed.last_height.min(self.inserted.last_height) + } } #[derive(Default, Debug, Clone, Copy, Allocative)] diff --git a/src/parser/datasets/mod.rs b/src/parser/datasets/mod.rs index bc4edc899..e8b797b18 100644 --- a/src/parser/datasets/mod.rs +++ b/src/parser/datasets/mod.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, ops::RangeInclusive, path::PathBuf}; +use std::{collections::BTreeMap, ops::RangeInclusive}; use allocative::Allocative; @@ -139,14 +139,18 @@ impl Datasets { utxo, }; - s.min_initial_states - .consume(MinInitialStates::compute_from_datasets(&s, config)); + s.set_initial_states(config); info!("Imported datasets"); Ok(s) } + fn set_initial_states(&mut self, config: &Config) { + self.min_initial_states + .consume(MinInitialStates::compute_from_datasets(self, config)); + } + pub fn insert(&mut self, insert_data: InsertData) { if insert_data.compute_addresses { self.address.insert(&insert_data); @@ -228,21 +232,6 @@ impl Datasets { &mut self.price.market_cap, ); - // No compute needed for now - // if self.block_metadata.should_compute(height, date) { - // self.block_metadata.compute(&compute_data); - // } - - // No compute needed for now - // if self.date_metadata.should_compute(height, date) { - // self.date_metadata.compute(&compute_data); - // } - - // No compute needed for now - // if self.coindays.should_compute(height, date) { - // self.coindays.compute(&compute_data); - // } - if self.transaction.should_compute(&compute_data) { self.transaction.compute( &compute_data, @@ -268,23 +257,12 @@ impl Datasets { } } - pub fn get_paths_to_type(&self, config: &Config) -> BTreeMap { - let mut path_to_type: BTreeMap = self - .to_any_dataset_vec() - .into_iter() - .flat_map(|dataset| dataset.to_all_map_vec()) - .flat_map(|map| map.get_paths_to_type()) - .collect(); + pub fn export(&mut self, config: &Config, height: Height) -> color_eyre::Result<()> { + let is_new = self + .min_initial_states + .min_last_height() + .map_or(true, |last| last <= height); - path_to_type.insert( - config.path_datasets_last_values().unwrap().to_owned(), - "Value".to_string(), - ); - - path_to_type - } - - pub fn export(&mut self, config: &Config) -> color_eyre::Result<()> { self.to_mut_any_dataset_vec() .into_iter() .for_each(|dataset| dataset.pre_export()); @@ -295,37 +273,27 @@ impl Datasets { let mut path_to_last: BTreeMap = BTreeMap::default(); - let path_dataset = config.path_datasets(); - let path_dataset_ser = path_dataset.to_str().unwrap(); - let path_price = config.path_price(); - let path_price_ser = path_price.to_str().unwrap(); - self.to_mut_any_dataset_vec() .into_iter() .for_each(|dataset| { dataset.post_export(); - dataset.to_all_map_vec().iter().for_each(|map| { - if let Some(last_path) = map.path_last() { - if let Some(last_value) = map.last_value() { - let mut last_path = last_path.clone(); - last_path.pop(); - let key = last_path - .to_str() - .unwrap() - .replace(path_dataset_ser, "") - .replace(path_price_ser, "") - .split("/") - .join("-") - .to_string(); - - path_to_last.insert(key, last_value); + if is_new { + dataset.to_all_map_vec().iter().for_each(|map| { + if map.path_last().is_some() { + if let Some(last_value) = map.last_value() { + path_to_last.insert(map.id(config), last_value); + } } - } - }); + }); + } }); - Json::export(&config.path_datasets_last_values(), &path_to_last)?; + if is_new { + Json::export(&config.path_datasets_last_values(), &path_to_last)?; + } + + self.set_initial_states(config); Ok(()) } diff --git a/src/parser/datasets/subs/recap.rs b/src/parser/datasets/subs/recap.rs index e02277c1e..7dac8d9d3 100644 --- a/src/parser/datasets/subs/recap.rs +++ b/src/parser/datasets/subs/recap.rs @@ -5,12 +5,12 @@ use allocative::Allocative; use crate::{ structs::{ Date, DateMapChunkId, GenericMap, MapChunkId, MapKey, MapKind, MapPath, MapSerialized, - MapValue, SerializedBTreeMap, + MapValue, SerializedDateMap, }, utils::{get_percentile, LossyFrom}, }; -pub type DateRecapDataset = RecapDataset>; +pub type DateRecapDataset = RecapDataset>; #[derive(Allocative)] pub struct RecapDataset { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index b720f435c..3379223bd 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -16,18 +16,15 @@ pub use states::*; use crate::structs::{Config, Exit}; -pub fn main( - config: &Config, - rpc: &Client, - exit: &Exit, - mut databases: Databases, - mut datasets: Datasets, -) -> color_eyre::Result<()> { +pub fn main(config: &Config, rpc: &Client, exit: &Exit) -> color_eyre::Result<()> { loop { let block_count = rpc.get_blockchain_info().unwrap().blocks as usize; info!("{block_count} blocks found."); + let mut databases = Databases::import(config); + let mut datasets = Datasets::import(config)?; + iter_blocks( config, rpc, diff --git a/src/server/api/handlers/dataset.rs b/src/server/api/handlers/dataset.rs index c1fa63cec..e547a44dd 100644 --- a/src/server/api/handlers/dataset.rs +++ b/src/server/api/handlers/dataset.rs @@ -1,224 +1,274 @@ -use std::{collections::BTreeMap, path::PathBuf}; +use std::{fmt::Debug, path::PathBuf, time::Instant}; use axum::{ extract::{Path, Query, State}, http::HeaderMap, response::{IntoResponse, Response}, }; -use color_eyre::{eyre::eyre, owo_colors::OwoColorize}; +use bincode::Decode; +use chrono::{DateTime, Utc}; +use color_eyre::eyre::{eyre, ContextCompat}; use reqwest::StatusCode; -use serde::Deserialize; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ - io::{Json, COMPRESSED_BIN_EXTENSION, JSON_EXTENSION}, server::{ api::{ - structs::{Chunk, Kind, Route}, + structs::{ChunkMetadata, DatasetRange, DatasetRangeChunk, Extension, Kind, Route}, API_URL_PREFIX, }, header_map::HeaderMapUtils, log_result, AppState, }, - structs::{Date, DateMap, Height, HeightMap, MapChunkId, HEIGHT_MAP_CHUNK_SIZE, OHLC}, -}; - -use super::{ - extension::Extension, - response::{typed_value_to_response, value_to_response}, + structs::{ + Date, GenericMap, Height, MapChunkId, MapKey, MapSerialized, MapValue, SerializedDateMap, + SerializedVec, OHLC, + }, }; #[derive(Deserialize)] -pub struct Params { - chunk: Option, - all: Option, +pub struct DatasetParams { + pub chunk: Option, + pub all: Option, + pub kind: String, } pub async fn dataset_handler( headers: HeaderMap, path: Path, - query: Query, + query: Query, State(app_state): State, ) -> Response { - match _dataset_handler(headers, &path, &query, app_state) { - Ok(response) => { - log_result( - response.status(), - &format!( - "{API_URL_PREFIX}/{}{}{}", - path.0, - query.chunk.map_or("".to_string(), |chunk| format!( - "{}{chunk}", - "?chunk=".bright_black() - )), - query.all.map_or("".to_string(), |all| format!( - "{}{all}", - "?all=".bright_black() - )) - ), - ); + let instant = Instant::now(); + let ser_path = format!( + "{API_URL_PREFIX}/{}?kind={}{}{}", + path.0, + query.kind, + query + .chunk + .map_or("".to_string(), |chunk| format!("&chunk={chunk}")), + query + .all + .map_or("".to_string(), |all| format!("&all={all}")) + ); + + match result_handler(headers, &path, &query, app_state) { + Ok(response) => { + log_result(response.status(), &ser_path, instant); response } Err(error) => { - let code = StatusCode::INTERNAL_SERVER_ERROR; - - log_result(code, &format!("{API_URL_PREFIX}/{}", path.0)); - - let mut response = (code, error.to_string()).into_response(); - + let mut response = + (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response(); + log_result(response.status(), &ser_path, instant); response.headers_mut().insert_cors(); - response } } } -const DATE_PREFIX: &str = "date-to-"; -const HEIGHT_PREFIX: &str = "height-to-"; - -fn _dataset_handler( +fn result_handler( headers: HeaderMap, Path(path): &Path, - query: &Query, - AppState { routes }: AppState, + query: &Query, + AppState { routes, .. }: AppState, ) -> color_eyre::Result { - if query.chunk.is_some() && query.all.is_some() { - return Err(eyre!("chunk and all are exclusive")); - } - - let (kind, id, route) = if path.starts_with(DATE_PREFIX) { - let id = convert_path_to_id(path.strip_prefix(DATE_PREFIX).unwrap()); - let route = routes.date.get(&id); - (Kind::Date, id, route) - } else if path.starts_with(HEIGHT_PREFIX) { - let id = convert_path_to_id(path.strip_prefix(HEIGHT_PREFIX).unwrap()); - let route = routes.height.get(&id); - (Kind::Height, id, route) - } else { - let id = convert_path_to_id(path); - let route = routes.last.get(&id); - (Kind::Last, id, route) - }; + let path_buf = PathBuf::from(&path); + let id = path_buf.file_stem().unwrap().to_str().unwrap(); + let ext = Extension::from(&path_buf); + let route = routes.get(id); if route.is_none() { - return Err(eyre!("Path error")); + return Err(eyre!("Wrong path")); } + let route = route.unwrap(); - let mut route = route.unwrap().to_owned(); - - let mut chunk = None; - - if query.all.map_or(true, |b| !b) { - match kind { - Kind::Date => { - let datasets = DateMap::::_read_dir(&route.file_path, &route.serialization); - - process_datasets(&headers, kind, &mut chunk, &mut route, query, datasets)?; - } - Kind::Height => { - let datasets = - HeightMap::::_read_dir(&route.file_path, &route.serialization); - - process_datasets(&headers, kind, &mut chunk, &mut route, query, datasets)?; - } - Kind::Last => { - if !route.values_type.ends_with("Value") { - route.file_path.set_extension(COMPRESSED_BIN_EXTENSION); - } else { - route.file_path.set_extension(JSON_EXTENSION); - } - } - } - } - - let (date, response) = headers.check_if_modified_since(&route.file_path)?; - - if let Some(response) = response { - return Ok(response); - } - - let type_name = route.values_type.split("::").last().unwrap(); - - let extension = Extension::from(&std::path::PathBuf::from(&path)); - - let mut response = match type_name { - "u8" => typed_value_to_response::(kind, &route, chunk, id, extension)?, - "u16" => typed_value_to_response::(kind, &route, chunk, id, extension)?, - "u32" => typed_value_to_response::(kind, &route, chunk, id, extension)?, - "u64" => typed_value_to_response::(kind, &route, chunk, id, extension)?, - "usize" => typed_value_to_response::(kind, &route, chunk, id, extension)?, - "f32" => typed_value_to_response::(kind, &route, chunk, id, extension)?, - "f64" => typed_value_to_response::(kind, &route, chunk, id, extension)?, - "OHLC" => typed_value_to_response::(kind, &route, chunk, id, extension)?, - "Date" => typed_value_to_response::(kind, &route, chunk, id, extension)?, - "Height" => typed_value_to_response::(kind, &route, chunk, id, extension)?, - "Value" => { - value_to_response::(Json::import(&route.file_path)?, extension) - } + let type_name = route.type_name.as_str(); + Ok(match type_name { + "u8" => typed_handler::(headers, id, ext, query, route)?, + "u16" => typed_handler::(headers, id, ext, query, route)?, + "u32" => typed_handler::(headers, id, ext, query, route)?, + "u64" => typed_handler::(headers, id, ext, query, route)?, + "usize" => typed_handler::(headers, id, ext, query, route)?, + "f32" => typed_handler::(headers, id, ext, query, route)?, + "f64" => typed_handler::(headers, id, ext, query, route)?, + "OHLC" => typed_handler::(headers, id, ext, query, route)?, + "Date" => typed_handler::(headers, id, ext, query, route)?, + "Height" => typed_handler::(headers, id, ext, query, route)?, + // "Value" => { + // value_to_response::(Json::import(&route.file_path)?, extension) + // } _ => panic!("Incompatible type: {type_name}"), - }; + }) +} +fn typed_handler( + headers: HeaderMap, + id: &str, + ext: Option, + query: &Query, + route: &Route, +) -> color_eyre::Result +where + T: Serialize + Debug + DeserializeOwned + Decode + MapValue, +{ + let kind = Kind::try_from(&query.kind)?; + if !route.list.contains(&kind) { + return Err(eyre!("{kind:?} not supported for this dataset")); + } + + let range = DatasetRange::try_from(query)?; + + let (mut response, date_modified) = match kind { + Kind::Date => map_to_response::>( + id, headers, route, &ext, range, query, + ), + Kind::Height => map_to_response::>( + id, headers, route, &ext, range, query, + ), + Kind::Last => { + let last_value: T = route.serialization.import(&route.path.join("last"))?; + return Ok(axum::response::Json(last_value).into_response()); + } + }?; + + let status_ok = response.status() == StatusCode::OK; let headers = response.headers_mut(); - headers.insert_last_modified(date); + + headers.insert_cors(); + + if status_ok { + headers.insert_last_modified(date_modified); + } + + match ext { + Some(extension) => { + headers.insert_content_disposition_attachment(); + match extension { + Extension::CSV => headers.insert_content_type_text_csv(), + Extension::JSON => headers.insert_content_type_application_json(), + } + } + _ => headers.insert_content_type_application_json(), + } Ok(response) } -fn convert_path_to_id(s: &str) -> String { - Extension::remove_extension(s).replace('-', "_") -} - -fn process_datasets( - headers: &HeaderMap, - kind: Kind, - chunk: &mut Option, - route: &mut Route, - query: &Query, - datasets: BTreeMap, -) -> color_eyre::Result<()> +fn map_to_response( + id: &str, + headers: HeaderMap, + route: &Route, + ext: &Option, + range: DatasetRange, + query: &Query, +) -> color_eyre::Result<(Response, DateTime)> where + Key: MapKey, + Value: MapValue, ChunkId: MapChunkId, + Serialized: MapSerialized, { - let (last_chunk_id, _) = datasets.last_key_value().unwrap_or_else(|| { - dbg!(&datasets, &route); - panic!() - }); + let folder_path = route.path.join(Key::map_name()); + let serialization = &route.serialization; - let chunk_id = query - .chunk - .map(|id| ChunkId::from_usize(id)) - .unwrap_or(*last_chunk_id); + let date_modified; - let path = datasets.get(&chunk_id); + let datasets = + GenericMap::::_read_dir(&folder_path, serialization); - if path.is_none() { - return Err(eyre!("Couldn't find chunk")); - } + let mut chunk_meta = None; - let path = path.unwrap(); - route.file_path = path.clone(); + let dataset = if let DatasetRange::Chunk(range_chunk) = range { + let chunk_id = match range_chunk { + DatasetRangeChunk::Last => { + *datasets + .last_key_value() + .context("Last tuple of dataset directory")? + .0 + } + DatasetRangeChunk::Chunk(chunk) => ChunkId::from_usize(chunk), + }; - let offset = match kind { - Kind::Date => 1, - Kind::Height => HEIGHT_MAP_CHUNK_SIZE as usize, - _ => panic!(), + let chunk_path = datasets.get(&chunk_id); + if chunk_path.is_none() { + return Err(eyre!("Couldn't find chunk")); + } + let chunk_path = chunk_path.unwrap(); + + let (date, response) = headers.check_if_modified_since(chunk_path)?; + date_modified = date; + + if let Some(response) = response { + return Ok((response, date_modified)); + } + + let to_url = |chunk: Option| { + chunk.and_then(|chunk| { + datasets.contains_key(&chunk).then(|| { + let scheme = headers.get_scheme(); + let host = headers.get_host(); + format!( + "{scheme}://{host}/api/{id}?kind={}&chunk={}", + query.kind, + chunk.to_usize() + ) + }) + }) + }; + + chunk_meta.replace(ChunkMetadata { + id: chunk_id.to_usize(), + next: to_url(chunk_id.next()), + previous: to_url(chunk_id.previous()), + }); + + serialization.import::(chunk_path)? + } else { + let newest_file = datasets + .values() + .max_by(|a, b| { + a.metadata() + .unwrap() + .modified() + .unwrap() + .cmp(&b.metadata().unwrap().modified().unwrap()) + }) + .context("Expect to find newest file")?; + + let (date, response) = headers.check_if_modified_since(newest_file)?; + date_modified = date; + + if let Some(response) = response { + return Ok((response, date_modified)); + } + + Serialized::import_all(&folder_path, serialization) }; - let offsetted_to_url = |offseted| { - datasets.get(&ChunkId::from_usize(offseted)).map(|_| { - let scheme = headers.get_scheme(); - let host = headers.get_host(); - format!("{scheme}://{host}/api/{}?chunk={offseted}", route.url_path) + let response = if *ext == Some(Extension::CSV) { + dataset.to_csv(id).into_response() + } else if let Some(chunk) = chunk_meta { + axum::response::Json(SerializedMapChunk { + chunk, + map: dataset.map(), + version: dataset.version(), }) + .into_response() + } else { + axum::response::Json(dataset).into_response() }; - let chunk_id = chunk_id.to_usize(); - - chunk.replace(Chunk { - id: chunk_id, - next: chunk_id.checked_add(offset).and_then(offsetted_to_url), - previous: chunk_id.checked_sub(offset).and_then(offsetted_to_url), - }); - - Ok(()) + Ok((response, date_modified)) +} + +#[derive(Serialize)] +struct SerializedMapChunk +where + T: Serialize, +{ + version: u32, + chunk: ChunkMetadata, + map: T, } diff --git a/src/server/api/handlers/fallback.rs b/src/server/api/handlers/fallback.rs deleted file mode 100644 index 3ce9c7618..000000000 --- a/src/server/api/handlers/fallback.rs +++ /dev/null @@ -1,19 +0,0 @@ -use axum::{extract::State, http::HeaderMap, response::Response}; -use reqwest::header::HOST; - -use crate::server::AppState; - -use super::response::{generic_to_reponse, update_reponse_headers}; - -pub async fn fallback(headers: HeaderMap, State(app_state): State) -> Response { - update_reponse_headers( - generic_to_reponse( - app_state - .routes - .to_full_paths(headers[HOST].to_str().unwrap().to_string()), - None, - ), - 60, - None, - ) -} diff --git a/src/server/api/handlers/last_values.rs b/src/server/api/handlers/last_values.rs new file mode 100644 index 000000000..1ba426f0a --- /dev/null +++ b/src/server/api/handlers/last_values.rs @@ -0,0 +1,13 @@ +use axum::{ + extract::State, + response::{IntoResponse, Response}, +}; +use serde_json::Value; + +use crate::{io::Json, server::AppState}; + +pub async fn last_values_handler(State(app_state): State) -> Response { + let values = Json::import::(&app_state.config.path_datasets_last_values()).unwrap(); + let values = axum::Json(values); + values.into_response() +} diff --git a/src/server/api/handlers/mod.rs b/src/server/api/handlers/mod.rs index 33474ff3a..518fe0dc7 100644 --- a/src/server/api/handlers/mod.rs +++ b/src/server/api/handlers/mod.rs @@ -1,8 +1,5 @@ mod dataset; -mod extension; -mod fallback; - -mod response; +mod last_values; pub use dataset::*; -pub use fallback::*; +pub use last_values::*; diff --git a/src/server/api/handlers/response.rs b/src/server/api/handlers/response.rs deleted file mode 100644 index 449c73dd1..000000000 --- a/src/server/api/handlers/response.rs +++ /dev/null @@ -1,167 +0,0 @@ -use std::fmt::Debug; - -use axum::response::{IntoResponse, Json, Response}; -use bincode::Decode; -use serde::de::DeserializeOwned; -use serde::Serialize; - -use crate::{ - server::{ - api::structs::{Chunk, Kind, Route}, - header_map::HeaderMapUtils, - }, - structs::{Date, MapValue, SerializedBTreeMap, SerializedVec}, -}; - -use super::extension::Extension; - -#[derive(Serialize)] -struct WrappedDataset<'a, T> -where - T: Serialize, -{ - source: &'a str, - chunk: Chunk, - dataset: T, -} - -#[derive(Serialize)] -struct WrappedValue -where - T: Serialize, -{ - value: T, -} - -pub fn typed_value_to_response( - kind: Kind, - route: &Route, - chunk: Option, - id: String, - extension: Option, -) -> color_eyre::Result -where - T: Serialize + Debug + DeserializeOwned + Decode + MapValue, -{ - Ok(match kind { - Kind::Date => { - let dataset = if chunk.is_some() { - route - .serialization - .import::>(&route.file_path)? - } else { - SerializedBTreeMap::::import_all(&route.file_path, &route.serialization) - }; - - if extension == Some(Extension::CSV) { - let mut csv = format!("date,{}\n", id); - - dataset.map.iter().for_each(|(k, v)| { - csv += &format!("{},{:?}\n", k, v); - }); - - string_to_response(csv, extension) - } else { - dataset_to_response(dataset, chunk, extension) - } - } - Kind::Height => { - let dataset = if chunk.is_some() { - route - .serialization - .import::>(&route.file_path)? - } else { - SerializedVec::::import_all(&route.file_path, &route.serialization) - }; - - if extension == Some(Extension::CSV) { - let mut csv = format!("height,{}\n", id); - - let starting_height = chunk.map_or(0, |chunk| chunk.id); - - dataset.map.iter().enumerate().for_each(|(k, v)| { - csv += &format!("{},{:?}\n", starting_height + k, v); - }); - - string_to_response(csv, extension) - } else { - dataset_to_response(dataset, chunk, extension) - } - } - Kind::Last => { - let value = route.serialization.import::(&route.file_path)?; - - if extension == Some(Extension::JSON) { - value_to_response(WrappedValue { value }, extension) - } else { - value_to_response(value, extension) - } - } - }) -} - -pub fn string_to_response(s: String, extension: Option) -> Response { - update_reponse_headers(s.into_response(), 5, extension) -} - -pub fn value_to_response(value: T, extension: Option) -> Response -where - T: Serialize, -{ - update_reponse_headers(generic_to_reponse(value, None), 1, extension) -} - -fn dataset_to_response( - dataset: T, - chunk: Option, - extension: Option, -) -> Response -where - T: Serialize, -{ - update_reponse_headers(generic_to_reponse(dataset, chunk), 5, extension) -} - -pub fn generic_to_reponse(generic: T, chunk: Option) -> Response -where - T: Serialize, -{ - if let Some(chunk) = chunk { - Json(WrappedDataset { - source: "https://kibo.money", - chunk, - dataset: generic, - }) - .into_response() - } else { - Json(generic).into_response() - } -} - -pub fn update_reponse_headers( - mut response: Response, - cache_time: u64, - extension: Option, -) -> Response { - let headers = response.headers_mut(); - - let max_age = cache_time; - let stale_while_revalidate = max_age; - - headers.insert_cors(); - headers.insert_cache_control_revalidate(max_age, stale_while_revalidate); - - match extension { - Some(extension) => { - headers.insert_content_disposition_attachment(); - - match extension { - Extension::CSV => headers.insert_content_type_text_csv(), - Extension::JSON => headers.insert_content_type_application_json(), - } - } - _ => headers.insert_content_type_application_json(), - } - - response -} diff --git a/src/server/api/mod.rs b/src/server/api/mod.rs index 5dfdc8871..9ea585089 100644 --- a/src/server/api/mod.rs +++ b/src/server/api/mod.rs @@ -1,5 +1,5 @@ use axum::{routing::get, Router}; -use handlers::{dataset_handler, fallback}; +use handlers::{dataset_handler, last_values_handler}; use super::AppState; @@ -14,8 +14,7 @@ pub trait ApiRoutes { impl ApiRoutes for Router { fn add_api_routes(self) -> Self { - self.route(&format!("{API_URL_PREFIX}/*path"), get(dataset_handler)) - .route(&format!("{API_URL_PREFIX}/"), get(fallback)) - .route(API_URL_PREFIX, get(fallback)) + self.route(&format!("{API_URL_PREFIX}/last"), get(last_values_handler)) + .route(&format!("{API_URL_PREFIX}/*path"), get(dataset_handler)) } } diff --git a/src/server/api/structs/chunk.rs b/src/server/api/structs/chunk_metadata.rs similarity index 85% rename from src/server/api/structs/chunk.rs rename to src/server/api/structs/chunk_metadata.rs index c375f75c5..9dc2535e0 100644 --- a/src/server/api/structs/chunk.rs +++ b/src/server/api/structs/chunk_metadata.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] -pub struct Chunk { +pub struct ChunkMetadata { pub id: usize, pub previous: Option, pub next: Option, diff --git a/src/server/api/handlers/extension.rs b/src/server/api/structs/extension.rs similarity index 76% rename from src/server/api/handlers/extension.rs rename to src/server/api/structs/extension.rs index 946ad67b9..21a7477c4 100644 --- a/src/server/api/handlers/extension.rs +++ b/src/server/api/structs/extension.rs @@ -31,13 +31,4 @@ impl Extension { Extension::JSON => "json", } } - - pub fn to_dot_str(&self) -> String { - format!(".{}", self.to_str()) - } - - pub fn remove_extension(s: &str) -> String { - s.replace(&Self::CSV.to_dot_str(), "") - .replace(&Self::JSON.to_dot_str(), "") - } } diff --git a/src/server/api/structs/kind.rs b/src/server/api/structs/kind.rs index 93d4ac932..ab25dc6f0 100644 --- a/src/server/api/structs/kind.rs +++ b/src/server/api/structs/kind.rs @@ -1,6 +1,59 @@ -#[derive(PartialEq, Eq, Clone, Copy)] +use std::collections::BTreeSet; + +use color_eyre::eyre::{eyre, ContextCompat}; +use serde::Deserialize; + +use crate::structs::{AnyMap, Date, Height, MapKey}; + +#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub enum Kind { Date, Height, Last, } + +impl TryFrom<&String> for Kind { + type Error = color_eyre::Report; + + fn try_from(str: &String) -> Result { + Ok( + match str + .to_lowercase() + .chars() + .next() + .context("Expect kind to have first letter")? + { + 'd' => Self::Date, + 'h' => Self::Height, + 'l' => Self::Last, + _ => return Err(eyre!("Bad kind")), + }, + ) + } +} + +impl From<&(dyn AnyMap + Send + Sync)> for BTreeSet { + fn from(map: &(dyn AnyMap + Send + Sync)) -> Self { + let mut s = Self::new(); + if map.key_name() == Date::map_name() { + s.insert(Kind::Date); + } + if map.key_name() == Height::map_name() { + s.insert(Kind::Height); + } + if map.last_value().is_some() { + s.insert(Kind::Last); + } + s + } +} + +// impl std::fmt::Display for Kind { +// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +// match *self { +// Self::Date => write!(f, "date"), +// Self::Height => write!(f, "height"), +// Self::Last => write!(f, "last"), +// } +// } +// } diff --git a/src/server/api/structs/mod.rs b/src/server/api/structs/mod.rs index 94f3cc402..76df682d8 100644 --- a/src/server/api/structs/mod.rs +++ b/src/server/api/structs/mod.rs @@ -1,9 +1,13 @@ -mod chunk; +mod chunk_metadata; +mod extension; mod kind; -mod paths; +mod range; +mod route; mod routes; -pub use chunk::*; +pub use chunk_metadata::*; +pub use extension::*; pub use kind::*; -pub use paths::*; +pub use range::*; +pub use route::*; pub use routes::*; diff --git a/src/server/api/structs/paths.rs b/src/server/api/structs/paths.rs deleted file mode 100644 index 9e707278f..000000000 --- a/src/server/api/structs/paths.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::collections::BTreeMap; - -use derive_deref::{Deref, DerefMut}; -use serde::Serialize; - -use crate::server::Grouped; - -#[derive(Clone, Default, Deref, DerefMut, Debug, Serialize)] -pub struct Paths(pub Grouped>); diff --git a/src/server/api/structs/range.rs b/src/server/api/structs/range.rs new file mode 100644 index 000000000..84754a07e --- /dev/null +++ b/src/server/api/structs/range.rs @@ -0,0 +1,32 @@ +use axum::extract::Query; +use color_eyre::eyre::eyre; + +use crate::server::api::handlers::DatasetParams; + +pub enum DatasetRange { + All, + Chunk(DatasetRangeChunk), +} + +impl TryFrom<&Query> for DatasetRange { + type Error = color_eyre::Report; + + fn try_from(query: &Query) -> Result { + if let Some(chunk) = query.chunk { + if query.all.is_some() { + Err(eyre!("chunk and all are exclusive")) + } else { + Ok(Self::Chunk(DatasetRangeChunk::Chunk(chunk))) + } + } else if query.all.is_some() { + Ok(Self::All) + } else { + Ok(Self::Chunk(DatasetRangeChunk::Last)) + } + } +} + +pub enum DatasetRangeChunk { + Chunk(usize), + Last, +} diff --git a/src/server/api/structs/route.rs b/src/server/api/structs/route.rs new file mode 100644 index 000000000..0eda227eb --- /dev/null +++ b/src/server/api/structs/route.rs @@ -0,0 +1,33 @@ +use std::{collections::BTreeSet, path::PathBuf}; + +use crate::{io::Serialization, structs::AnyMap}; + +use super::Kind; + +#[derive(Debug, Clone)] +pub struct Route { + pub type_name: String, + pub list: BTreeSet, + pub path: PathBuf, + pub serialization: Serialization, +} + +impl Route { + pub fn update(&mut self, map: &(dyn AnyMap + Send + Sync)) { + self.list.append(&mut BTreeSet::from(map)); + if self.serialization != map.serialization() { + panic!("route.upate() different serialization") + } + } +} + +impl From<&(dyn AnyMap + Send + Sync)> for Route { + fn from(map: &(dyn AnyMap + Send + Sync)) -> Self { + Self { + list: BTreeSet::from(map), + path: map.path_parent().to_owned(), + type_name: map.type_name().split("::").last().unwrap().to_owned(), + serialization: map.serialization(), + } + } +} diff --git a/src/server/api/structs/routes.rs b/src/server/api/structs/routes.rs index 0afbd3b0c..14141142f 100644 --- a/src/server/api/structs/routes.rs +++ b/src/server/api/structs/routes.rs @@ -1,135 +1,49 @@ -use std::{ - collections::{BTreeMap, HashMap}, - fs, - path::PathBuf, -}; +use std::collections::BTreeMap; use derive_deref::{Deref, DerefMut}; -use itertools::Itertools; use crate::{ - io::Serialization, - server::{api::API_URL_PREFIX, Grouped}, + parser::{AnyDatasets, Datasets}, structs::Config, }; -use super::Paths; +use super::Route; -#[derive(Clone, Debug)] -pub struct Route { - pub url_path: String, - pub file_path: PathBuf, - pub values_type: String, - pub serialization: Serialization, -} - -#[derive(Clone, Default, Deref, DerefMut)] -pub struct Routes(pub Grouped>); +#[derive(Debug, Clone, Default, Deref, DerefMut)] +pub struct Routes(BTreeMap); const WEBSITE_TYPES_PATH: &str = "../website/scripts/types"; impl Routes { - pub fn build(paths_to_type: BTreeMap, config: &Config) -> Self { - let mut routes = Routes::default(); - - paths_to_type.into_iter().for_each(|(path, value)| { - let try_from_path = if path.is_file() { - path.clone() - } else { - fs::read_dir(&path) - .unwrap_or_else(|_| { - dbg!(&path); - panic!(); - }) - .map(|e| e.unwrap().path()) - .find(|e| e.is_file()) - .unwrap() - }; - - let serialization = - Serialization::try_from(&try_from_path).unwrap_or(Serialization::Binary); - - let file_path_ser = path.to_str().unwrap().to_owned(); - let split_key = file_path_ser.replace( - &format!("{}/", config.path_datasets().to_str().unwrap()), - "", - ); - let split_key = - split_key.replace(&format!("{}/", config.path_kibodir().to_str().unwrap()), ""); - let mut split_key = split_key.split('/').collect_vec(); - let last = split_key.pop().unwrap().to_owned(); - let last = last.split('.').next().unwrap(); - - // Use case for: "../datasets/last": "Value", - if split_key.is_empty() { - split_key.push("last"); - } - - let map_key = split_key.iter().join("_"); - - let url_path = split_key.iter().join("-"); - - let values_type = value.to_owned(); - - match last { - "date" => { - routes.date.insert( - map_key, - Route { - url_path: format!("date-to-{url_path}"), - file_path: path, - values_type, - serialization, - }, - ); - } - "height" => { - routes.height.insert( - map_key, - Route { - url_path: format!("height-to-{url_path}"), - file_path: path, - values_type, - serialization, - }, - ); - } - "last" => { - routes.last.insert( - map_key, - Route { - url_path, - file_path: path, - values_type, - serialization, - }, - ); - } - _ => { - dbg!(&path, value, &last, &split_key); - panic!("") - } - } - }); - - routes + pub fn build(datasets: &Datasets, config: &Config) -> Self { + datasets + .to_any_dataset_vec() + .into_iter() + .flat_map(|dataset| dataset.to_all_map_vec()) + .fold(Self::default(), |mut routes, map| { + routes + .entry(map.id(config)) + .or_insert_with(|| Route::from(map)) + .update(map); + routes + }) } pub fn generate_dts_file(&self) { - let map_to_type = |name: &str, map: &HashMap| -> String { - let paths = map - .values() - .map(|route| format!("\"{}\"", route.url_path)) - .join(" | "); + // let map_to_type = |name: &str, map: &HashMap| -> String { + // let paths = map + // .values() + // .map(|route| format!("\"{}\"", route.url_path)) + // .join(" | "); - format!("export type {}Path = {};\n", name, paths) - }; + // format!("export type {}Path = {};\n", name, paths) + // }; - let date_type = map_to_type("Date", &self.date); + // let date_type = map_to_type("Date", &self.date); - let height_type = map_to_type("Height", &self.height); + // let height_type = map_to_type("Height", &self.height); - let last_type = map_to_type("Last", &self.last); + // let last_type = map_to_type("Last", &self.last); // fs::write( // format!("{WEBSITE_TYPES_PATH}/paths.d.ts"), @@ -137,37 +51,4 @@ impl Routes { // ) // .unwrap(); } - - pub fn to_full_paths(&self, host: String) -> Paths { - let url = { - let scheme = if host.contains("0.0.0.0") || host.contains("localhost") { - "http" - } else { - "https" - }; - - format!("{scheme}://{host}") - }; - - let transform = |map: &HashMap| -> BTreeMap { - map.iter() - .map(|(key, route)| { - ( - key.to_owned(), - format!("{url}{API_URL_PREFIX}/{}", route.url_path.to_owned()), - ) - }) - .collect() - }; - - let date_paths = transform(&self.date); - let height_paths = transform(&self.height); - let last_paths = transform(&self.last); - - Paths(Grouped { - date: date_paths, - height: height_paths, - last: last_paths, - }) - } } diff --git a/src/server/header_map.rs b/src/server/header_map.rs index 145f42796..837ec6132 100644 --- a/src/server/header_map.rs +++ b/src/server/header_map.rs @@ -31,6 +31,7 @@ pub trait HeaderMapUtils { ) -> color_eyre::Result<(DateTime, Option>)>; fn insert_cache_control_immutable(&mut self); + #[allow(unused)] fn insert_cache_control_revalidate(&mut self, max_age: u64, stale_while_revalidate: u64); fn insert_last_modified(&mut self, date: DateTime); diff --git a/src/server/mod.rs b/src/server/mod.rs index 4d0de972b..9284ae71c 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,43 +1,32 @@ -use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; +use std::{sync::Arc, time::Instant}; use api::{structs::Routes, ApiRoutes}; use axum::{routing::get, serve, Router}; use color_eyre::owo_colors::OwoColorize; use log::{error, info}; use reqwest::StatusCode; -use serde::Serialize; use tokio::net::TcpListener; use tower_http::compression::CompressionLayer; use website::WebsiteRoutes; use crate::structs::Config; -mod api; +pub mod api; mod header_map; mod website; -#[derive(Clone, Debug, Default, Serialize)] -pub struct Grouped { - pub date: T, - pub height: T, - pub last: T, -} - #[derive(Clone)] pub struct AppState { routes: Arc, + config: Config, } -pub async fn main( - paths_to_type: BTreeMap, - config: &Config, -) -> color_eyre::Result<()> { - let routes = Routes::build(paths_to_type, config); - +pub async fn main(routes: Routes, config: Config) -> color_eyre::Result<()> { routes.generate_dts_file(); let state = AppState { routes: Arc::new(routes), + config, }; let compression_layer = CompressionLayer::new() @@ -72,11 +61,13 @@ pub async fn main( Ok(()) } -pub fn log_result(code: StatusCode, path: &str) { +pub fn log_result(code: StatusCode, path: &str, instant: Instant) { + let time = format!("{}µs", instant.elapsed().as_micros()); + let time = time.bright_black(); match code { - StatusCode::INTERNAL_SERVER_ERROR => error!("{} {}", code.as_u16().red(), path), - StatusCode::NOT_MODIFIED => info!("{} {}", code.as_u16().bright_black(), path), - StatusCode::OK => info!("{} {}", code.as_u16().green(), path), - _ => error!("{} {}", code.as_u16().red(), path), + StatusCode::INTERNAL_SERVER_ERROR => error!("{} {} {}", code.as_u16().red(), path, time), + StatusCode::NOT_MODIFIED => info!("{} {} {}", code.as_u16().bright_black(), path, time), + StatusCode::OK => info!("{} {} {}", code.as_u16().green(), path, time), + _ => error!("{} {} {}", code.as_u16().red(), path, time), } } diff --git a/src/server/website/handlers/file.rs b/src/server/website/handlers/file.rs index 3c6159a53..125e7136c 100644 --- a/src/server/website/handlers/file.rs +++ b/src/server/website/handlers/file.rs @@ -1,6 +1,7 @@ use std::{ fs::{self}, path::{Path, PathBuf}, + time::Instant, }; use axum::{ @@ -27,6 +28,8 @@ pub async fn index_handler(headers: HeaderMap) -> Response { } fn any_handler(headers: HeaderMap, path: Option>) -> Response { + let instant = Instant::now(); + let response = if let Some(path) = path.as_ref() { let path = path.0.replace("..", "").replace("\\", ""); @@ -56,6 +59,7 @@ fn any_handler(headers: HeaderMap, path: Option>) -> Respo log_result( response.status(), &format!("/{}", path.map_or("".to_owned(), |p| p.0)), + instant, ); response @@ -124,8 +128,6 @@ fn _path_to_response(headers: &HeaderMap, path: &Path) -> color_eyre::Result &Path; + fn path_parent(&self) -> &Path; fn path_last(&self) -> &Option; - fn last_value(&self) -> Option; - - fn t_name(&self) -> &str; - - fn get_paths_to_type(&self) -> Vec<(PathBuf, String)> { - let t_name = self.t_name().to_string(); - - if let Some(path_last) = self.path_last() { - vec![ - (self.path().to_owned(), t_name.clone()), - (path_last.unwrap().to_owned(), t_name), - ] - } else { - vec![(self.path().to_owned(), t_name)] - } - } - + fn serialization(&self) -> Serialization; + fn type_name(&self) -> &str; + fn key_name(&self) -> &str; fn pre_export(&mut self); fn export(&self) -> color_eyre::Result<()>; fn post_export(&mut self); - fn delete_files(&self); - fn kind(&self) -> MapKind; + fn id(&self, config: &Config) -> String; } diff --git a/src/structs/config.rs b/src/structs/config.rs index f10e3919c..f51af3a0d 100644 --- a/src/structs/config.rs +++ b/src/structs/config.rs @@ -10,6 +10,8 @@ use color_eyre::eyre::eyre; use log::info; use serde::{Deserialize, Serialize}; +use crate::io::JSON_EXTENSION; + use super::MapPath; #[derive(Parser, Debug, Clone, Default, Serialize, Deserialize, PartialEq)] @@ -74,6 +76,8 @@ pub struct Config { } impl Config { + pub const DATASET_DIR_NAME: &str = "datasets"; + pub fn import() -> color_eyre::Result { let path = Self::path_dot_kibo(); let _ = fs::create_dir_all(&path); @@ -285,7 +289,7 @@ impl Config { } pub fn path_datasets_last_values(&self) -> MapPath { - self.path_datasets().join("last.json") + self.path_datasets().join(&format!("last.{JSON_EXTENSION}")) } pub fn path_price(&self) -> MapPath { @@ -293,7 +297,7 @@ impl Config { } pub fn path_databases(&self) -> PathBuf { - self.path_kibodir().join("databases") + self.path_kibodir().join(Self::DATASET_DIR_NAME) } pub fn path_states(&self) -> PathBuf { diff --git a/src/structs/date_map.rs b/src/structs/date_map.rs index 37e853efe..54b97530c 100644 --- a/src/structs/date_map.rs +++ b/src/structs/date_map.rs @@ -1,10 +1,10 @@ use std::iter::Sum; use super::{ - AnyMap, Date, DateMapChunkId, GenericMap, Height, HeightMap, MapValue, SerializedBTreeMap, + AnyMap, Date, DateMapChunkId, GenericMap, Height, HeightMap, MapValue, SerializedDateMap, }; -pub type DateMap = GenericMap>; +pub type DateMap = GenericMap>; impl DateMap where diff --git a/src/structs/date_map_chunk_id.rs b/src/structs/date_map_chunk_id.rs index dbc8eb4fa..07554e24a 100644 --- a/src/structs/date_map_chunk_id.rs +++ b/src/structs/date_map_chunk_id.rs @@ -15,7 +15,7 @@ impl DateMapChunkId { } impl MapChunkId for DateMapChunkId { - fn to_name(&self) -> String { + fn to_string(&self) -> String { self.0.to_string() } @@ -39,4 +39,12 @@ impl MapChunkId for DateMapChunkId { fn from_usize(id: usize) -> Self { Self(id as i32) } + + fn next(&self) -> Option { + self.0.checked_add(1).map(Self) + } + + fn previous(&self) -> Option { + self.0.checked_sub(1).map(Self) + } } diff --git a/src/structs/generic_map.rs b/src/structs/generic_map.rs index 8c1e52244..6cdb099f6 100644 --- a/src/structs/generic_map.rs +++ b/src/structs/generic_map.rs @@ -1,6 +1,6 @@ use std::{ collections::{BTreeMap, VecDeque}, - fmt::Debug, + fmt::{Debug, Display}, fs, iter::Sum, mem, @@ -19,7 +19,7 @@ use crate::{ utils::{get_percentile, LossyFrom}, }; -use super::{AnyMap, MapPath, MapValue}; +use super::{AnyMap, Config, MapPath, MapValue}; #[derive(Debug, Clone, Copy, Allocative, PartialEq, Eq)] pub enum MapKind { @@ -29,7 +29,7 @@ pub enum MapKind { pub trait MapKey where - Self: Sized + PartialOrd + Ord + Clone + Copy + Debug, + Self: Sized + PartialOrd + Ord + Clone + Copy + Debug + Display, ChunkId: MapChunkId, { fn to_chunk_id(&self) -> ChunkId; @@ -61,16 +61,21 @@ where fn get(&self, serialized_key: &Key) -> Option<&Value>; fn last(&self) -> Option<&Value>; fn extend(&mut self, map: BTreeMap); + fn import_all(path: &Path, serialization: &Serialization) -> Self; + fn to_csv(self, id: &str) -> String; + fn map(&self) -> &impl Serialize; } pub trait MapChunkId where Self: Ord + Debug + Copy + Clone, { - fn to_name(&self) -> String; + fn to_string(&self) -> String; fn from_path(path: &Path) -> color_eyre::Result; fn to_usize(self) -> usize; fn from_usize(id: usize) -> Self; + fn previous(&self) -> Option; + fn next(&self) -> Option; } #[derive(Debug, Allocative)] @@ -79,6 +84,7 @@ pub struct GenericMap { kind: MapKind, path_all: MapPath, + path_parent: MapPath, path_last: Option, chunks_in_memory: usize, @@ -150,6 +156,7 @@ where kind, path_all, + path_parent: path.to_owned(), path_last, chunks_in_memory, @@ -177,15 +184,7 @@ where } }); - s.initial_last_key = s - .imported - .iter() - .last() - .and_then(|(last_chunk_id, serialized)| serialized.get_last_key(last_chunk_id)); - - s.initial_first_unsafe_key = s - .initial_last_key - .and_then(|last_key| last_key.to_first_unsafe()); + s.set_initial_keys(); // if s.initial_first_unsafe_key.is_none() { // log(&format!("Missing dataset: {path:?}/{}", Key::map_name())); @@ -194,6 +193,18 @@ where s } + fn set_initial_keys(&mut self) { + self.initial_last_key = self + .imported + .iter() + .last() + .and_then(|(last_chunk_id, serialized)| serialized.get_last_key(last_chunk_id)); + + self.initial_first_unsafe_key = self + .initial_last_key + .and_then(|last_key| last_key.to_first_unsafe()); + } + fn read_dir(&self) -> BTreeMap { Self::_read_dir(&self.path_all, &self.serialization) } @@ -308,10 +319,26 @@ where Key: MapKey, Serialized: MapSerialized, { + fn id(&self, config: &Config) -> String { + let path_to_string = |p: &Path| p.to_str().unwrap().to_owned(); + path_to_string(self.path_parent()) + .replace(&path_to_string(&config.path_kibodir()), "") + .replace(&format!("/{}/", Config::DATASET_DIR_NAME), "") + .replace("/", "-") + } + + fn serialization(&self) -> Serialization { + self.serialization + } + fn path(&self) -> &Path { &self.path_all } + fn path_parent(&self) -> &Path { + &self.path_parent + } + fn path_last(&self) -> &Option { &self.path_last } @@ -323,10 +350,14 @@ where .and_then(|v| serde_json::to_value(v).ok()) } - fn t_name(&self) -> &str { + fn type_name(&self) -> &str { std::any::type_name::() } + fn key_name(&self) -> &str { + Key::map_name() + } + fn pre_export(&mut self) { self.to_insert.iter_mut().for_each(|(chunk_id, map)| { if let Some((key, _)) = map.first_key_value() { @@ -351,6 +382,8 @@ where .or_insert(Serialized::new(self.version)) .extend(mem::take(map)); }); + + self.set_initial_keys(); } fn export(&self) -> color_eyre::Result<()> { @@ -367,7 +400,7 @@ where panic!(); }); - let path = self.path_all.join(&chunk_id.to_name()); + let path = self.path_all.join(&chunk_id.to_string()); self.serialization.export(&path, serialized)?; diff --git a/src/structs/height_map_chunk_id.rs b/src/structs/height_map_chunk_id.rs index 06d3856ce..bfe82058a 100644 --- a/src/structs/height_map_chunk_id.rs +++ b/src/structs/height_map_chunk_id.rs @@ -19,7 +19,7 @@ impl HeightMapChunkId { } impl MapChunkId for HeightMapChunkId { - fn to_name(&self) -> String { + fn to_string(&self) -> String { let start = ***self; let end = start + HEIGHT_MAP_CHUNK_SIZE; @@ -46,4 +46,14 @@ impl MapChunkId for HeightMapChunkId { fn from_usize(id: usize) -> Self { Self(Height::new(id as u32)) } + + fn next(&self) -> Option { + self.checked_add(HEIGHT_MAP_CHUNK_SIZE) + .map(|h| Self(Height::new(h))) + } + + fn previous(&self) -> Option { + self.checked_sub(HEIGHT_MAP_CHUNK_SIZE) + .map(|h| Self(Height::new(h))) + } } diff --git a/src/structs/map_value.rs b/src/structs/map_value.rs index f9790c72d..4f1dcf9fa 100644 --- a/src/structs/map_value.rs +++ b/src/structs/map_value.rs @@ -1,4 +1,4 @@ -use std::fmt::Debug; +use std::fmt::{Debug, Display}; use allocative::Allocative; use bincode::{Decode, Encode}; @@ -18,6 +18,7 @@ pub trait MapValue: + Sync + Send + Allocative + + Display { } diff --git a/src/structs/mod.rs b/src/structs/mod.rs index 0a7043ce3..f1f012319 100644 --- a/src/structs/mod.rs +++ b/src/structs/mod.rs @@ -72,7 +72,6 @@ pub use map_value::*; pub use ohlc::*; pub use partial_txout_data::*; pub use price::*; -pub use rpc::*; pub use sent_data::*; pub use serialized_btreemap::*; pub use serialized_vec::*; diff --git a/src/structs/ohlc.rs b/src/structs/ohlc.rs index 666a52b1f..16fc833a5 100644 --- a/src/structs/ohlc.rs +++ b/src/structs/ohlc.rs @@ -1,3 +1,5 @@ +use std::fmt::{self}; + use allocative::Allocative; use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; @@ -10,3 +12,13 @@ pub struct OHLC { pub low: f32, pub close: f32, } + +impl fmt::Display for OHLC { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{{ open: {}, high: {}, low: {}, close: {} }}", + self.open, self.high, self.low, self.close + ) + } +} diff --git a/src/structs/serialized_btreemap.rs b/src/structs/serialized_btreemap.rs index 97ec47ad5..e70fb953d 100644 --- a/src/structs/serialized_btreemap.rs +++ b/src/structs/serialized_btreemap.rs @@ -6,7 +6,9 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::io::Serialization; -use super::{DateMap, MapChunkId, MapKey, MapSerialized, MapValue}; +use super::{Date, DateMap, MapChunkId, MapKey, MapSerialized, MapValue}; + +pub type SerializedDateMap = SerializedBTreeMap; #[derive(Debug, Default, Serialize, Deserialize, Encode, Decode, Allocative)] pub struct SerializedBTreeMap @@ -17,41 +19,11 @@ where pub map: BTreeMap, } -impl SerializedBTreeMap -where - Key: Ord, -{ - pub fn import_all(path: &Path, serialization: &Serialization) -> Self - where - Self: Debug + Serialize + DeserializeOwned + Encode + Decode, - ChunkId: MapChunkId, - Key: MapKey, - Value: MapValue, - { - let mut s = None; - - DateMap::::_read_dir(path, serialization) - .iter() - .for_each(|(_, path)| { - let map = serialization.import::(path).unwrap(); - - if s.is_none() { - s.replace(map); - } else { - #[allow(clippy::unnecessary_unwrap)] - s.as_mut().unwrap().map.extend(map.map); - } - }); - - s.unwrap() - } -} - impl MapSerialized for SerializedBTreeMap where Self: Debug + Serialize + DeserializeOwned + Encode + Decode, ChunkId: MapChunkId, - Key: MapKey, + Key: MapKey + Serialize, Value: MapValue, { fn new(version: u32) -> Self { @@ -80,4 +52,35 @@ where fn extend(&mut self, map: BTreeMap) { self.map.extend(map) } + + fn import_all(path: &Path, serialization: &Serialization) -> Self { + let mut s = None; + + DateMap::::_read_dir(path, serialization) + .iter() + .for_each(|(_, path)| { + let map = serialization.import::(path).unwrap(); + + if s.is_none() { + s.replace(map); + } else { + #[allow(clippy::unnecessary_unwrap)] + s.as_mut().unwrap().map.extend(map.map); + } + }); + + s.unwrap() + } + + fn to_csv(self, id: &str) -> String { + let mut csv = format!("{},{}\n", Key::map_name(), id); + self.map.iter().for_each(|(k, v)| { + csv += &format!("{},{}\n", k, v); + }); + csv + } + + fn map(&self) -> &impl Serialize { + &self.map + } } diff --git a/src/structs/serialized_vec.rs b/src/structs/serialized_vec.rs index aa02f51d3..f8dac4c1b 100644 --- a/src/structs/serialized_vec.rs +++ b/src/structs/serialized_vec.rs @@ -14,31 +14,6 @@ pub struct SerializedVec { pub map: Vec, } -impl SerializedVec { - pub fn import_all(path: &Path, serialization: &Serialization) -> Self - where - Self: Debug + Serialize + DeserializeOwned + Encode + Decode, - Value: MapValue, - { - let mut s = None; - - HeightMap::::_read_dir(path, serialization) - .iter() - .for_each(|(_, path)| { - let mut map = serialization.import::(path).unwrap(); - - if s.is_none() { - s.replace(map); - } else { - #[allow(clippy::unnecessary_unwrap)] - s.as_mut().unwrap().map.append(&mut map.map); - } - }); - - s.unwrap() - } -} - impl MapSerialized for SerializedVec where Self: Debug + Serialize + DeserializeOwned + Encode + Decode, @@ -87,4 +62,39 @@ where } }); } + + fn import_all(path: &Path, serialization: &Serialization) -> Self + where + Self: Debug + Serialize + DeserializeOwned + Encode + Decode, + Value: MapValue, + { + let mut s = None; + + HeightMap::::_read_dir(path, serialization) + .iter() + .for_each(|(_, path)| { + let mut map = serialization.import::(path).unwrap(); + + if s.is_none() { + s.replace(map); + } else { + #[allow(clippy::unnecessary_unwrap)] + s.as_mut().unwrap().map.append(&mut map.map); + } + }); + + s.unwrap() + } + + fn to_csv(self, id: &str) -> String { + let mut csv = format!("{},{}\n", Key::map_name(), id); + self.map.iter().enumerate().for_each(|(k, v)| { + csv += &format!("{:?},{:?}\n", k, v); + }); + csv + } + + fn map(&self) -> &impl Serialize { + &self.map + } } diff --git a/src/structs/timestamp.rs b/src/structs/timestamp.rs index 6e8455a23..e1ee2efeb 100644 --- a/src/structs/timestamp.rs +++ b/src/structs/timestamp.rs @@ -1,4 +1,4 @@ -use std::ops::Sub; +use std::{fmt, ops::Sub}; use allocative::Allocative; use bincode::{Decode, Encode}; @@ -81,3 +81,9 @@ impl Sub for Timestamp { Self::wrap(self.0 - rhs.0) } } + +impl fmt::Display for Timestamp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", **self) + } +} From 8a733ee337fd43f4c0a28d1d6de88acb7a6ed0c4 Mon Sep 17 00:00:00 2001 From: nym21 Date: Sun, 22 Dec 2024 09:39:27 +0100 Subject: [PATCH 10/63] changelog: update --- CHANGELOG.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c15669ab..46d6696cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,17 @@ ## Global -- Merged parser and server crates into a single project (and thus executable), so now both will run at the same time with a single `cargo run -r` +- Merged parser and server crates into a single project (and thus executable), so now both will run at the same time with a single `cargo run -r` [#7392982](https://github.com/kibo-money/kibo/commit/7392982824c2db94bcd57251fd41986117c29a23) - Added `--no-server` and `--no-parser` to disable each if needed - Improved executable parameters -- Started using `log` and `env_logger` crates instead of custom code +- Started using `log` and `env_logger` crates instead of custom code [#7392982](https://github.com/kibo-money/kibo/commit/7392982824c2db94bcd57251fd41986117c29a23) - Improved logs -- Automated databases defragmention (and removed parameter) to improve disk usage and parse speed -- Fixed input being unfocused right after being focused in Brave browser -- Moved Sanakirja database wrapper to its own crate: `snkrj` +- Automated databases defragmention (and removed parameter) to improve disk usage and parse speed [#7392982](https://github.com/kibo-money/kibo/commit/7392982824c2db94bcd57251fd41986117c29a23) +- Fixed input being unfocused right after being focused in Brave browser [#9a9ae61](https://github.com/kibo-money/kibo/commit/9a9ae614d07b54c08b7e9c0e2aefe3b52fdb93c5) +- Moved Sanakirja database wrapper to its own crate: `snkrj` [0f936dc](https://github.com/kibo-money/kibo/commit/0f936dcdfbecac0a228f023bd500d6e98f56446a) +- Reworked server's API code [#6ab0f46]( https://github.com/kibo-money/kibo/commit/6ab0f463119a902a1b7ca9691b54f61543bb8f2f) + - New route format: `/api/date-to-realized-price` is now `/api/realized-price?kind=date` + - Added status and timing to logs ## Git From 2b017ac6b5cf24ade3fc88eb475c02078d3d5297 Mon Sep 17 00:00:00 2001 From: nym21 Date: Sun, 22 Dec 2024 10:22:55 +0100 Subject: [PATCH 11/63] website: lc: update to v4.2.2 --- .../packages/lightweight-charts/types.d.ts | 3 +- .../lightweight-charts/v4.2.2/script.js | 9 + .../lightweight-charts/v4.2.2/types.d.ts | 3882 +++++++++++++++++ .../packages/lightweight-charts/wrapper.js | 4 +- src/website/scripts/main.js | 4 +- src/website/scripts/service-worker.js | 2 +- src/website/scripts/types/self.d.ts | 2 +- 7 files changed, 3898 insertions(+), 8 deletions(-) create mode 100644 src/website/packages/lightweight-charts/v4.2.2/script.js create mode 100644 src/website/packages/lightweight-charts/v4.2.2/types.d.ts diff --git a/src/website/packages/lightweight-charts/types.d.ts b/src/website/packages/lightweight-charts/types.d.ts index 02c5ab127..ca3fb9e42 100644 --- a/src/website/packages/lightweight-charts/types.d.ts +++ b/src/website/packages/lightweight-charts/types.d.ts @@ -1,6 +1,5 @@ import { Signal } from "../solid-signals/types"; import { Accessor } from "../solid-signals/2024-11-02/types/signals"; -import { Owner } from "../solid-signals/2024-11-02/types/core/owner"; import { DeepPartial, BaselineStyleOptions, @@ -11,7 +10,7 @@ import { CandlestickData, ISeriesApi, BaselineData, -} from "./v4.2.0/types"; +} from "./v4.2.2/types"; import { Color, Valued, ValuedCandlestickData } from "../../scripts/types/self"; interface BaseSeriesBlueprint { diff --git a/src/website/packages/lightweight-charts/v4.2.2/script.js b/src/website/packages/lightweight-charts/v4.2.2/script.js new file mode 100644 index 000000000..3c5693745 --- /dev/null +++ b/src/website/packages/lightweight-charts/v4.2.2/script.js @@ -0,0 +1,9 @@ +// @ts-nocheck + +/*! + * @license + * TradingView Lightweight Charts™ v4.2.2 + * Copyright (c) 2024 TradingView, Inc. + * Licensed under Apache License 2.0 https://www.apache.org/licenses/LICENSE-2.0 + */ +const t={upColor:"#26a69a",downColor:"#ef5350",wickVisible:!0,borderVisible:!0,borderColor:"#378658",borderUpColor:"#26a69a",borderDownColor:"#ef5350",wickColor:"#737375",wickUpColor:"#26a69a",wickDownColor:"#ef5350"},i={upColor:"#26a69a",downColor:"#ef5350",openVisible:!0,thinBars:!0},n={color:"#2196f3",lineStyle:0,lineWidth:3,lineType:0,lineVisible:!0,crosshairMarkerVisible:!0,crosshairMarkerRadius:4,crosshairMarkerBorderColor:"",crosshairMarkerBorderWidth:2,crosshairMarkerBackgroundColor:"",lastPriceAnimation:0,pointMarkersVisible:!1},s={topColor:"rgba( 46, 220, 135, 0.4)",bottomColor:"rgba( 40, 221, 100, 0)",invertFilledArea:!1,lineColor:"#33D778",lineStyle:0,lineWidth:3,lineType:0,lineVisible:!0,crosshairMarkerVisible:!0,crosshairMarkerRadius:4,crosshairMarkerBorderColor:"",crosshairMarkerBorderWidth:2,crosshairMarkerBackgroundColor:"",lastPriceAnimation:0,pointMarkersVisible:!1},e={baseValue:{type:"price",price:0},topFillColor1:"rgba(38, 166, 154, 0.28)",topFillColor2:"rgba(38, 166, 154, 0.05)",topLineColor:"rgba(38, 166, 154, 1)",bottomFillColor1:"rgba(239, 83, 80, 0.05)",bottomFillColor2:"rgba(239, 83, 80, 0.28)",bottomLineColor:"rgba(239, 83, 80, 1)",lineWidth:3,lineStyle:0,lineType:0,lineVisible:!0,crosshairMarkerVisible:!0,crosshairMarkerRadius:4,crosshairMarkerBorderColor:"",crosshairMarkerBorderWidth:2,crosshairMarkerBackgroundColor:"",lastPriceAnimation:0,pointMarkersVisible:!1},r={color:"#26a69a",base:0},h={color:"#2196f3"},l={title:"",visible:!0,lastValueVisible:!0,priceLineVisible:!0,priceLineSource:0,priceLineWidth:1,priceLineColor:"",priceLineStyle:2,baseLineVisible:!0,baseLineWidth:1,baseLineColor:"#B2B5BE",baseLineStyle:0,priceFormat:{type:"price",precision:2,minMove:.01}};var a,o;function _(t,i){const n={0:[],1:[t.lineWidth,t.lineWidth],2:[2*t.lineWidth,2*t.lineWidth],3:[6*t.lineWidth,6*t.lineWidth],4:[t.lineWidth,4*t.lineWidth]}[i];t.setLineDash(n)}function u(t,i,n,s){t.beginPath();const e=t.lineWidth%2?.5:0;t.moveTo(n,i+e),t.lineTo(s,i+e),t.stroke()}function c(t,i){if(!t)throw new Error("Assertion failed"+(i?": "+i:""))}function d(t){if(void 0===t)throw new Error("Value is undefined");return t}function f(t){if(null===t)throw new Error("Value is null");return t}function v(t){return f(d(t))}!function(t){t[t.Simple=0]="Simple",t[t.WithSteps=1]="WithSteps",t[t.Curved=2]="Curved"}(a||(a={})),function(t){t[t.Solid=0]="Solid",t[t.Dotted=1]="Dotted",t[t.Dashed=2]="Dashed",t[t.LargeDashed=3]="LargeDashed",t[t.SparseDotted=4]="SparseDotted"}(o||(o={}));const p={khaki:"#f0e68c",azure:"#f0ffff",aliceblue:"#f0f8ff",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gainsboro:"#dcdcdc",gray:"#808080",green:"#008000",honeydew:"#f0fff0",floralwhite:"#fffaf0",lightblue:"#add8e6",lightcoral:"#f08080",lemonchiffon:"#fffacd",hotpink:"#ff69b4",lightyellow:"#ffffe0",greenyellow:"#adff2f",lightgoldenrodyellow:"#fafad2",limegreen:"#32cd32",linen:"#faf0e6",lightcyan:"#e0ffff",magenta:"#f0f",maroon:"#800000",olive:"#808000",orange:"#ffa500",oldlace:"#fdf5e6",mediumblue:"#0000cd",transparent:"#0000",lime:"#0f0",lightpink:"#ffb6c1",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",midnightblue:"#191970",orchid:"#da70d6",mediumorchid:"#ba55d3",mediumturquoise:"#48d1cc",orangered:"#ff4500",royalblue:"#4169e1",powderblue:"#b0e0e6",red:"#f00",coral:"#ff7f50",turquoise:"#40e0d0",white:"#fff",whitesmoke:"#f5f5f5",wheat:"#f5deb3",teal:"#008080",steelblue:"#4682b4",bisque:"#ffe4c4",aquamarine:"#7fffd4",aqua:"#0ff",sienna:"#a0522d",silver:"#c0c0c0",springgreen:"#00ff7f",antiquewhite:"#faebd7",burlywood:"#deb887",brown:"#a52a2a",beige:"#f5f5dc",chocolate:"#d2691e",chartreuse:"#7fff00",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cadetblue:"#5f9ea0",tomato:"#ff6347",fuchsia:"#f0f",blue:"#00f",salmon:"#fa8072",blanchedalmond:"#ffebcd",slateblue:"#6a5acd",slategray:"#708090",thistle:"#d8bfd8",tan:"#d2b48c",cyan:"#0ff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",blueviolet:"#8a2be2",black:"#000",darkmagenta:"#8b008b",darkslateblue:"#483d8b",darkkhaki:"#bdb76b",darkorchid:"#9932cc",darkorange:"#ff8c00",darkgreen:"#006400",darkred:"#8b0000",dodgerblue:"#1e90ff",darkslategray:"#2f4f4f",dimgray:"#696969",deepskyblue:"#00bfff",firebrick:"#b22222",forestgreen:"#228b22",indigo:"#4b0082",ivory:"#fffff0",lavenderblush:"#fff0f5",feldspar:"#d19275",indianred:"#cd5c5c",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightskyblue:"#87cefa",lightslategray:"#789",lightslateblue:"#8470ff",snow:"#fffafa",lightseagreen:"#20b2aa",lightsalmon:"#ffa07a",darksalmon:"#e9967a",darkviolet:"#9400d3",mediumpurple:"#9370d8",mediumaquamarine:"#66cdaa",skyblue:"#87ceeb",lavender:"#e6e6fa",lightsteelblue:"#b0c4de",mediumvioletred:"#c71585",mintcream:"#f5fffa",navajowhite:"#ffdead",navy:"#000080",olivedrab:"#6b8e23",palevioletred:"#d87093",violetred:"#d02090",yellow:"#ff0",yellowgreen:"#9acd32",lawngreen:"#7cfc00",pink:"#ffc0cb",paleturquoise:"#afeeee",palegoldenrod:"#eee8aa",darkolivegreen:"#556b2f",darkseagreen:"#8fbc8f",darkturquoise:"#00ced1",peachpuff:"#ffdab9",deeppink:"#ff1493",violet:"#ee82ee",palegreen:"#98fb98",mediumseagreen:"#3cb371",peru:"#cd853f",saddlebrown:"#8b4513",sandybrown:"#f4a460",rosybrown:"#bc8f8f",purple:"#800080",seagreen:"#2e8b57",seashell:"#fff5ee",papayawhip:"#ffefd5",mediumslateblue:"#7b68ee",plum:"#dda0dd",mediumspringgreen:"#00fa9a"};function m(t){return t<0?0:t>255?255:Math.round(t)||0}function b(t){return t<=0||t>1?Math.min(Math.max(t,0),1):Math.round(1e4*t)/1e4}const w=/^#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?$/i,g=/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i,M=/^rgb\(\s*(-?\d{1,10})\s*,\s*(-?\d{1,10})\s*,\s*(-?\d{1,10})\s*\)$/,x=/^rgba\(\s*(-?\d{1,10})\s*,\s*(-?\d{1,10})\s*,\s*(-?\d{1,10})\s*,\s*(-?\d*\.?\d+)\s*\)$/;function S(t){(t=t.toLowerCase())in p&&(t=p[t]);{const i=x.exec(t)||M.exec(t);if(i)return[m(parseInt(i[1],10)),m(parseInt(i[2],10)),m(parseInt(i[3],10)),b(i.length<5?1:parseFloat(i[4]))]}{const i=g.exec(t);if(i)return[m(parseInt(i[1],16)),m(parseInt(i[2],16)),m(parseInt(i[3],16)),1]}{const i=w.exec(t);if(i)return[m(17*parseInt(i[1],16)),m(17*parseInt(i[2],16)),m(17*parseInt(i[3],16)),1]}throw new Error(`Cannot parse color: ${t}`)}function k(t){return.199*t[0]+.687*t[1]+.114*t[2]}function y(t){const i=S(t);return{t:`rgb(${i[0]}, ${i[1]}, ${i[2]})`,i:k(i)>160?"black":"white"}}class C{constructor(){this.h=[]}l(t,i,n){const s={o:t,_:i,u:!0===n};this.h.push(s)}v(t){const i=this.h.findIndex((i=>t===i.o));i>-1&&this.h.splice(i,1)}p(t){this.h=this.h.filter((i=>i._!==t))}m(t,i,n){const s=[...this.h];this.h=this.h.filter((t=>!t.u)),s.forEach((s=>s.o(t,i,n)))}M(){return this.h.length>0}S(){this.h=[]}}function T(t,...i){for(const n of i)for(const i in n)void 0!==n[i]&&("object"!=typeof n[i]||void 0===t[i]||Array.isArray(n[i])?t[i]=n[i]:T(t[i],n[i]));return t}function P(t){return"number"==typeof t&&isFinite(t)}function R(t){return"number"==typeof t&&t%1==0}function D(t){return"string"==typeof t}function O(t){return"boolean"==typeof t}function V(t){const i=t;if(!i||"object"!=typeof i)return i;let n,s,e;for(s in n=Array.isArray(i)?[]:{},i)i.hasOwnProperty(s)&&(e=i[s],n[s]=e&&"object"==typeof e?V(e):e);return n}function B(t){return null!==t}function A(t){return null===t?void 0:t}const z="-apple-system, BlinkMacSystemFont, 'Trebuchet MS', Roboto, Ubuntu, sans-serif";function E(t,i,n){return void 0===i&&(i=z),`${n=void 0!==n?`${n} `:""}${t}px ${i}`}class I{constructor(t){this.k={C:1,T:5,P:NaN,R:"",D:"",O:"",V:"",B:0,A:0,I:0,L:0,N:0},this.F=t}W(){const t=this.k,i=this.j(),n=this.H();return t.P===i&&t.D===n||(t.P=i,t.D=n,t.R=E(i,n),t.L=2.5/12*i,t.B=t.L,t.A=i/12*t.T,t.I=i/12*t.T,t.N=0),t.O=this.$(),t.V=this.U(),this.k}$(){return this.F.W().layout.textColor}U(){return this.F.q()}j(){return this.F.W().layout.fontSize}H(){return this.F.W().layout.fontFamily}}class L{constructor(){this.Y=[]}Z(t){this.Y=t}X(t,i,n){this.Y.forEach((s=>{s.X(t,i,n)}))}}class N{X(t,i,n){t.useBitmapCoordinateSpace((t=>this.K(t,i,n)))}}class F extends N{constructor(){super(...arguments),this.G=null}J(t){this.G=t}K({context:t,horizontalPixelRatio:i,verticalPixelRatio:n}){if(null===this.G||null===this.G.tt)return;const s=this.G.tt,e=this.G,r=Math.max(1,Math.floor(i))%2/2,h=h=>{t.beginPath();for(let l=s.to-1;l>=s.from;--l){const s=e.it[l],a=Math.round(s.nt*i)+r,o=s.st*n,_=h*n+r;t.moveTo(a,o),t.arc(a,o,_,0,2*Math.PI)}t.fill()};e.et>0&&(t.fillStyle=e.rt,h(e.ht+e.et)),t.fillStyle=e.lt,h(e.ht)}}function W(){return{it:[{nt:0,st:0,ot:0,_t:0}],lt:"",rt:"",ht:0,et:0,tt:null}}const j={from:0,to:1};class H{constructor(t,i){this.ut=new L,this.ct=[],this.dt=[],this.ft=!0,this.F=t,this.vt=i,this.ut.Z(this.ct)}bt(t){const i=this.F.wt();i.length!==this.ct.length&&(this.dt=i.map(W),this.ct=this.dt.map((t=>{const i=new F;return i.J(t),i})),this.ut.Z(this.ct)),this.ft=!0}gt(){return this.ft&&(this.Mt(),this.ft=!1),this.ut}Mt(){const t=2===this.vt.W().mode,i=this.F.wt(),n=this.vt.xt(),s=this.F.St();i.forEach(((i,e)=>{var r;const h=this.dt[e],l=i.kt(n);if(t||null===l||!i.yt())return void(h.tt=null);const a=f(i.Ct());h.lt=l.Tt,h.ht=l.ht,h.et=l.Pt,h.it[0]._t=l._t,h.it[0].st=i.Dt().Rt(l._t,a.Ot),h.rt=null!==(r=l.Vt)&&void 0!==r?r:this.F.Bt(h.it[0].st/i.Dt().At()),h.it[0].ot=n,h.it[0].nt=s.zt(n),h.tt=j}))}}class $ extends N{constructor(t){super(),this.Et=t}K({context:t,bitmapSize:i,horizontalPixelRatio:n,verticalPixelRatio:s}){if(null===this.Et)return;const e=this.Et.It.yt,r=this.Et.Lt.yt;if(!e&&!r)return;const h=Math.round(this.Et.nt*n),l=Math.round(this.Et.st*s);t.lineCap="butt",e&&h>=0&&(t.lineWidth=Math.floor(this.Et.It.et*n),t.strokeStyle=this.Et.It.O,t.fillStyle=this.Et.It.O,_(t,this.Et.It.Nt),function(t,i,n,s){t.beginPath();const e=t.lineWidth%2?.5:0;t.moveTo(i+e,n),t.lineTo(i+e,s),t.stroke()}(t,h,0,i.height)),r&&l>=0&&(t.lineWidth=Math.floor(this.Et.Lt.et*s),t.strokeStyle=this.Et.Lt.O,t.fillStyle=this.Et.Lt.O,_(t,this.Et.Lt.Nt),u(t,l,0,i.width))}}class U{constructor(t){this.ft=!0,this.Ft={It:{et:1,Nt:0,O:"",yt:!1},Lt:{et:1,Nt:0,O:"",yt:!1},nt:0,st:0},this.Wt=new $(this.Ft),this.jt=t}bt(){this.ft=!0}gt(){return this.ft&&(this.Mt(),this.ft=!1),this.Wt}Mt(){const t=this.jt.yt(),i=f(this.jt.Ht()),n=i.$t().W().crosshair,s=this.Ft;if(2===n.mode)return s.Lt.yt=!1,void(s.It.yt=!1);s.Lt.yt=t&&this.jt.Ut(i),s.It.yt=t&&this.jt.qt(),s.Lt.et=n.horzLine.width,s.Lt.Nt=n.horzLine.style,s.Lt.O=n.horzLine.color,s.It.et=n.vertLine.width,s.It.Nt=n.vertLine.style,s.It.O=n.vertLine.color,s.nt=this.jt.Yt(),s.st=this.jt.Zt()}}function q(t,i,n,s,e,r){t.fillRect(i+r,n,s-2*r,r),t.fillRect(i+r,n+e-r,s-2*r,r),t.fillRect(i,n,r,e),t.fillRect(i+s-r,n,r,e)}function Y(t,i,n,s,e,r){t.save(),t.globalCompositeOperation="copy",t.fillStyle=r,t.fillRect(i,n,s,e),t.restore()}function Z(t,i,n,s,e,r){t.beginPath(),t.roundRect?t.roundRect(i,n,s,e,r):(t.lineTo(i+s-r[1],n),0!==r[1]&&t.arcTo(i+s,n,i+s,n+r[1],r[1]),t.lineTo(i+s,n+e-r[2]),0!==r[2]&&t.arcTo(i+s,n+e,i+s-r[2],n+e,r[2]),t.lineTo(i+r[3],n+e),0!==r[3]&&t.arcTo(i,n+e,i,n+e-r[3],r[3]),t.lineTo(i,n+r[0]),0!==r[0]&&t.arcTo(i,n,i+r[0],n,r[0]))}function X(t,i,n,s,e,r,h=0,l=[0,0,0,0],a=""){if(t.save(),!h||!a||a===r)return Z(t,i,n,s,e,l),t.fillStyle=r,t.fill(),void t.restore();const o=h/2;var _;Z(t,i+o,n+o,s-h,e-h,(_=-o,l.map((t=>0===t?t:t+_)))),"transparent"!==r&&(t.fillStyle=r,t.fill()),"transparent"!==a&&(t.lineWidth=h,t.strokeStyle=a,t.closePath(),t.stroke()),t.restore()}function K(t,i,n,s,e,r,h){t.save(),t.globalCompositeOperation="copy";const l=t.createLinearGradient(0,0,0,e);l.addColorStop(0,r),l.addColorStop(1,h),t.fillStyle=l,t.fillRect(i,n,s,e),t.restore()}class G{constructor(t,i){this.J(t,i)}J(t,i){this.Et=t,this.Xt=i}At(t,i){return this.Et.yt?t.P+t.L+t.B:0}X(t,i,n,s){if(!this.Et.yt||0===this.Et.Kt.length)return;const e=this.Et.O,r=this.Xt.t,h=t.useBitmapCoordinateSpace((t=>{const h=t.context;h.font=i.R;const l=this.Gt(t,i,n,s),a=l.Jt;return l.Qt?X(h,a.ti,a.ii,a.ni,a.si,r,a.ei,[a.ht,0,0,a.ht],r):X(h,a.ri,a.ii,a.ni,a.si,r,a.ei,[0,a.ht,a.ht,0],r),this.Et.hi&&(h.fillStyle=e,h.fillRect(a.ri,a.li,a.ai-a.ri,a.oi)),this.Et._i&&(h.fillStyle=i.V,h.fillRect(l.Qt?a.ui-a.ei:0,a.ii,a.ei,a.ci-a.ii)),l}));t.useMediaCoordinateSpace((({context:t})=>{const n=h.di;t.font=i.R,t.textAlign=h.Qt?"right":"left",t.textBaseline="middle",t.fillStyle=e,t.fillText(this.Et.Kt,n.fi,(n.ii+n.ci)/2+n.pi)}))}Gt(t,i,n,s){var e;const{context:r,bitmapSize:h,mediaSize:l,horizontalPixelRatio:a,verticalPixelRatio:o}=t,_=this.Et.hi||!this.Et.mi?i.T:0,u=this.Et.bi?i.C:0,c=i.L+this.Xt.wi,d=i.B+this.Xt.gi,f=i.A,v=i.I,p=this.Et.Kt,m=i.P,b=n.Mi(r,p),w=Math.ceil(n.xi(r,p)),g=m+c+d,M=i.C+f+v+w+_,x=Math.max(1,Math.floor(o));let S=Math.round(g*o);S%2!=x%2&&(S+=1);const k=u>0?Math.max(1,Math.floor(u*a)):0,y=Math.round(M*a),C=Math.round(_*a),T=null!==(e=this.Xt.Si)&&void 0!==e?e:this.Xt.ki,P=Math.round(T*o)-Math.floor(.5*o),R=Math.floor(P+x/2-S/2),D=R+S,O="right"===s,V=O?l.width-u:u,B=O?h.width-k:k;let A,z,E;return O?(A=B-y,z=B-C,E=V-_-f-u):(A=B+y,z=B+C,E=V+_+f),{Qt:O,Jt:{ii:R,li:P,ci:D,ni:y,si:S,ht:2*a,ei:k,ti:A,ri:B,ai:z,oi:x,ui:h.width},di:{ii:R/o,ci:D/o,fi:E,pi:b}}}}class J{constructor(t){this.yi={ki:0,t:"#000",gi:0,wi:0},this.Ci={Kt:"",yt:!1,hi:!0,mi:!1,Vt:"",O:"#FFF",_i:!1,bi:!1},this.Ti={Kt:"",yt:!1,hi:!1,mi:!0,Vt:"",O:"#FFF",_i:!0,bi:!0},this.ft=!0,this.Pi=new(t||G)(this.Ci,this.yi),this.Ri=new(t||G)(this.Ti,this.yi)}Kt(){return this.Di(),this.Ci.Kt}ki(){return this.Di(),this.yi.ki}bt(){this.ft=!0}At(t,i=!1){return Math.max(this.Pi.At(t,i),this.Ri.At(t,i))}Oi(){return this.yi.Si||0}Vi(t){this.yi.Si=t}Bi(){return this.Di(),this.Ci.yt||this.Ti.yt}Ai(){return this.Di(),this.Ci.yt}gt(t){return this.Di(),this.Ci.hi=this.Ci.hi&&t.W().ticksVisible,this.Ti.hi=this.Ti.hi&&t.W().ticksVisible,this.Pi.J(this.Ci,this.yi),this.Ri.J(this.Ti,this.yi),this.Pi}zi(){return this.Di(),this.Pi.J(this.Ci,this.yi),this.Ri.J(this.Ti,this.yi),this.Ri}Di(){this.ft&&(this.Ci.hi=!0,this.Ti.hi=!1,this.Ei(this.Ci,this.Ti,this.yi))}}class Q extends J{constructor(t,i,n){super(),this.jt=t,this.Ii=i,this.Li=n}Ei(t,i,n){if(t.yt=!1,2===this.jt.W().mode)return;const s=this.jt.W().horzLine;if(!s.labelVisible)return;const e=this.Ii.Ct();if(!this.jt.yt()||this.Ii.Ni()||null===e)return;const r=y(s.labelBackgroundColor);n.t=r.t,t.O=r.i;const h=2/12*this.Ii.P();n.wi=h,n.gi=h;const l=this.Li(this.Ii);n.ki=l.ki,t.Kt=this.Ii.Fi(l._t,e),t.yt=!0}}const tt=/[1-9]/g;class it{constructor(){this.Et=null}J(t){this.Et=t}X(t,i){if(null===this.Et||!1===this.Et.yt||0===this.Et.Kt.length)return;const n=t.useMediaCoordinateSpace((({context:t})=>(t.font=i.R,Math.round(i.Wi.xi(t,f(this.Et).Kt,tt)))));if(n<=0)return;const s=i.ji,e=n+2*s,r=e/2,h=this.Et.Hi;let l=this.Et.ki,a=Math.floor(l-r)+.5;a<0?(l+=Math.abs(0-a),a=Math.floor(l-r)+.5):a+e>h&&(l-=Math.abs(h-(a+e)),a=Math.floor(l-r)+.5);const o=a+e,_=Math.ceil(0+i.C+i.T+i.L+i.P+i.B);t.useBitmapCoordinateSpace((({context:t,horizontalPixelRatio:n,verticalPixelRatio:s})=>{const e=f(this.Et);t.fillStyle=e.t;const r=Math.round(a*n),h=Math.round(0*s),l=Math.round(o*n),u=Math.round(_*s),c=Math.round(2*n);if(t.beginPath(),t.moveTo(r,h),t.lineTo(r,u-c),t.arcTo(r,u,r+c,u,c),t.lineTo(l-c,u),t.arcTo(l,u,l,u-c,c),t.lineTo(l,h),t.fill(),e.hi){const r=Math.round(e.ki*n),l=h,a=Math.round((l+i.T)*s);t.fillStyle=e.O;const o=Math.max(1,Math.floor(n)),_=Math.floor(.5*n);t.fillRect(r-_,l,o,a-l)}})),t.useMediaCoordinateSpace((({context:t})=>{const n=f(this.Et),e=0+i.C+i.T+i.L+i.P/2;t.font=i.R,t.textAlign="left",t.textBaseline="middle",t.fillStyle=n.O;const r=i.Wi.Mi(t,"Apr0");t.translate(a+s,e+r),t.fillText(n.Kt,0,0)}))}}class nt{constructor(t,i,n){this.ft=!0,this.Wt=new it,this.Ft={yt:!1,t:"#4c525e",O:"white",Kt:"",Hi:0,ki:NaN,hi:!0},this.vt=t,this.$i=i,this.Li=n}bt(){this.ft=!0}gt(){return this.ft&&(this.Mt(),this.ft=!1),this.Wt.J(this.Ft),this.Wt}Mt(){const t=this.Ft;if(t.yt=!1,2===this.vt.W().mode)return;const i=this.vt.W().vertLine;if(!i.labelVisible)return;const n=this.$i.St();if(n.Ni())return;t.Hi=n.Hi();const s=this.Li();if(null===s)return;t.ki=s.ki;const e=n.Ui(this.vt.xt());t.Kt=n.qi(f(e)),t.yt=!0;const r=y(i.labelBackgroundColor);t.t=r.t,t.O=r.i,t.hi=n.W().ticksVisible}}class st{constructor(){this.Yi=null,this.Zi=0}Xi(){return this.Zi}Ki(t){this.Zi=t}Dt(){return this.Yi}Gi(t){this.Yi=t}Ji(t){return[]}Qi(){return[]}yt(){return!0}}var et;!function(t){t[t.Normal=0]="Normal",t[t.Magnet=1]="Magnet",t[t.Hidden=2]="Hidden"}(et||(et={}));class rt extends st{constructor(t,i){super(),this.tn=null,this.nn=NaN,this.sn=0,this.en=!0,this.rn=new Map,this.hn=!1,this.ln=NaN,this.an=NaN,this._n=NaN,this.un=NaN,this.$i=t,this.cn=i,this.dn=new H(t,this);this.fn=((t,i)=>n=>{const s=i(),e=t();if(n===f(this.tn).vn())return{_t:e,ki:s};{const t=f(n.Ct());return{_t:n.pn(s,t),ki:s}}})((()=>this.nn),(()=>this.an));const n=((t,i)=>()=>{const n=this.$i.St().mn(t()),s=i();return n&&Number.isFinite(s)?{ot:n,ki:s}:null})((()=>this.sn),(()=>this.Yt()));this.bn=new nt(this,t,n),this.wn=new U(this)}W(){return this.cn}gn(t,i){this._n=t,this.un=i}Mn(){this._n=NaN,this.un=NaN}xn(){return this._n}Sn(){return this.un}kn(t,i,n){this.hn||(this.hn=!0),this.en=!0,this.yn(t,i,n)}xt(){return this.sn}Yt(){return this.ln}Zt(){return this.an}yt(){return this.en}Cn(){this.en=!1,this.Tn(),this.nn=NaN,this.ln=NaN,this.an=NaN,this.tn=null,this.Mn()}Pn(t){return null!==this.tn?[this.wn,this.dn]:[]}Ut(t){return t===this.tn&&this.cn.horzLine.visible}qt(){return this.cn.vertLine.visible}Rn(t,i){this.en&&this.tn===t||this.rn.clear();const n=[];return this.tn===t&&n.push(this.Dn(this.rn,i,this.fn)),n}Qi(){return this.en?[this.bn]:[]}Ht(){return this.tn}On(){this.wn.bt(),this.rn.forEach((t=>t.bt())),this.bn.bt(),this.dn.bt()}Vn(t){return t&&!t.vn().Ni()?t.vn():null}yn(t,i,n){this.Bn(t,i,n)&&this.On()}Bn(t,i,n){const s=this.ln,e=this.an,r=this.nn,h=this.sn,l=this.tn,a=this.Vn(n);this.sn=t,this.ln=isNaN(t)?NaN:this.$i.St().zt(t),this.tn=n;const o=null!==a?a.Ct():null;return null!==a&&null!==o?(this.nn=i,this.an=a.Rt(i,o)):(this.nn=NaN,this.an=NaN),s!==this.ln||e!==this.an||h!==this.sn||r!==this.nn||l!==this.tn}Tn(){const t=this.$i.wt().map((t=>t.zn().An())).filter(B),i=0===t.length?null:Math.max(...t);this.sn=null!==i?i:NaN}Dn(t,i,n){let s=t.get(i);return void 0===s&&(s=new Q(this,i,n),t.set(i,s)),s}}function ht(t){return"left"===t||"right"===t}class lt{constructor(t){this.En=new Map,this.In=[],this.Ln=t}Nn(t,i){const n=function(t,i){return void 0===t?i:{Fn:Math.max(t.Fn,i.Fn),Wn:t.Wn||i.Wn}}(this.En.get(t),i);this.En.set(t,n)}jn(){return this.Ln}Hn(t){const i=this.En.get(t);return void 0===i?{Fn:this.Ln}:{Fn:Math.max(this.Ln,i.Fn),Wn:i.Wn}}$n(){this.Un(),this.In=[{qn:0}]}Yn(t){this.Un(),this.In=[{qn:1,Ot:t}]}Zn(t){this.Xn(),this.In.push({qn:5,Ot:t})}Un(){this.Xn(),this.In.push({qn:6})}Kn(){this.Un(),this.In=[{qn:4}]}Gn(t){this.Un(),this.In.push({qn:2,Ot:t})}Jn(t){this.Un(),this.In.push({qn:3,Ot:t})}Qn(){return this.In}ts(t){for(const i of t.In)this.ns(i);this.Ln=Math.max(this.Ln,t.Ln),t.En.forEach(((t,i)=>{this.Nn(i,t)}))}static ss(){return new lt(2)}static es(){return new lt(3)}ns(t){switch(t.qn){case 0:this.$n();break;case 1:this.Yn(t.Ot);break;case 2:this.Gn(t.Ot);break;case 3:this.Jn(t.Ot);break;case 4:this.Kn();break;case 5:this.Zn(t.Ot);break;case 6:this.Xn()}}Xn(){const t=this.In.findIndex((t=>5===t.qn));-1!==t&&this.In.splice(t,1)}}const at=".";function ot(t,i){if(!P(t))return"n/a";if(!R(i))throw new TypeError("invalid length");if(i<0||i>16)throw new TypeError("invalid length");if(0===i)return t.toString();return("0000000000000000"+t.toString()).slice(-i)}class _t{constructor(t,i){if(i||(i=1),P(t)&&R(t)||(t=100),t<0)throw new TypeError("invalid base");this.Ii=t,this.rs=i,this.hs()}format(t){const i=t<0?"−":"";return t=Math.abs(t),i+this.ls(t)}hs(){if(this.os=0,this.Ii>0&&this.rs>0){let t=this.Ii;for(;t>1;)t/=10,this.os++}}ls(t){const i=this.Ii/this.rs;let n=Math.floor(t),s="";const e=void 0!==this.os?this.os:NaN;if(i>1){let r=+(Math.round(t*i)-n*i).toFixed(this.os);r>=i&&(r-=i,n+=1),s=at+ot(+r.toFixed(this.os)*this.rs,e)}else n=Math.round(n*i)/i,e>0&&(s=at+ot(0,e));return n.toFixed(0)+s}}class ut extends _t{constructor(t=100){super(t)}format(t){return`${super.format(t)}%`}}class ct{constructor(t){this._s=t}format(t){let i="";return t<0&&(i="-",t=-t),t<995?i+this.us(t):t<999995?i+this.us(t/1e3)+"K":t<999999995?(t=1e3*Math.round(t/1e3),i+this.us(t/1e6)+"M"):(t=1e6*Math.round(t/1e6),i+this.us(t/1e9)+"B")}us(t){let i;const n=Math.pow(10,this._s);return i=(t=Math.round(t*n)/n)>=1e-15&&t<1?t.toFixed(this._s).replace(/\.?0+$/,""):String(t),i.replace(/(\.[1-9]*)0+$/,((t,i)=>i))}}function dt(t,i,n,s,e,r,h){if(0===i.length||s.from>=i.length||s.to<=0)return;const{context:l,horizontalPixelRatio:a,verticalPixelRatio:o}=t,_=i[s.from];let u=r(t,_),c=_;if(s.to-s.from<2){const i=e/2;l.beginPath();const n={nt:_.nt-i,st:_.st},s={nt:_.nt+i,st:_.st};l.moveTo(n.nt*a,n.st*o),l.lineTo(s.nt*a,s.st*o),h(t,u,n,s)}else{const e=(i,n)=>{h(t,u,c,n),l.beginPath(),u=i,c=n};let d=c;l.beginPath(),l.moveTo(_.nt*a,_.st*o);for(let h=s.from+1;h=s.from;--n){const s=i[n];if(s){const i=e(t,s);i!==a&&(l.beginPath(),null!==a&&l.fill(),l.fillStyle=i,a=i);const n=Math.round(s.nt*r)+o,u=s.st*h;l.moveTo(n,u),l.arc(n,u,_,0,2*Math.PI)}}l.fill()}(t,i,l,n,o)}}class Tt extends Ct{Ds(t,i){return i.lt}}function Pt(t,i,n,s,e=0,r=i.length){let h=r-e;for(;0>1,l=e+r;s(i[l],n)===t?(e=l+1,h-=r+1):h=r}return e}const Rt=Pt.bind(null,!0),Dt=Pt.bind(null,!1);function Ot(t,i){return t.ot0&&r=s&&(l=r-1),h>0&&hObject.assign(Object.assign({},t),this.Is.$s().Hs(t.ot))))}Us(){this.Es=null}Fs(){this.Bs&&(this.qs(),this.Bs=!1),this.As&&(this.js(),this.As=!1),this.Vs&&(this.Ys(),this.Vs=!1)}Ys(){const t=this.Is.Dt(),i=this.Ls.St();if(this.Us(),i.Ni()||t.Ni())return;const n=i.Zs();if(null===n)return;if(0===this.Is.zn().Xs())return;const s=this.Is.Ct();null!==s&&(this.Es=Bt(this.zs,n,this.Ns),this.Ks(t,i,s.Ot),this.Gs())}}class zt extends At{constructor(t,i){super(t,i,!0)}Ks(t,i,n){i.Js(this.zs,A(this.Es)),t.Qs(this.zs,n,A(this.Es))}te(t,i){return{ot:t,_t:i,nt:NaN,st:NaN}}qs(){const t=this.Is.$s();this.zs=this.Is.zn().ie().map((i=>{const n=i.Ot[3];return this.ne(i.se,n,t)}))}}class Et extends zt{constructor(t,i){super(t,i),this.Ws=new L,this.ee=new kt,this.re=new Tt,this.Ws.Z([this.ee,this.re])}ne(t,i,n){return Object.assign(Object.assign({},this.te(t,i)),n.Hs(t))}Gs(){const t=this.Is.W();this.ee.J({ds:t.lineType,it:this.zs,Nt:t.lineStyle,et:t.lineWidth,fs:null,vs:t.invertFilledArea,tt:this.Es,cs:this.Ls.St().he()}),this.re.J({ds:t.lineVisible?t.lineType:void 0,it:this.zs,Nt:t.lineStyle,et:t.lineWidth,tt:this.Es,cs:this.Ls.St().he(),Rs:t.pointMarkersVisible?t.pointMarkersRadius||t.lineWidth/2+2:void 0})}}class It extends N{constructor(){super(...arguments),this.Et=null,this.le=0,this.ae=0}J(t){this.Et=t}K({context:t,horizontalPixelRatio:i,verticalPixelRatio:n}){if(null===this.Et||0===this.Et.zn.length||null===this.Et.tt)return;if(this.le=this.oe(i),this.le>=2){Math.max(1,Math.floor(i))%2!=this.le%2&&this.le--}this.ae=this.Et._e?Math.min(this.le,Math.floor(i)):this.le;let s=null;const e=this.ae<=this.le&&this.Et.he>=Math.floor(1.5*i);for(let r=this.Et.tt.from;rf+p-1&&(e=f+p-1,s=e-_+1),t.fillRect(i,s,o-i,e-s+1)}const i=a+m;let s=Math.max(f,Math.round(h.pe*n)-l),e=s+_-1;e>f+p-1&&(e=f+p-1,s=e-_+1),t.fillRect(u+1,s,i-u,e-s+1)}}}oe(t){const i=Math.floor(t);return Math.max(i,Math.floor(function(t,i){return Math.floor(.3*t*i)}(f(this.Et).he,t)))}}class Lt extends At{constructor(t,i){super(t,i,!1)}Ks(t,i,n){i.Js(this.zs,A(this.Es)),t.me(this.zs,n,A(this.Es))}be(t,i,n){return{ot:t,we:i.Ot[0],ge:i.Ot[1],Me:i.Ot[2],xe:i.Ot[3],nt:NaN,ve:NaN,ce:NaN,de:NaN,pe:NaN}}qs(){const t=this.Is.$s();this.zs=this.Is.zn().ie().map((i=>this.ne(i.se,i,t)))}}class Nt extends Lt{constructor(){super(...arguments),this.Ws=new It}ne(t,i,n){return Object.assign(Object.assign({},this.be(t,i,n)),n.Hs(t))}Gs(){const t=this.Is.W();this.Ws.J({zn:this.zs,he:this.Ls.St().he(),fe:t.openVisible,_e:t.thinBars,tt:this.Es})}}class Ft extends wt{constructor(){super(...arguments),this.Cs=new St}ps(t,i){const n=this.G;return this.Cs.bs(t,{gs:i.Se,Ms:i.ke,xs:i.ye,Ss:i.Ce,ks:t.bitmapSize.height,fs:n.fs})}}class Wt extends Ct{constructor(){super(...arguments),this.Te=new St}Ds(t,i){const n=this.G;return this.Te.bs(t,{gs:i.Pe,Ms:i.Pe,xs:i.Re,Ss:i.Re,ks:t.bitmapSize.height,fs:n.fs})}}class jt extends zt{constructor(t,i){super(t,i),this.Ws=new L,this.De=new Ft,this.Oe=new Wt,this.Ws.Z([this.De,this.Oe])}ne(t,i,n){return Object.assign(Object.assign({},this.te(t,i)),n.Hs(t))}Gs(){const t=this.Is.Ct();if(null===t)return;const i=this.Is.W(),n=this.Is.Dt().Rt(i.baseValue.price,t.Ot),s=this.Ls.St().he();this.De.J({it:this.zs,et:i.lineWidth,Nt:i.lineStyle,ds:i.lineType,fs:n,vs:!1,tt:this.Es,cs:s}),this.Oe.J({it:this.zs,et:i.lineWidth,Nt:i.lineStyle,ds:i.lineVisible?i.lineType:void 0,Rs:i.pointMarkersVisible?i.pointMarkersRadius||i.lineWidth/2+2:void 0,fs:n,tt:this.Es,cs:s})}}class Ht extends N{constructor(){super(...arguments),this.Et=null,this.le=0}J(t){this.Et=t}K(t){if(null===this.Et||0===this.Et.zn.length||null===this.Et.tt)return;const{horizontalPixelRatio:i}=t;if(this.le=function(t,i){if(t>=2.5&&t<=4)return Math.floor(3*i);const n=1-.2*Math.atan(Math.max(4,t)-4)/(.5*Math.PI),s=Math.floor(t*n*i),e=Math.floor(t*i),r=Math.min(s,e);return Math.max(Math.floor(i),r)}(this.Et.he,i),this.le>=2){Math.floor(i)%2!=this.le%2&&this.le--}const n=this.Et.zn;this.Et.Ve&&this.Be(t,n,this.Et.tt),this.Et._i&&this.Ae(t,n,this.Et.tt);const s=this.ze(i);(!this.Et._i||this.le>2*s)&&this.Ee(t,n,this.Et.tt)}Be(t,i,n){if(null===this.Et)return;const{context:s,horizontalPixelRatio:e,verticalPixelRatio:r}=t;let h="",l=Math.min(Math.floor(e),Math.floor(this.Et.he*e));l=Math.max(Math.floor(e),Math.min(l,this.le));const a=Math.floor(.5*l);let o=null;for(let t=n.from;t2*l)q(s,o,u,_-o+1,c-u+1,l);else{const t=_-o+1;s.fillRect(o,u,t,c-u+1)}a=_}}Ee(t,i,n){if(null===this.Et)return;const{context:s,horizontalPixelRatio:e,verticalPixelRatio:r}=t;let h="";const l=this.ze(e);for(let t=n.from;to||s.fillRect(_,a,u-_+1,o-a+1)}}}class $t extends Lt{constructor(){super(...arguments),this.Ws=new Ht}ne(t,i,n){return Object.assign(Object.assign({},this.be(t,i,n)),n.Hs(t))}Gs(){const t=this.Is.W();this.Ws.J({zn:this.zs,he:this.Ls.St().he(),Ve:t.wickVisible,_i:t.borderVisible,tt:this.Es})}}class Ut{constructor(t,i){this.Ne=t,this.Ii=i}X(t,i,n){this.Ne.draw(t,this.Ii,i,n)}}class qt extends At{constructor(t,i,n){super(t,i,!1),this.wn=n,this.Ws=new Ut(this.wn.renderer(),(i=>{const n=t.Ct();return null===n?null:t.Dt().Rt(i,n.Ot)}))}Fe(t){return this.wn.priceValueBuilder(t)}We(t){return this.wn.isWhitespace(t)}qs(){const t=this.Is.$s();this.zs=this.Is.zn().ie().map((i=>Object.assign(Object.assign({ot:i.se,nt:NaN},t.Hs(i.se)),{je:i.He})))}Ks(t,i){i.Js(this.zs,A(this.Es))}Gs(){this.wn.update({bars:this.zs.map(Yt),barSpacing:this.Ls.St().he(),visibleRange:this.Es},this.Is.W())}}function Yt(t){return{x:t.nt,time:t.ot,originalData:t.je,barColor:t.ue}}class Zt extends N{constructor(){super(...arguments),this.Et=null,this.$e=[]}J(t){this.Et=t,this.$e=[]}K({context:t,horizontalPixelRatio:i,verticalPixelRatio:n}){if(null===this.Et||0===this.Et.it.length||null===this.Et.tt)return;this.$e.length||this.Ue(i);const s=Math.max(1,Math.floor(n)),e=Math.round(this.Et.qe*n)-Math.floor(s/2),r=e+s;for(let i=this.Et.tt.from;is.Ze?s.ui=n.Os-i-1:n.Os=s.ui+i+1))}let s=Math.ceil(this.Et.he*t);for(let t=this.Et.tt.from;t0&&s<4)for(let t=this.Et.tt.from;ts&&(i.Ye>i.Ze?i.ui-=1:i.Os+=1)}}}class Xt extends zt{constructor(){super(...arguments),this.Ws=new Zt}ne(t,i,n){return Object.assign(Object.assign({},this.te(t,i)),n.Hs(t))}Gs(){const t={it:this.zs,he:this.Ls.St().he(),tt:this.Es,qe:this.Is.Dt().Rt(this.Is.W().base,f(this.Is.Ct()).Ot)};this.Ws.J(t)}}class Kt extends zt{constructor(){super(...arguments),this.Ws=new Tt}ne(t,i,n){return Object.assign(Object.assign({},this.te(t,i)),n.Hs(t))}Gs(){const t=this.Is.W(),i={it:this.zs,Nt:t.lineStyle,ds:t.lineVisible?t.lineType:void 0,et:t.lineWidth,Rs:t.pointMarkersVisible?t.pointMarkersRadius||t.lineWidth/2+2:void 0,tt:this.Es,cs:this.Ls.St().he()};this.Ws.J(i)}}const Gt=/[2-9]/g;class Jt{constructor(t=50){this.Xe=0,this.Ke=1,this.Ge=1,this.Je={},this.Qe=new Map,this.tr=t}ir(){this.Xe=0,this.Qe.clear(),this.Ke=1,this.Ge=1,this.Je={}}xi(t,i,n){return this.nr(t,i,n).width}Mi(t,i,n){const s=this.nr(t,i,n);return((s.actualBoundingBoxAscent||0)-(s.actualBoundingBoxDescent||0))/2}nr(t,i,n){const s=n||Gt,e=String(i).replace(s,"0");if(this.Qe.has(e))return d(this.Qe.get(e)).sr;if(this.Xe===this.tr){const t=this.Je[this.Ge];delete this.Je[this.Ge],this.Qe.delete(t),this.Ge++,this.Xe--}t.save(),t.textBaseline="middle";const r=t.measureText(e);return t.restore(),0===r.width&&i.length||(this.Qe.set(e,{sr:r,er:this.Ke}),this.Je[this.Ke]=e,this.Xe++,this.Ke++),r}}class Qt{constructor(t){this.rr=null,this.k=null,this.hr="right",this.lr=t}ar(t,i,n){this.rr=t,this.k=i,this.hr=n}X(t){null!==this.k&&null!==this.rr&&this.rr.X(t,this.k,this.lr,this.hr)}}class ti{constructor(t,i,n){this._r=t,this.lr=new Jt(50),this.ur=i,this.F=n,this.j=-1,this.Wt=new Qt(this.lr)}gt(){const t=this.F.cr(this.ur);if(null===t)return null;const i=t.dr(this.ur)?t.vr():this.ur.Dt();if(null===i)return null;const n=t.pr(i);if("overlay"===n)return null;const s=this.F.mr();return s.P!==this.j&&(this.j=s.P,this.lr.ir()),this.Wt.ar(this._r.zi(),s,n),this.Wt}}class ii extends N{constructor(){super(...arguments),this.Et=null}J(t){this.Et=t}br(t,i){var n;if(!(null===(n=this.Et)||void 0===n?void 0:n.yt))return null;const{st:s,et:e,wr:r}=this.Et;return i>=s-e-7&&i<=s+e+7?{gr:this.Et,wr:r}:null}K({context:t,bitmapSize:i,horizontalPixelRatio:n,verticalPixelRatio:s}){if(null===this.Et)return;if(!1===this.Et.yt)return;const e=Math.round(this.Et.st*s);e<0||e>i.height||(t.lineCap="butt",t.strokeStyle=this.Et.O,t.lineWidth=Math.floor(this.Et.et*n),_(t,this.Et.Nt),u(t,e,0,i.width))}}class ni{constructor(t){this.Mr={st:0,O:"rgba(0, 0, 0, 0)",et:1,Nt:0,yt:!1},this.Sr=new ii,this.ft=!0,this.Is=t,this.Ls=t.$t(),this.Sr.J(this.Mr)}bt(){this.ft=!0}gt(){return this.Is.yt()?(this.ft&&(this.kr(),this.ft=!1),this.Sr):null}}class si extends ni{constructor(t){super(t)}kr(){this.Mr.yt=!1;const t=this.Is.Dt(),i=t.yr().yr;if(2!==i&&3!==i)return;const n=this.Is.W();if(!n.baseLineVisible||!this.Is.yt())return;const s=this.Is.Ct();null!==s&&(this.Mr.yt=!0,this.Mr.st=t.Rt(s.Ot,s.Ot),this.Mr.O=n.baseLineColor,this.Mr.et=n.baseLineWidth,this.Mr.Nt=n.baseLineStyle)}}class ei extends N{constructor(){super(...arguments),this.Et=null}J(t){this.Et=t}He(){return this.Et}K({context:t,horizontalPixelRatio:i,verticalPixelRatio:n}){const s=this.Et;if(null===s)return;const e=Math.max(1,Math.floor(i)),r=e%2/2,h=Math.round(s.Ze.x*i)+r,l=s.Ze.y*n;t.fillStyle=s.Cr,t.beginPath();const a=Math.max(2,1.5*s.Tr)*i;t.arc(h,l,a,0,2*Math.PI,!1),t.fill(),t.fillStyle=s.Pr,t.beginPath(),t.arc(h,l,s.ht*i,0,2*Math.PI,!1),t.fill(),t.lineWidth=e,t.strokeStyle=s.Rr,t.beginPath(),t.arc(h,l,s.ht*i+e/2,0,2*Math.PI,!1),t.stroke()}}const ri=[{Dr:0,Or:.25,Vr:4,Br:10,Ar:.25,zr:0,Er:.4,Ir:.8},{Dr:.25,Or:.525,Vr:10,Br:14,Ar:0,zr:0,Er:.8,Ir:0},{Dr:.525,Or:1,Vr:14,Br:14,Ar:0,zr:0,Er:0,Ir:0}];function hi(t,i,n,s){return function(t,i){if("transparent"===t)return t;const n=S(t),s=n[3];return`rgba(${n[0]}, ${n[1]}, ${n[2]}, ${i*s})`}(t,n+(s-n)*i)}function li(t,i){const n=t%2600/2600;let s;for(const t of ri)if(n>=t.Dr&&n<=t.Or){s=t;break}c(void 0!==s,"Last price animation internal logic error");const e=(n-s.Dr)/(s.Or-s.Dr);return{Pr:hi(i,e,s.Ar,s.zr),Rr:hi(i,e,s.Er,s.Ir),ht:(r=e,h=s.Vr,l=s.Br,h+(l-h)*r)};var r,h,l}class ai{constructor(t){this.Wt=new ei,this.ft=!0,this.Lr=!0,this.Nr=performance.now(),this.Fr=this.Nr-1,this.Wr=t}jr(){this.Fr=this.Nr-1,this.bt()}Hr(){if(this.bt(),2===this.Wr.W().lastPriceAnimation){const t=performance.now(),i=this.Fr-t;if(i>0)return void(i<650&&(this.Fr+=2600));this.Nr=t,this.Fr=t+2600}}bt(){this.ft=!0}$r(){this.Lr=!0}yt(){return 0!==this.Wr.W().lastPriceAnimation}Ur(){switch(this.Wr.W().lastPriceAnimation){case 0:return!1;case 1:return!0;case 2:return performance.now()<=this.Fr}}gt(){return this.ft?(this.Mt(),this.ft=!1,this.Lr=!1):this.Lr&&(this.qr(),this.Lr=!1),this.Wt}Mt(){this.Wt.J(null);const t=this.Wr.$t().St(),i=t.Zs(),n=this.Wr.Ct();if(null===i||null===n)return;const s=this.Wr.Yr(!0);if(s.Zr||!i.Xr(s.se))return;const e={x:t.zt(s.se),y:this.Wr.Dt().Rt(s._t,n.Ot)},r=s.O,h=this.Wr.W().lineWidth,l=li(this.Kr(),r);this.Wt.J({Cr:r,Tr:h,Pr:l.Pr,Rr:l.Rr,ht:l.ht,Ze:e})}qr(){const t=this.Wt.He();if(null!==t){const i=li(this.Kr(),t.Cr);t.Pr=i.Pr,t.Rr=i.Rr,t.ht=i.ht}}Kr(){return this.Ur()?performance.now()-this.Nr:2599}}function oi(t,i){return xt(Math.min(Math.max(t,12),30)*i)}function _i(t,i){switch(t){case"arrowDown":case"arrowUp":return oi(i,1);case"circle":return oi(i,.8);case"square":return oi(i,.7)}}function ui(t){return function(t){const i=Math.ceil(t);return i%2!=0?i-1:i}(oi(t,1))}function ci(t){return Math.max(oi(t,.1),3)}function di(t,i,n){return i?t:n?Math.ceil(t/2):0}function fi(t,i,n,s,e){const r=_i("square",n),h=(r-1)/2,l=t-h,a=i-h;return s>=l&&s<=l+r&&e>=a&&e<=a+r}function vi(t,i,n,s){const e=(_i("arrowUp",s)-1)/2*n.Gr,r=(xt(s/2)-1)/2*n.Gr;i.beginPath(),t?(i.moveTo(n.nt-e,n.st),i.lineTo(n.nt,n.st-e),i.lineTo(n.nt+e,n.st),i.lineTo(n.nt+r,n.st),i.lineTo(n.nt+r,n.st+e),i.lineTo(n.nt-r,n.st+e),i.lineTo(n.nt-r,n.st)):(i.moveTo(n.nt-e,n.st),i.lineTo(n.nt,n.st+e),i.lineTo(n.nt+e,n.st),i.lineTo(n.nt+r,n.st),i.lineTo(n.nt+r,n.st-e),i.lineTo(n.nt-r,n.st-e),i.lineTo(n.nt-r,n.st)),i.fill()}function pi(t,i,n,s,e,r){return fi(i,n,s,e,r)}class mi extends N{constructor(){super(...arguments),this.Et=null,this.lr=new Jt,this.j=-1,this.H="",this.Jr=""}J(t){this.Et=t}ar(t,i){this.j===t&&this.H===i||(this.j=t,this.H=i,this.Jr=E(t,i),this.lr.ir())}br(t,i){if(null===this.Et||null===this.Et.tt)return null;for(let n=this.Et.tt.from;n=t&&e<=t+n&&r>=i-h&&r<=i+h}(t.Kt.nt,t.Kt.st,t.Kt.Hi,t.Kt.At,i,n))||function(t,i,n){if(0===t.Xs)return!1;switch(t.ih){case"arrowDown":case"arrowUp":return pi(0,t.nt,t.st,t.Xs,i,n);case"circle":return function(t,i,n,s,e){const r=2+_i("circle",n)/2,h=t-s,l=i-e;return Math.sqrt(h*h+l*l)<=r}(t.nt,t.st,t.Xs,i,n);case"square":return fi(t.nt,t.st,t.Xs,i,n)}}(t,i,n)}function gi(t,i,n,s,e,r,h,l,a){const o=P(n)?n:n.xe,_=P(n)?n:n.ge,u=P(n)?n:n.Me,c=P(i.size)?Math.max(i.size,0):1,d=ui(l.he())*c,f=d/2;switch(t.Xs=d,i.position){case"inBar":return t.st=h.Rt(o,a),void(void 0!==t.Kt&&(t.Kt.st=t.st+f+r+.6*e));case"aboveBar":return t.st=h.Rt(_,a)-f-s.nh,void 0!==t.Kt&&(t.Kt.st=t.st-f-.6*e,s.nh+=1.2*e),void(s.nh+=d+r);case"belowBar":return t.st=h.Rt(u,a)+f+s.sh,void 0!==t.Kt&&(t.Kt.st=t.st+f+r+.6*e,s.sh+=1.2*e),void(s.sh+=d+r)}i.position}class Mi{constructor(t,i){this.ft=!0,this.eh=!0,this.rh=!0,this.hh=null,this.ah=null,this.Wt=new mi,this.Wr=t,this.$i=i,this.Et={it:[],tt:null}}bt(t){this.ft=!0,this.rh=!0,"data"===t&&(this.eh=!0,this.ah=null)}gt(t){if(!this.Wr.yt())return null;this.ft&&this.oh();const i=this.$i.W().layout;return this.Wt.ar(i.fontSize,i.fontFamily),this.Wt.J(this.Et),this.Wt}_h(){if(this.rh){if(this.Wr.uh().length>0){const t=this.$i.St().he(),i=ci(t),n=1.5*ui(t)+2*i,s=this.dh();this.hh={above:di(n,s.aboveBar,s.inBar),below:di(n,s.belowBar,s.inBar)}}else this.hh=null;this.rh=!1}return this.hh}dh(){return null===this.ah&&(this.ah=this.Wr.uh().reduce(((t,i)=>(t[i.position]||(t[i.position]=!0),t)),{inBar:!1,aboveBar:!1,belowBar:!1})),this.ah}oh(){const t=this.Wr.Dt(),i=this.$i.St(),n=this.Wr.uh();this.eh&&(this.Et.it=n.map((t=>({ot:t.time,nt:0,st:0,Xs:0,ih:t.shape,O:t.color,Qr:t.Qr,wr:t.id,Kt:void 0}))),this.eh=!1);const s=this.$i.W().layout;this.Et.tt=null;const e=i.Zs();if(null===e)return;const r=this.Wr.Ct();if(null===r)return;if(0===this.Et.it.length)return;let h=NaN;const l=ci(i.he()),a={nh:l,sh:l};this.Et.tt=Bt(this.Et.it,e,!0);for(let e=this.Et.tt.from;e0&&(_.Kt={th:o.text,nt:0,st:0,Hi:0,At:0});const u=this.Wr.fh(o.time);null!==u&&gi(_,o,u,a,s.fontSize,l,t,i,r.Ot)}this.ft=!1}}class xi extends ni{constructor(t){super(t)}kr(){const t=this.Mr;t.yt=!1;const i=this.Is.W();if(!i.priceLineVisible||!this.Is.yt())return;const n=this.Is.Yr(0===i.priceLineSource);n.Zr||(t.yt=!0,t.st=n.ki,t.O=this.Is.ph(n.O),t.et=i.priceLineWidth,t.Nt=i.priceLineStyle)}}class Si extends J{constructor(t){super(),this.jt=t}Ei(t,i,n){t.yt=!1,i.yt=!1;const s=this.jt;if(!s.yt())return;const e=s.W(),r=e.lastValueVisible,h=""!==s.mh(),l=0===e.seriesLastValueMode,a=s.Yr(!1);if(a.Zr)return;r&&(t.Kt=this.bh(a,r,l),t.yt=0!==t.Kt.length),(h||l)&&(i.Kt=this.wh(a,r,h,l),i.yt=i.Kt.length>0);const o=s.ph(a.O),_=y(o);n.t=_.t,n.ki=a.ki,i.Vt=s.$t().Bt(a.ki/s.Dt().At()),t.Vt=o,t.O=_.i,i.O=_.i}wh(t,i,n,s){let e="";const r=this.jt.mh();return n&&0!==r.length&&(e+=`${r} `),i&&s&&(e+=this.jt.Dt().gh()?t.Mh:t.xh),e.trim()}bh(t,i,n){return i?n?this.jt.Dt().gh()?t.xh:t.Mh:t.Kt:""}}function ki(t,i,n,s){const e=Number.isFinite(i),r=Number.isFinite(n);return e&&r?t(i,n):e||r?e?i:n:s}class yi{constructor(t,i){this.Sh=t,this.kh=i}yh(t){return null!==t&&(this.Sh===t.Sh&&this.kh===t.kh)}Ch(){return new yi(this.Sh,this.kh)}Th(){return this.Sh}Ph(){return this.kh}Rh(){return this.kh-this.Sh}Ni(){return this.kh===this.Sh||Number.isNaN(this.kh)||Number.isNaN(this.Sh)}ts(t){return null===t?this:new yi(ki(Math.min,this.Th(),t.Th(),-1/0),ki(Math.max,this.Ph(),t.Ph(),1/0))}Dh(t){if(!P(t))return;if(0===this.kh-this.Sh)return;const i=.5*(this.kh+this.Sh);let n=this.kh-i,s=this.Sh-i;n*=t,s*=t,this.kh=i+n,this.Sh=i+s}Oh(t){P(t)&&(this.kh+=t,this.Sh+=t)}Vh(){return{minValue:this.Sh,maxValue:this.kh}}static Bh(t){return null===t?null:new yi(t.minValue,t.maxValue)}}class Ci{constructor(t,i){this.Ah=t,this.zh=i||null}Eh(){return this.Ah}Ih(){return this.zh}Vh(){return null===this.Ah?null:{priceRange:this.Ah.Vh(),margins:this.zh||void 0}}static Bh(t){return null===t?null:new Ci(yi.Bh(t.priceRange),t.margins)}}class Ti extends ni{constructor(t,i){super(t),this.Lh=i}kr(){const t=this.Mr;t.yt=!1;const i=this.Lh.W();if(!this.Is.yt()||!i.lineVisible)return;const n=this.Lh.Nh();null!==n&&(t.yt=!0,t.st=n,t.O=i.color,t.et=i.lineWidth,t.Nt=i.lineStyle,t.wr=this.Lh.W().id)}}class Pi extends J{constructor(t,i){super(),this.Wr=t,this.Lh=i}Ei(t,i,n){t.yt=!1,i.yt=!1;const s=this.Lh.W(),e=s.axisLabelVisible,r=""!==s.title,h=this.Wr;if(!e||!h.yt())return;const l=this.Lh.Nh();if(null===l)return;r&&(i.Kt=s.title,i.yt=!0),i.Vt=h.$t().Bt(l/h.Dt().At()),t.Kt=this.Fh(s.price),t.yt=!0;const a=y(s.axisLabelColor||s.color);n.t=a.t;const o=s.axisLabelTextColor||a.i;t.O=o,i.O=o,n.ki=l}Fh(t){const i=this.Wr.Ct();return null===i?"":this.Wr.Dt().Fi(t,i.Ot)}}class Ri{constructor(t,i){this.Wr=t,this.cn=i,this.Wh=new Ti(t,this),this._r=new Pi(t,this),this.jh=new ti(this._r,t,t.$t())}Hh(t){T(this.cn,t),this.bt(),this.Wr.$t().$h()}W(){return this.cn}Uh(){return this.Wh}qh(){return this.jh}Yh(){return this._r}bt(){this.Wh.bt(),this._r.bt()}Nh(){const t=this.Wr,i=t.Dt();if(t.$t().St().Ni()||i.Ni())return null;const n=t.Ct();return null===n?null:i.Rt(this.cn.price,n.Ot)}}class Di extends st{constructor(t){super(),this.$i=t}$t(){return this.$i}}const Oi={Bar:(t,i,n,s)=>{var e;const r=i.upColor,h=i.downColor,l=f(t(n,s)),a=v(l.Ot[0])<=v(l.Ot[3]);return{ue:null!==(e=l.O)&&void 0!==e?e:a?r:h}},Candlestick:(t,i,n,s)=>{var e,r,h;const l=i.upColor,a=i.downColor,o=i.borderUpColor,_=i.borderDownColor,u=i.wickUpColor,c=i.wickDownColor,d=f(t(n,s)),p=v(d.Ot[0])<=v(d.Ot[3]);return{ue:null!==(e=d.O)&&void 0!==e?e:p?l:a,Le:null!==(r=d.Vt)&&void 0!==r?r:p?o:_,Ie:null!==(h=d.Zh)&&void 0!==h?h:p?u:c}},Custom:(t,i,n,s)=>{var e;return{ue:null!==(e=f(t(n,s)).O)&&void 0!==e?e:i.color}},Area:(t,i,n,s)=>{var e,r,h,l;const a=f(t(n,s));return{ue:null!==(e=a.lt)&&void 0!==e?e:i.lineColor,lt:null!==(r=a.lt)&&void 0!==r?r:i.lineColor,Ts:null!==(h=a.Ts)&&void 0!==h?h:i.topColor,Ps:null!==(l=a.Ps)&&void 0!==l?l:i.bottomColor}},Baseline:(t,i,n,s)=>{var e,r,h,l,a,o;const _=f(t(n,s));return{ue:_.Ot[3]>=i.baseValue.price?i.topLineColor:i.bottomLineColor,Pe:null!==(e=_.Pe)&&void 0!==e?e:i.topLineColor,Re:null!==(r=_.Re)&&void 0!==r?r:i.bottomLineColor,Se:null!==(h=_.Se)&&void 0!==h?h:i.topFillColor1,ke:null!==(l=_.ke)&&void 0!==l?l:i.topFillColor2,ye:null!==(a=_.ye)&&void 0!==a?a:i.bottomFillColor1,Ce:null!==(o=_.Ce)&&void 0!==o?o:i.bottomFillColor2}},Line:(t,i,n,s)=>{var e,r;const h=f(t(n,s));return{ue:null!==(e=h.O)&&void 0!==e?e:i.color,lt:null!==(r=h.O)&&void 0!==r?r:i.color}},Histogram:(t,i,n,s)=>{var e;return{ue:null!==(e=f(t(n,s)).O)&&void 0!==e?e:i.color}}};class Vi{constructor(t){this.Xh=(t,i)=>void 0!==i?i.Ot:this.Wr.zn().Kh(t),this.Wr=t,this.Gh=Oi[t.Jh()]}Hs(t,i){return this.Gh(this.Xh,this.Wr.W(),t,i)}}var Bi;!function(t){t[t.NearestLeft=-1]="NearestLeft",t[t.None=0]="None",t[t.NearestRight=1]="NearestRight"}(Bi||(Bi={}));const Ai=30;class zi{constructor(){this.Qh=[],this.tl=new Map,this.il=new Map}nl(){return this.Xs()>0?this.Qh[this.Qh.length-1]:null}sl(){return this.Xs()>0?this.el(0):null}An(){return this.Xs()>0?this.el(this.Qh.length-1):null}Xs(){return this.Qh.length}Ni(){return 0===this.Xs()}Xr(t){return null!==this.rl(t,0)}Kh(t){return this.hl(t)}hl(t,i=0){const n=this.rl(t,i);return null===n?null:Object.assign(Object.assign({},this.ll(n)),{se:this.el(n)})}ie(){return this.Qh}al(t,i,n){if(this.Ni())return null;let s=null;for(const e of n){s=Ei(s,this.ol(t,i,e))}return s}J(t){this.il.clear(),this.tl.clear(),this.Qh=t}el(t){return this.Qh[t].se}ll(t){return this.Qh[t]}rl(t,i){const n=this._l(t);if(null===n&&0!==i)switch(i){case-1:return this.ul(t);case 1:return this.cl(t);default:throw new TypeError("Unknown search mode")}return n}ul(t){let i=this.dl(t);return i>0&&(i-=1),i!==this.Qh.length&&this.el(i)t.set.se>i))}vl(t,i,n){let s=null;for(let e=t;es.ml&&(s.ml=t)))}return s}ol(t,i,n){if(this.Ni())return null;let s=null;const e=f(this.sl()),r=f(this.An()),h=Math.max(t,e),l=Math.min(i,r),a=Math.ceil(h/Ai)*Ai,o=Math.max(a,Math.floor(l/Ai)*Ai);{const t=this.dl(h),e=this.fl(Math.min(l,a,i));s=Ei(s,this.vl(t,e,n))}let _=this.tl.get(n);void 0===_&&(_=new Map,this.tl.set(n,_));for(let t=Math.max(a+1,h);tnew Li(t)));return this.yl={gl:e,Ml:r},r}Qi(){var t,i,n,s;const e=null!==(n=null===(i=(t=this.Dl).timeAxisViews)||void 0===i?void 0:i.call(t))&&void 0!==n?n:[];if((null===(s=this.Cl)||void 0===s?void 0:s.gl)===e)return this.Cl.Ml;const r=this.Wr.$t().St(),h=e.map((t=>new Fi(t,r)));return this.Cl={gl:e,Ml:h},h}Rn(){var t,i,n,s;const e=null!==(n=null===(i=(t=this.Dl).priceAxisViews)||void 0===i?void 0:i.call(t))&&void 0!==n?n:[];if((null===(s=this.Tl)||void 0===s?void 0:s.gl)===e)return this.Tl.Ml;const r=this.Wr.Dt(),h=e.map((t=>new Wi(t,r)));return this.Tl={gl:e,Ml:h},h}Vl(){var t,i,n,s;const e=null!==(n=null===(i=(t=this.Dl).priceAxisPaneViews)||void 0===i?void 0:i.call(t))&&void 0!==n?n:[];if((null===(s=this.Pl)||void 0===s?void 0:s.gl)===e)return this.Pl.Ml;const r=e.map((t=>new Li(t)));return this.Pl={gl:e,Ml:r},r}Bl(){var t,i,n,s;const e=null!==(n=null===(i=(t=this.Dl).timeAxisPaneViews)||void 0===i?void 0:i.call(t))&&void 0!==n?n:[];if((null===(s=this.Rl)||void 0===s?void 0:s.gl)===e)return this.Rl.Ml;const r=e.map((t=>new Li(t)));return this.Rl={gl:e,Ml:r},r}Al(t,i){var n,s,e;return null!==(e=null===(s=(n=this.Dl).autoscaleInfo)||void 0===s?void 0:s.call(n,t,i))&&void 0!==e?e:null}br(t,i){var n,s,e;return null!==(e=null===(s=(n=this.Dl).hitTest)||void 0===s?void 0:s.call(n,t,i))&&void 0!==e?e:null}}function Hi(t,i,n,s){t.forEach((t=>{i(t).forEach((t=>{t.xl()===n&&s.push(t)}))}))}function $i(t){return t.Pn()}function Ui(t){return t.Vl()}function qi(t){return t.Bl()}class Yi extends Di{constructor(t,i,n,s,e){super(t),this.Et=new zi,this.Wh=new xi(this),this.zl=[],this.El=new si(this),this.Il=null,this.Ll=null,this.Nl=[],this.Fl=[],this.Wl=null,this.jl=[],this.cn=i,this.Hl=n;const r=new Si(this);this.rn=[r],this.jh=new ti(r,this,t),"Area"!==n&&"Line"!==n&&"Baseline"!==n||(this.Il=new ai(this)),this.$l(),this.Ul(e)}S(){null!==this.Wl&&clearTimeout(this.Wl)}ph(t){return this.cn.priceLineColor||t}Yr(t){const i={Zr:!0},n=this.Dt();if(this.$t().St().Ni()||n.Ni()||this.Et.Ni())return i;const s=this.$t().St().Zs(),e=this.Ct();if(null===s||null===e)return i;let r,h;if(t){const t=this.Et.nl();if(null===t)return i;r=t,h=t.se}else{const t=this.Et.hl(s.ui(),-1);if(null===t)return i;if(r=this.Et.Kh(t.se),null===r)return i;h=t.se}const l=r.Ot[3],a=this.$s().Hs(h,{Ot:r}),o=n.Rt(l,e.Ot);return{Zr:!1,_t:l,Kt:n.Fi(l,e.Ot),Mh:n.ql(l),xh:n.Yl(l,e.Ot),O:a.ue,ki:o,se:h}}$s(){return null!==this.Ll||(this.Ll=new Vi(this)),this.Ll}W(){return this.cn}Hh(t){const i=t.priceScaleId;void 0!==i&&i!==this.cn.priceScaleId&&this.$t().Zl(this,i),T(this.cn,t),void 0!==t.priceFormat&&(this.$l(),this.$t().Xl()),this.$t().Kl(this),this.$t().Gl(),this.wn.bt("options")}J(t,i){this.Et.J(t),this.Jl(),this.wn.bt("data"),this.dn.bt("data"),null!==this.Il&&(i&&i.Ql?this.Il.Hr():0===t.length&&this.Il.jr());const n=this.$t().cr(this);this.$t().ta(n),this.$t().Kl(this),this.$t().Gl(),this.$t().$h()}ia(t){this.Nl=t,this.Jl();const i=this.$t().cr(this);this.dn.bt("data"),this.$t().ta(i),this.$t().Kl(this),this.$t().Gl(),this.$t().$h()}na(){return this.Nl}uh(){return this.Fl}sa(t){const i=new Ri(this,t);return this.zl.push(i),this.$t().Kl(this),i}ea(t){const i=this.zl.indexOf(t);-1!==i&&this.zl.splice(i,1),this.$t().Kl(this)}Jh(){return this.Hl}Ct(){const t=this.ra();return null===t?null:{Ot:t.Ot[3],ha:t.ot}}ra(){const t=this.$t().St().Zs();if(null===t)return null;const i=t.Os();return this.Et.hl(i,1)}zn(){return this.Et}fh(t){const i=this.Et.Kh(t);return null===i?null:"Bar"===this.Hl||"Candlestick"===this.Hl||"Custom"===this.Hl?{we:i.Ot[0],ge:i.Ot[1],Me:i.Ot[2],xe:i.Ot[3]}:i.Ot[3]}la(t){const i=[];Hi(this.jl,$i,"top",i);const n=this.Il;return null!==n&&n.yt()?(null===this.Wl&&n.Ur()&&(this.Wl=setTimeout((()=>{this.Wl=null,this.$t().aa()}),0)),n.$r(),i.unshift(n),i):i}Pn(){const t=[];this.oa()||t.push(this.El),t.push(this.wn,this.Wh,this.dn);const i=this.zl.map((t=>t.Uh()));return t.push(...i),Hi(this.jl,$i,"normal",t),t}_a(){return this.ua($i,"bottom")}ca(t){return this.ua(Ui,t)}da(t){return this.ua(qi,t)}fa(t,i){return this.jl.map((n=>n.br(t,i))).filter((t=>null!==t))}Ji(t){return[this.jh,...this.zl.map((t=>t.qh()))]}Rn(t,i){if(i!==this.Yi&&!this.oa())return[];const n=[...this.rn];for(const t of this.zl)n.push(t.Yh());return this.jl.forEach((t=>{n.push(...t.Rn())})),n}Qi(){const t=[];return this.jl.forEach((i=>{t.push(...i.Qi())})),t}Al(t,i){if(void 0!==this.cn.autoscaleInfoProvider){const n=this.cn.autoscaleInfoProvider((()=>{const n=this.va(t,i);return null===n?null:n.Vh()}));return Ci.Bh(n)}return this.va(t,i)}pa(){return this.cn.priceFormat.minMove}ma(){return this.ba}On(){var t;this.wn.bt(),this.dn.bt();for(const t of this.rn)t.bt();for(const t of this.zl)t.bt();this.Wh.bt(),this.El.bt(),null===(t=this.Il)||void 0===t||t.bt(),this.jl.forEach((t=>t.On()))}Dt(){return f(super.Dt())}kt(t){if(!(("Line"===this.Hl||"Area"===this.Hl||"Baseline"===this.Hl)&&this.cn.crosshairMarkerVisible))return null;const i=this.Et.Kh(t);if(null===i)return null;return{_t:i.Ot[3],ht:this.wa(),Vt:this.ga(),Pt:this.Ma(),Tt:this.xa(t)}}mh(){return this.cn.title}yt(){return this.cn.visible}Sa(t){this.jl.push(new ji(t,this))}ka(t){this.jl=this.jl.filter((i=>i.Ol()!==t))}ya(){if(this.wn instanceof qt!=!1)return t=>this.wn.Fe(t)}Ca(){if(this.wn instanceof qt!=!1)return t=>this.wn.We(t)}oa(){return!ht(this.Dt().Ta())}va(t,i){if(!R(t)||!R(i)||this.Et.Ni())return null;const n="Line"===this.Hl||"Area"===this.Hl||"Baseline"===this.Hl||"Histogram"===this.Hl?[3]:[2,1],s=this.Et.al(t,i,n);let e=null!==s?new yi(s.pl,s.ml):null;if("Histogram"===this.Jh()){const t=this.cn.base,i=new yi(t,t);e=null!==e?e.ts(i):i}let r=this.dn._h();return this.jl.forEach((n=>{const s=n.Al(t,i);if(null==s?void 0:s.priceRange){const t=new yi(s.priceRange.minValue,s.priceRange.maxValue);e=null!==e?e.ts(t):t}var h,l,a,o;(null==s?void 0:s.margins)&&(h=r,l=s.margins,r={above:Math.max(null!==(a=null==h?void 0:h.above)&&void 0!==a?a:0,l.above),below:Math.max(null!==(o=null==h?void 0:h.below)&&void 0!==o?o:0,l.below)})})),new Ci(e,r)}wa(){switch(this.Hl){case"Line":case"Area":case"Baseline":return this.cn.crosshairMarkerRadius}return 0}ga(){switch(this.Hl){case"Line":case"Area":case"Baseline":{const t=this.cn.crosshairMarkerBorderColor;if(0!==t.length)return t}}return null}Ma(){switch(this.Hl){case"Line":case"Area":case"Baseline":return this.cn.crosshairMarkerBorderWidth}return 0}xa(t){switch(this.Hl){case"Line":case"Area":case"Baseline":{const t=this.cn.crosshairMarkerBackgroundColor;if(0!==t.length)return t}}return this.$s().Hs(t).ue}$l(){switch(this.cn.priceFormat.type){case"custom":this.ba={format:this.cn.priceFormat.formatter};break;case"volume":this.ba=new ct(this.cn.priceFormat.precision);break;case"percent":this.ba=new ut(this.cn.priceFormat.precision);break;default:{const t=Math.pow(10,this.cn.priceFormat.precision);this.ba=new _t(t,this.cn.priceFormat.minMove*t)}}null!==this.Yi&&this.Yi.Pa()}Jl(){const t=this.$t().St();if(!t.Ra()||this.Et.Ni())return void(this.Fl=[]);const i=f(this.Et.sl());this.Fl=this.Nl.map(((n,s)=>{const e=f(t.Da(n.time,!0)),r=et instanceof Yi)).reduce(((t,s)=>{if(n.dr(s)||!s.yt())return t;const e=s.Dt(),r=s.zn();if(e.Ni()||!r.Xr(i))return t;const h=r.Kh(i);if(null===h)return t;const l=v(s.Ct());return t.concat([e.Rt(h.Ot[3],l.Ot)])}),[]);if(0===l.length)return s;l.sort(((t,i)=>Math.abs(t-h)-Math.abs(i-h)));const a=l[0];return s=e.pn(a,r),s}}class Xi extends N{constructor(){super(...arguments),this.Et=null}J(t){this.Et=t}K({context:t,bitmapSize:i,horizontalPixelRatio:n,verticalPixelRatio:s}){if(null===this.Et)return;const e=Math.max(1,Math.floor(n));t.lineWidth=e,function(t,i){t.save(),t.lineWidth%2&&t.translate(.5,.5),i(),t.restore()}(t,(()=>{const r=f(this.Et);if(r.Ba){t.strokeStyle=r.Aa,_(t,r.za),t.beginPath();for(const s of r.Ea){const r=Math.round(s.Ia*n);t.moveTo(r,-e),t.lineTo(r,i.height+e)}t.stroke()}if(r.La){t.strokeStyle=r.Na,_(t,r.Fa),t.beginPath();for(const n of r.Wa){const r=Math.round(n.Ia*s);t.moveTo(-e,r),t.lineTo(i.width+e,r)}t.stroke()}}))}}class Ki{constructor(t){this.Wt=new Xi,this.ft=!0,this.tn=t}bt(){this.ft=!0}gt(){if(this.ft){const t=this.tn.$t().W().grid,i={La:t.horzLines.visible,Ba:t.vertLines.visible,Na:t.horzLines.color,Aa:t.vertLines.color,Fa:t.horzLines.style,za:t.vertLines.style,Wa:this.tn.vn().ja(),Ea:(this.tn.$t().St().ja()||[]).map((t=>({Ia:t.coord})))};this.Wt.J(i),this.ft=!1}return this.Wt}}class Gi{constructor(t){this.wn=new Ki(t)}Uh(){return this.wn}}const Ji={Ha:4,$a:1e-4};function Qi(t,i){const n=100*(t-i)/i;return i<0?-n:n}function tn(t,i){const n=Qi(t.Th(),i),s=Qi(t.Ph(),i);return new yi(n,s)}function nn(t,i){const n=100*(t-i)/i+100;return i<0?-n:n}function sn(t,i){const n=nn(t.Th(),i),s=nn(t.Ph(),i);return new yi(n,s)}function en(t,i){const n=Math.abs(t);if(n<1e-15)return 0;const s=Math.log10(n+i.$a)+i.Ha;return t<0?-s:s}function rn(t,i){const n=Math.abs(t);if(n<1e-15)return 0;const s=Math.pow(10,n-i.Ha)-i.$a;return t<0?-s:s}function hn(t,i){if(null===t)return null;const n=en(t.Th(),i),s=en(t.Ph(),i);return new yi(n,s)}function ln(t,i){if(null===t)return null;const n=rn(t.Th(),i),s=rn(t.Ph(),i);return new yi(n,s)}function an(t){if(null===t)return Ji;const i=Math.abs(t.Ph()-t.Th());if(i>=1||i<1e-15)return Ji;const n=Math.ceil(Math.abs(Math.log10(i))),s=Ji.Ha+n;return{Ha:s,$a:1/Math.pow(10,s)}}class on{constructor(t,i){if(this.Ua=t,this.qa=i,function(t){if(t<0)return!1;for(let i=t;i>1;i/=10)if(i%10!=0)return!1;return!0}(this.Ua))this.Ya=[2,2.5,2];else{this.Ya=[];for(let t=this.Ua;1!==t;){if(t%2==0)this.Ya.push(2),t/=2;else{if(t%5!=0)throw new Error("unexpected base");this.Ya.push(2,2.5),t/=5}if(this.Ya.length>100)throw new Error("something wrong with base")}}}Za(t,i,n){const s=0===this.Ua?0:1/this.Ua;let e=Math.pow(10,Math.max(0,Math.ceil(Math.log10(t-i)))),r=0,h=this.qa[0];for(;;){const t=Mt(e,s,1e-14)&&e>s+1e-14,i=Mt(e,n*h,1e-14),l=Mt(e,1,1e-14);if(!(t&&i&&l))break;e/=h,h=this.qa[++r%this.qa.length]}if(e<=s+1e-14&&(e=s),e=Math.max(1,e),this.Ya.length>0&&(l=e,a=1,o=1e-14,Math.abs(l-a)s+1e-14;)e/=h,h=this.Ya[++r%this.Ya.length];var l,a,o;return e}}class _n{constructor(t,i,n,s){this.Xa=[],this.Ii=t,this.Ua=i,this.Ka=n,this.Ga=s}Za(t,i){if(t=o?1:-1;let d=null,f=0;for(let n=a-u;n>o;n-=_){const s=this.Ga(n,i,!0);null!==d&&Math.abs(s-d)l||(ff(t.Xi())-f(i.Xi())))}var cn;!function(t){t[t.Normal=0]="Normal",t[t.Logarithmic=1]="Logarithmic",t[t.Percentage=2]="Percentage",t[t.IndexedTo100=3]="IndexedTo100"}(cn||(cn={}));const dn=new ut,fn=new _t(100,1);class vn{constructor(t,i,n,s){this.ro=0,this.ho=null,this.Ah=null,this.lo=null,this.ao={oo:!1,_o:null},this.uo=0,this.co=0,this.do=new C,this.fo=new C,this.vo=[],this.po=null,this.mo=null,this.bo=null,this.wo=null,this.ba=fn,this.Mo=an(null),this.xo=t,this.cn=i,this.So=n,this.ko=s,this.yo=new _n(this,100,this.Co.bind(this),this.To.bind(this))}Ta(){return this.xo}W(){return this.cn}Hh(t){if(T(this.cn,t),this.Pa(),void 0!==t.mode&&this.Po({yr:t.mode}),void 0!==t.scaleMargins){const i=d(t.scaleMargins.top),n=d(t.scaleMargins.bottom);if(i<0||i>1)throw new Error(`Invalid top margin - expect value between 0 and 1, given=${i}`);if(n<0||n>1)throw new Error(`Invalid bottom margin - expect value between 0 and 1, given=${n}`);if(i+n>1)throw new Error(`Invalid margins - sum of margins must be less than 1, given=${i+n}`);this.Ro(),this.mo=null}}Do(){return this.cn.autoScale}eo(){return 1===this.cn.mode}gh(){return 2===this.cn.mode}Oo(){return 3===this.cn.mode}yr(){return{Wn:this.cn.autoScale,Vo:this.cn.invertScale,yr:this.cn.mode}}Po(t){const i=this.yr();let n=null;void 0!==t.Wn&&(this.cn.autoScale=t.Wn),void 0!==t.yr&&(this.cn.mode=t.yr,2!==t.yr&&3!==t.yr||(this.cn.autoScale=!0),this.ao.oo=!1),1===i.yr&&t.yr!==i.yr&&(!function(t,i){if(null===t)return!1;const n=rn(t.Th(),i),s=rn(t.Ph(),i);return isFinite(n)&&isFinite(s)}(this.Ah,this.Mo)?this.cn.autoScale=!0:(n=ln(this.Ah,this.Mo),null!==n&&this.Bo(n))),1===t.yr&&t.yr!==i.yr&&(n=hn(this.Ah,this.Mo),null!==n&&this.Bo(n));const s=i.yr!==this.cn.mode;s&&(2===i.yr||this.gh())&&this.Pa(),s&&(3===i.yr||this.Oo())&&this.Pa(),void 0!==t.Vo&&i.Vo!==t.Vo&&(this.cn.invertScale=t.Vo,this.Ao()),this.fo.m(i,this.yr())}zo(){return this.fo}P(){return this.So.fontSize}At(){return this.ro}Eo(t){this.ro!==t&&(this.ro=t,this.Ro(),this.mo=null)}Io(){if(this.ho)return this.ho;const t=this.At()-this.Lo()-this.No();return this.ho=t,t}Eh(){return this.Fo(),this.Ah}Bo(t,i){const n=this.Ah;(i||null===n&&null!==t||null!==n&&!n.yh(t))&&(this.mo=null,this.Ah=t)}Ni(){return this.Fo(),0===this.ro||!this.Ah||this.Ah.Ni()}Wo(t){return this.Vo()?t:this.At()-1-t}Rt(t,i){return this.gh()?t=Qi(t,i):this.Oo()&&(t=nn(t,i)),this.To(t,i)}Qs(t,i,n){this.Fo();const s=this.No(),e=f(this.Eh()),r=e.Th(),h=e.Ph(),l=this.Io()-1,a=this.Vo(),o=l/(h-r),_=void 0===n?0:n.from,u=void 0===n?t.length:n.to,c=this.jo();for(let n=_;nt.On()))}Pa(){this.mo=null;const t=this.s_();let i=100;null!==t&&(i=Math.round(1/t.pa())),this.ba=fn,this.gh()?(this.ba=dn,i=100):this.Oo()?(this.ba=new _t(100,1),i=100):null!==t&&(this.ba=t.ma()),this.yo=new _n(this,i,this.Co.bind(this),this.To.bind(this)),this.yo.Qa()}qo(){this.po=null}s_(){return this.vo[0]||null}Lo(){return this.Vo()?this.cn.scaleMargins.bottom*this.At()+this.co:this.cn.scaleMargins.top*this.At()+this.uo}No(){return this.Vo()?this.cn.scaleMargins.top*this.At()+this.uo:this.cn.scaleMargins.bottom*this.At()+this.co}Fo(){this.ao.oo||(this.ao.oo=!0,this.h_())}Ro(){this.ho=null}To(t,i){if(this.Fo(),this.Ni())return 0;t=this.eo()&&t?en(t,this.Mo):t;const n=f(this.Eh()),s=this.No()+(this.Io()-1)*(t-n.Th())/n.Rh();return this.Wo(s)}Co(t,i){if(this.Fo(),this.Ni())return 0;const n=this.Wo(t),s=f(this.Eh()),e=s.Th()+s.Rh()*((n-this.No())/(this.Io()-1));return this.eo()?rn(e,this.Mo):e}Ao(){this.mo=null,this.yo.Qa()}h_(){const t=this.ao._o;if(null===t)return;let i=null;const n=this.e_();let s=0,e=0;for(const r of n){if(!r.yt())continue;const n=r.Ct();if(null===n)continue;const h=r.Al(t.Os(),t.ui());let l=h&&h.Eh();if(null!==l){switch(this.cn.mode){case 1:l=hn(l,this.Mo);break;case 2:l=tn(l,n.Ot);break;case 3:l=sn(l,n.Ot)}if(i=null===i?l:i.ts(f(l)),null!==h){const t=h.Ih();null!==t&&(s=Math.max(s,t.above),e=Math.max(e,t.below))}}}if(s===this.uo&&e===this.co||(this.uo=s,this.co=e,this.mo=null,this.Ro()),null!==i){if(i.Th()===i.Ph()){const t=this.s_(),n=5*(null===t||this.gh()||this.Oo()?1:t.pa());this.eo()&&(i=ln(i,this.Mo)),i=new yi(i.Th()-n,i.Ph()+n),this.eo()&&(i=hn(i,this.Mo))}if(this.eo()){const t=ln(i,this.Mo),n=an(t);if(r=n,h=this.Mo,r.Ha!==h.Ha||r.$a!==h.$a){const s=null!==this.lo?ln(this.lo,this.Mo):null;this.Mo=n,i=hn(t,n),null!==s&&(this.lo=hn(s,n))}}this.Bo(i)}else null===this.Ah&&(this.Bo(new yi(-.5,.5)),this.Mo=an(null));var r,h;this.ao.oo=!0}jo(){return this.gh()?Qi:this.Oo()?nn:this.eo()?t=>en(t,this.Mo):null}l_(t,i,n){return void 0===i?(void 0===n&&(n=this.ma()),n.format(t)):i(t)}Fh(t,i){return this.l_(t,this.ko.priceFormatter,i)}n_(t,i){return this.l_(t,this.ko.percentageFormatter,i)}}class pn{constructor(t,i){this.vo=[],this.a_=new Map,this.ro=0,this.o_=0,this.__=1e3,this.po=null,this.u_=new C,this.kl=t,this.$i=i,this.c_=new Gi(this);const n=i.W();this.d_=this.f_("left",n.leftPriceScale),this.v_=this.f_("right",n.rightPriceScale),this.d_.zo().l(this.p_.bind(this,this.d_),this),this.v_.zo().l(this.p_.bind(this,this.v_),this),this.m_(n)}m_(t){if(t.leftPriceScale&&this.d_.Hh(t.leftPriceScale),t.rightPriceScale&&this.v_.Hh(t.rightPriceScale),t.localization&&(this.d_.Pa(),this.v_.Pa()),t.overlayPriceScales){const i=Array.from(this.a_.values());for(const n of i){const i=f(n[0].Dt());i.Hh(t.overlayPriceScales),t.localization&&i.Pa()}}}b_(t){switch(t){case"left":return this.d_;case"right":return this.v_}return this.a_.has(t)?d(this.a_.get(t))[0].Dt():null}S(){this.$t().w_().p(this),this.d_.zo().p(this),this.v_.zo().p(this),this.vo.forEach((t=>{t.S&&t.S()})),this.u_.m()}g_(){return this.__}M_(t){this.__=t}$t(){return this.$i}Hi(){return this.o_}At(){return this.ro}x_(t){this.o_=t,this.S_()}Eo(t){this.ro=t,this.d_.Eo(t),this.v_.Eo(t),this.vo.forEach((i=>{if(this.dr(i)){const n=i.Dt();null!==n&&n.Eo(t)}})),this.S_()}Va(){return this.vo}dr(t){const i=t.Dt();return null===i||this.d_!==i&&this.v_!==i}Uo(t,i,n){const s=void 0!==n?n:this.y_().k_+1;this.C_(t,i,s)}Yo(t){const i=this.vo.indexOf(t);c(-1!==i,"removeDataSource: invalid data source"),this.vo.splice(i,1);const n=f(t.Dt()).Ta();if(this.a_.has(n)){const i=d(this.a_.get(n)),s=i.indexOf(t);-1!==s&&(i.splice(s,1),0===i.length&&this.a_.delete(n))}const s=t.Dt();s&&s.Va().indexOf(t)>=0&&s.Yo(t),null!==s&&(s.qo(),this.T_(s)),this.po=null}pr(t){return t===this.d_?"left":t===this.v_?"right":"overlay"}P_(){return this.d_}R_(){return this.v_}D_(t,i){t.Ko(i)}O_(t,i){t.Go(i),this.S_()}V_(t){t.Jo()}B_(t,i){t.Qo(i)}A_(t,i){t.t_(i),this.S_()}z_(t){t.i_()}S_(){this.vo.forEach((t=>{t.On()}))}vn(){let t=null;return this.$i.W().rightPriceScale.visible&&0!==this.v_.Va().length?t=this.v_:this.$i.W().leftPriceScale.visible&&0!==this.d_.Va().length?t=this.d_:0!==this.vo.length&&(t=this.vo[0].Dt()),null===t&&(t=this.v_),t}vr(){let t=null;return this.$i.W().rightPriceScale.visible?t=this.v_:this.$i.W().leftPriceScale.visible&&(t=this.d_),t}T_(t){null!==t&&t.Do()&&this.E_(t)}I_(t){const i=this.kl.Zs();t.Po({Wn:!0}),null!==i&&t.r_(i),this.S_()}L_(){this.E_(this.d_),this.E_(this.v_)}N_(){this.T_(this.d_),this.T_(this.v_),this.vo.forEach((t=>{this.dr(t)&&this.T_(t.Dt())})),this.S_(),this.$i.$h()}$o(){return null===this.po&&(this.po=un(this.vo)),this.po}F_(){return this.u_}W_(){return this.c_}E_(t){const i=t.e_();if(i&&i.length>0&&!this.kl.Ni()){const i=this.kl.Zs();null!==i&&t.r_(i)}t.On()}y_(){const t=this.$o();if(0===t.length)return{j_:0,k_:0};let i=0,n=0;for(let s=0;sn&&(n=e))}return{j_:i,k_:n}}C_(t,i,n){let s=this.b_(i);if(null===s&&(s=this.f_(i,this.$i.W().overlayPriceScales)),this.vo.push(t),!ht(i)){const n=this.a_.get(i)||[];n.push(t),this.a_.set(i,n)}s.Uo(t),t.Gi(s),t.Ki(n),this.T_(s),this.po=null}p_(t,i,n){i.yr!==n.yr&&this.E_(t)}f_(t,i){const n=Object.assign({visible:!0,autoScale:!0},V(i)),s=new vn(t,n,this.$i.W().layout,this.$i.W().localization);return s.Eo(this.At()),s}}class mn{constructor(t,i,n=50){this.Xe=0,this.Ke=1,this.Ge=1,this.Qe=new Map,this.Je=new Map,this.H_=t,this.U_=i,this.tr=n}q_(t){const i=t.time,n=this.U_.cacheKey(i),s=this.Qe.get(n);if(void 0!==s)return s.Y_;if(this.Xe===this.tr){const t=this.Je.get(this.Ge);this.Je.delete(this.Ge),this.Qe.delete(d(t)),this.Ge++,this.Xe--}const e=this.H_(t);return this.Qe.set(n,{Y_:e,er:this.Ke}),this.Je.set(this.Ke,n),this.Xe++,this.Ke++,e}}class bn{constructor(t,i){c(t<=i,"right should be >= left"),this.Z_=t,this.X_=i}Os(){return this.Z_}ui(){return this.X_}K_(){return this.X_-this.Z_+1}Xr(t){return this.Z_<=t&&t<=this.X_}yh(t){return this.Z_===t.Os()&&this.X_===t.ui()}}function wn(t,i){return null===t||null===i?t===i:t.yh(i)}class gn{constructor(){this.G_=new Map,this.Qe=null,this.J_=!1}Q_(t){this.J_=t,this.Qe=null}tu(t,i){this.iu(i),this.Qe=null;for(let n=i;n{t<=n[0].index?i.push(s):n.splice(Rt(n,t,(i=>i.indexi-t))){if(!this.G_.get(n))continue;const s=i;i=[];const e=s.length;let r=0;const h=d(this.G_.get(n)),l=h.length;let a=1/0,o=-1/0;for(let n=0;n=t&&_-o>=t)i.push(l),o=_;else if(this.J_)return s}for(;ri.weight?t:i}class Sn{constructor(t,i,n,s){this.o_=0,this.ou=null,this._u=[],this.wo=null,this.bo=null,this.uu=new gn,this.cu=new Map,this.du=Mn.au(),this.fu=!0,this.vu=new C,this.pu=new C,this.mu=new C,this.bu=null,this.wu=null,this.gu=[],this.cn=i,this.ko=n,this.Mu=i.rightOffset,this.xu=i.barSpacing,this.$i=t,this.U_=s,this.Su(),this.uu.Q_(i.uniformDistribution)}W(){return this.cn}ku(t){T(this.ko,t),this.yu(),this.Su()}Hh(t,i){var n;T(this.cn,t),this.cn.fixLeftEdge&&this.Cu(),this.cn.fixRightEdge&&this.Tu(),void 0!==t.barSpacing&&this.$i.Gn(t.barSpacing),void 0!==t.rightOffset&&this.$i.Jn(t.rightOffset),void 0!==t.minBarSpacing&&this.$i.Gn(null!==(n=t.barSpacing)&&void 0!==n?n:this.xu),this.yu(),this.Su(),this.mu.m()}mn(t){var i,n;return null!==(n=null===(i=this._u[t])||void 0===i?void 0:i.time)&&void 0!==n?n:null}Ui(t){var i;return null!==(i=this._u[t])&&void 0!==i?i:null}Da(t,i){if(this._u.length<1)return null;if(this.U_.key(t)>this.U_.key(this._u[this._u.length-1].time))return i?this._u.length-1:null;const n=Rt(this._u,this.U_.key(t),((t,i)=>this.U_.key(t.time)0}Zs(){return this.Pu(),this.du.hu()}Ru(){return this.Pu(),this.du.lu()}Du(){const t=this.Zs();if(null===t)return null;const i={from:t.Os(),to:t.ui()};return this.Ou(i)}Ou(t){const i=Math.round(t.from),n=Math.round(t.to),s=f(this.Vu()),e=f(this.Bu());return{from:f(this.Ui(Math.max(s,i))),to:f(this.Ui(Math.min(e,n)))}}Au(t){return{from:f(this.Da(t.from,!0)),to:f(this.Da(t.to,!0))}}Hi(){return this.o_}x_(t){if(!isFinite(t)||t<=0)return;if(this.o_===t)return;const i=this.Ru(),n=this.o_;if(this.o_=t,this.fu=!0,this.cn.lockVisibleTimeRangeOnResize&&0!==n){const i=this.xu*t/n;this.xu=i}if(this.cn.fixLeftEdge&&null!==i&&i.Os()<=0){const i=n-t;this.Mu-=Math.round(i/this.xu)+1,this.fu=!0}this.zu(),this.Eu()}zt(t){if(this.Ni()||!R(t))return 0;const i=this.Iu()+this.Mu-t;return this.o_-(i+.5)*this.xu-1}Js(t,i){const n=this.Iu(),s=void 0===i?0:i.from,e=void 0===i?t.length:i.to;for(let i=s;ii/2&&!o?n.needAlignCoordinate=!1:n.needAlignCoordinate=_&&t.index<=l||u&&t.index>=a,c++}return this.gu.length=c,this.wu=this.gu,this.gu}Uu(){this.fu=!0,this.Gn(this.cn.barSpacing),this.Jn(this.cn.rightOffset)}qu(t){this.fu=!0,this.ou=t,this.Eu(),this.Cu()}Yu(t,i){const n=this.Nu(t),s=this.he(),e=s+i*(s/10);this.Gn(e),this.cn.rightBarStaysOnScroll||this.Jn(this.ju()+(n-this.Nu(t)))}Ko(t){this.wo&&this.i_(),null===this.bo&&null===this.bu&&(this.Ni()||(this.bo=t,this.Zu()))}Go(t){if(null===this.bu)return;const i=gt(this.o_-t,0,this.o_),n=gt(this.o_-f(this.bo),0,this.o_);0!==i&&0!==n&&this.Gn(this.bu.he*i/n)}Jo(){null!==this.bo&&(this.bo=null,this.Xu())}Qo(t){null===this.wo&&null===this.bu&&(this.Ni()||(this.wo=t,this.Zu()))}t_(t){if(null===this.wo)return;const i=(this.wo-t)/this.he();this.Mu=f(this.bu).ju+i,this.fu=!0,this.Eu()}i_(){null!==this.wo&&(this.wo=null,this.Xu())}Ku(){this.Gu(this.cn.rightOffset)}Gu(t,i=400){if(!isFinite(t))throw new RangeError("offset is required and must be finite number");if(!isFinite(i)||i<=0)throw new RangeError("animationDuration (optional) must be finite positive number");const n=this.Mu,s=performance.now();this.$i.Zn({Ju:t=>(t-s)/i>=1,Qu:e=>{const r=(e-s)/i;return r>=1?t:n+(t-n)*r}})}bt(t,i){this.fu=!0,this._u=t,this.uu.tu(t,i),this.Eu()}tc(){return this.vu}nc(){return this.pu}sc(){return this.mu}Iu(){return this.ou||0}ec(t){const i=t.K_();this.Wu(this.o_/i),this.Mu=t.ui()-this.Iu(),this.Eu(),this.fu=!0,this.$i.Fu(),this.$i.$h()}rc(){const t=this.Vu(),i=this.Bu();null!==t&&null!==i&&this.ec(new bn(t,i+this.cn.rightOffset))}hc(t){const i=new bn(t.from,t.to);this.ec(i)}qi(t){return void 0!==this.ko.timeFormatter?this.ko.timeFormatter(t.originalTime):this.U_.formatHorzItem(t.time)}Hu(){const{handleScroll:t,handleScale:i}=this.$i.W();return!(t.horzTouchDrag||t.mouseWheel||t.pressedMouseMove||t.vertTouchDrag||i.axisDoubleClickReset.time||i.axisPressedMouseMove.time||i.mouseWheel||i.pinch)}Vu(){return 0===this._u.length?null:0}Bu(){return 0===this._u.length?null:this._u.length-1}lc(t){return(this.o_-1-t)/this.xu}Nu(t){const i=this.lc(t),n=this.Iu()+this.Mu-i;return Math.round(1e6*n)/1e6}Wu(t){const i=this.xu;this.xu=t,this.zu(),i!==this.xu&&(this.fu=!0,this.ac())}Pu(){if(!this.fu)return;if(this.fu=!1,this.Ni())return void this.oc(Mn.au());const t=this.Iu(),i=this.o_/this.xu,n=this.Mu+t,s=new bn(n-i+1,n);this.oc(new Mn(s))}zu(){const t=this._c();if(this.xut&&(this.xu=t,this.fu=!0)}}_c(){return this.cn.fixLeftEdge&&this.cn.fixRightEdge&&0!==this._u.length?this.o_/this._u.length:this.cn.minBarSpacing}Eu(){const t=this.uc();null!==t&&this.Mui&&(this.Mu=i,this.fu=!0)}uc(){const t=this.Vu(),i=this.ou;if(null===t||null===i)return null;return t-i-1+(this.cn.fixLeftEdge?this.o_/this.xu:Math.min(2,this._u.length))}cc(){return this.cn.fixRightEdge?0:this.o_/this.xu-Math.min(2,this._u.length)}Zu(){this.bu={he:this.he(),ju:this.ju()}}Xu(){this.bu=null}$u(t){let i=this.cu.get(t.weight);return void 0===i&&(i=new mn((t=>this.dc(t)),this.U_),this.cu.set(t.weight,i)),i.q_(t)}dc(t){return this.U_.formatTickmark(t,this.ko)}oc(t){const i=this.du;this.du=t,wn(i.hu(),this.du.hu())||this.vu.m(),wn(i.lu(),this.du.lu())||this.pu.m(),this.ac()}ac(){this.wu=null}yu(){this.ac(),this.cu.clear()}Su(){this.U_.updateFormatter(this.ko)}Cu(){if(!this.cn.fixLeftEdge)return;const t=this.Vu();if(null===t)return;const i=this.Zs();if(null===i)return;const n=i.Os()-t;if(n<0){const t=this.Mu-n-1;this.Jn(t)}this.zu()}Tu(){this.Eu(),this.zu()}}class kn{X(t,i,n){t.useMediaCoordinateSpace((t=>this.K(t,i,n)))}wl(t,i,n){t.useMediaCoordinateSpace((t=>this.fc(t,i,n)))}fc(t,i,n){}}class yn extends kn{constructor(t){super(),this.vc=new Map,this.Et=t}K(t){}fc(t){if(!this.Et.yt)return;const{context:i,mediaSize:n}=t;let s=0;for(const t of this.Et.mc){if(0===t.Kt.length)continue;i.font=t.R;const e=this.bc(i,t.Kt);e>n.width?t.Yu=n.width/e:t.Yu=1,s+=t.wc*t.Yu}let e=0;switch(this.Et.gc){case"top":e=0;break;case"center":e=Math.max((n.height-s)/2,0);break;case"bottom":e=Math.max(n.height-s,0)}i.fillStyle=this.Et.O;for(const t of this.Et.mc){i.save();let s=0;switch(this.Et.Mc){case"left":i.textAlign="left",s=t.wc/2;break;case"center":i.textAlign="center",s=n.width/2;break;case"right":i.textAlign="right",s=n.width-1-t.wc/2}i.translate(s,e),i.textBaseline="top",i.font=t.R,i.scale(t.Yu,t.Yu),i.fillText(t.Kt,0,t.xc),i.restore(),e+=t.wc*t.Yu}}bc(t,i){const n=this.Sc(t.font);let s=n.get(i);return void 0===s&&(s=t.measureText(i).width,n.set(i,s)),s}Sc(t){let i=this.vc.get(t);return void 0===i&&(i=new Map,this.vc.set(t,i)),i}}class Cn{constructor(t){this.ft=!0,this.Ft={yt:!1,O:"",mc:[],gc:"center",Mc:"center"},this.Wt=new yn(this.Ft),this.jt=t}bt(){this.ft=!0}gt(){return this.ft&&(this.Mt(),this.ft=!1),this.Wt}Mt(){const t=this.jt.W(),i=this.Ft;i.yt=t.visible,i.yt&&(i.O=t.color,i.Mc=t.horzAlign,i.gc=t.vertAlign,i.mc=[{Kt:t.text,R:E(t.fontSize,t.fontFamily,t.fontStyle),wc:1.2*t.fontSize,xc:0,Yu:0}])}}class Tn extends st{constructor(t,i){super(),this.cn=i,this.wn=new Cn(this)}Rn(){return[]}Pn(){return[this.wn]}W(){return this.cn}On(){this.wn.bt()}}var Pn,Rn,Dn,On,Vn;!function(t){t[t.OnTouchEnd=0]="OnTouchEnd",t[t.OnNextTap=1]="OnNextTap"}(Pn||(Pn={}));class Bn{constructor(t,i,n){this.kc=[],this.yc=[],this.o_=0,this.Cc=null,this.Tc=new C,this.Pc=new C,this.Rc=null,this.Dc=t,this.cn=i,this.U_=n,this.Oc=new I(this),this.kl=new Sn(this,i.timeScale,this.cn.localization,n),this.vt=new rt(this,i.crosshair),this.Vc=new Zi(i.crosshair),this.Bc=new Tn(this,i.watermark),this.Ac(),this.kc[0].M_(2e3),this.zc=this.Ec(0),this.Ic=this.Ec(1)}Xl(){this.Lc(lt.es())}$h(){this.Lc(lt.ss())}aa(){this.Lc(new lt(1))}Kl(t){const i=this.Nc(t);this.Lc(i)}Fc(){return this.Cc}Wc(t){const i=this.Cc;this.Cc=t,null!==i&&this.Kl(i.jc),null!==t&&this.Kl(t.jc)}W(){return this.cn}Hh(t){T(this.cn,t),this.kc.forEach((i=>i.m_(t))),void 0!==t.timeScale&&this.kl.Hh(t.timeScale),void 0!==t.localization&&this.kl.ku(t.localization),(t.leftPriceScale||t.rightPriceScale)&&this.Tc.m(),this.zc=this.Ec(0),this.Ic=this.Ec(1),this.Xl()}Hc(t,i){if("left"===t)return void this.Hh({leftPriceScale:i});if("right"===t)return void this.Hh({rightPriceScale:i});const n=this.$c(t);null!==n&&(n.Dt.Hh(i),this.Tc.m())}$c(t){for(const i of this.kc){const n=i.b_(t);if(null!==n)return{Ht:i,Dt:n}}return null}St(){return this.kl}Uc(){return this.kc}qc(){return this.Bc}Yc(){return this.vt}Zc(){return this.Pc}Xc(t,i){t.Eo(i),this.Fu()}x_(t){this.o_=t,this.kl.x_(this.o_),this.kc.forEach((i=>i.x_(t))),this.Fu()}Ac(t){const i=new pn(this.kl,this);void 0!==t?this.kc.splice(t,0,i):this.kc.push(i);const n=void 0===t?this.kc.length-1:t,s=lt.es();return s.Nn(n,{Fn:0,Wn:!0}),this.Lc(s),i}D_(t,i,n){t.D_(i,n)}O_(t,i,n){t.O_(i,n),this.Gl(),this.Lc(this.Kc(t,2))}V_(t,i){t.V_(i),this.Lc(this.Kc(t,2))}B_(t,i,n){i.Do()||t.B_(i,n)}A_(t,i,n){i.Do()||(t.A_(i,n),this.Gl(),this.Lc(this.Kc(t,2)))}z_(t,i){i.Do()||(t.z_(i),this.Lc(this.Kc(t,2)))}I_(t,i){t.I_(i),this.Lc(this.Kc(t,2))}Gc(t){this.kl.Ko(t)}Jc(t,i){const n=this.St();if(n.Ni()||0===i)return;const s=n.Hi();t=Math.max(1,Math.min(t,s)),n.Yu(t,i),this.Fu()}Qc(t){this.td(0),this.nd(t),this.sd()}ed(t){this.kl.Go(t),this.Fu()}rd(){this.kl.Jo(),this.$h()}td(t){this.kl.Qo(t)}nd(t){this.kl.t_(t),this.Fu()}sd(){this.kl.i_(),this.$h()}wt(){return this.yc}hd(t,i,n,s,e){this.vt.gn(t,i);let r=NaN,h=this.kl.Lu(t);const l=this.kl.Zs();null!==l&&(h=Math.min(Math.max(l.Os(),h),l.ui()));const a=s.vn(),o=a.Ct();null!==o&&(r=a.pn(i,o)),r=this.Vc.Oa(r,h,s),this.vt.kn(h,r,s),this.aa(),e||this.Pc.m(this.vt.xt(),{x:t,y:i},n)}ld(t,i,n){const s=n.vn(),e=s.Ct(),r=s.Rt(t,f(e)),h=this.kl.Da(i,!0),l=this.kl.zt(f(h));this.hd(l,r,null,n,!0)}ad(t){this.Yc().Cn(),this.aa(),t||this.Pc.m(null,null,null)}Gl(){const t=this.vt.Ht();if(null!==t){const i=this.vt.xn(),n=this.vt.Sn();this.hd(i,n,null,t)}this.vt.On()}od(t,i,n){const s=this.kl.mn(0);void 0!==i&&void 0!==n&&this.kl.bt(i,n);const e=this.kl.mn(0),r=this.kl.Iu(),h=this.kl.Zs();if(null!==h&&null!==s&&null!==e){const i=h.Xr(r),l=this.U_.key(s)>this.U_.key(e),a=null!==t&&t>r&&!l,o=this.kl.W().allowShiftVisibleRangeOnWhitespaceReplacement,_=i&&(!(void 0===n)||o)&&this.kl.W().shiftVisibleRangeOnNewBar;if(a&&!_){const i=t-r;this.kl.Jn(this.kl.ju()-i)}}this.kl.qu(t)}ta(t){null!==t&&t.N_()}cr(t){const i=this.kc.find((i=>i.$o().includes(t)));return void 0===i?null:i}Fu(){this.Bc.On(),this.kc.forEach((t=>t.N_())),this.Gl()}S(){this.kc.forEach((t=>t.S())),this.kc.length=0,this.cn.localization.priceFormatter=void 0,this.cn.localization.percentageFormatter=void 0,this.cn.localization.timeFormatter=void 0}_d(){return this.Oc}mr(){return this.Oc.W()}w_(){return this.Tc}ud(t,i,n){const s=this.kc[0],e=this.dd(i,t,s,n);return this.yc.push(e),1===this.yc.length?this.Xl():this.$h(),e}fd(t){const i=this.cr(t),n=this.yc.indexOf(t);c(-1!==n,"Series not found"),this.yc.splice(n,1),f(i).Yo(t),t.S&&t.S()}Zl(t,i){const n=f(this.cr(t));n.Yo(t);const s=this.$c(i);if(null===s){const s=t.Xi();n.Uo(t,i,s)}else{const e=s.Ht===n?t.Xi():void 0;s.Ht.Uo(t,i,e)}}rc(){const t=lt.ss();t.$n(),this.Lc(t)}vd(t){const i=lt.ss();i.Yn(t),this.Lc(i)}Kn(){const t=lt.ss();t.Kn(),this.Lc(t)}Gn(t){const i=lt.ss();i.Gn(t),this.Lc(i)}Jn(t){const i=lt.ss();i.Jn(t),this.Lc(i)}Zn(t){const i=lt.ss();i.Zn(t),this.Lc(i)}Un(){const t=lt.ss();t.Un(),this.Lc(t)}pd(){return this.cn.rightPriceScale.visible?"right":"left"}md(){return this.Ic}q(){return this.zc}Bt(t){const i=this.Ic,n=this.zc;if(i===n)return i;if(t=Math.max(0,Math.min(100,Math.round(100*t))),null===this.Rc||this.Rc.Ts!==n||this.Rc.Ps!==i)this.Rc={Ts:n,Ps:i,bd:new Map};else{const i=this.Rc.bd.get(t);if(void 0!==i)return i}const s=function(t,i,n){const[s,e,r,h]=S(t),[l,a,o,_]=S(i),u=[m(s+n*(l-s)),m(e+n*(a-e)),m(r+n*(o-r)),b(h+n*(_-h))];return`rgba(${u[0]}, ${u[1]}, ${u[2]}, ${u[3]})`}(n,i,t/100);return this.Rc.bd.set(t,s),s}Kc(t,i){const n=new lt(i);if(null!==t){const s=this.kc.indexOf(t);n.Nn(s,{Fn:i})}return n}Nc(t,i){return void 0===i&&(i=2),this.Kc(this.cr(t),i)}Lc(t){this.Dc&&this.Dc(t),this.kc.forEach((t=>t.W_().Uh().bt()))}dd(t,i,n,s){const e=new Yi(this,t,i,n,s),r=void 0!==t.priceScaleId?t.priceScaleId:this.pd();return n.Uo(e,r),ht(r)||e.Hh(t),e}Ec(t){const i=this.cn.layout;return"gradient"===i.background.type?0===t?i.background.topColor:i.background.bottomColor:i.background.color}}function An(t){return!P(t)&&!D(t)}function zn(t){return P(t)}!function(t){t[t.Disabled=0]="Disabled",t[t.Continuous=1]="Continuous",t[t.OnDataUpdate=2]="OnDataUpdate"}(Rn||(Rn={})),function(t){t[t.LastBar=0]="LastBar",t[t.LastVisible=1]="LastVisible"}(Dn||(Dn={})),function(t){t.Solid="solid",t.VerticalGradient="gradient"}(On||(On={})),function(t){t[t.Year=0]="Year",t[t.Month=1]="Month",t[t.DayOfMonth=2]="DayOfMonth",t[t.Time=3]="Time",t[t.TimeWithSeconds=4]="TimeWithSeconds"}(Vn||(Vn={}));const En=t=>t.getUTCFullYear();function In(t,i,n){return i.replace(/yyyy/g,(t=>ot(En(t),4))(t)).replace(/yy/g,(t=>ot(En(t)%100,2))(t)).replace(/MMMM/g,((t,i)=>new Date(t.getUTCFullYear(),t.getUTCMonth(),1).toLocaleString(i,{month:"long"}))(t,n)).replace(/MMM/g,((t,i)=>new Date(t.getUTCFullYear(),t.getUTCMonth(),1).toLocaleString(i,{month:"short"}))(t,n)).replace(/MM/g,(t=>ot((t=>t.getUTCMonth()+1)(t),2))(t)).replace(/dd/g,(t=>ot((t=>t.getUTCDate())(t),2))(t))}class Ln{constructor(t="yyyy-MM-dd",i="default"){this.wd=t,this.gd=i}q_(t){return In(t,this.wd,this.gd)}}class Nn{constructor(t){this.Md=t||"%h:%m:%s"}q_(t){return this.Md.replace("%h",ot(t.getUTCHours(),2)).replace("%m",ot(t.getUTCMinutes(),2)).replace("%s",ot(t.getUTCSeconds(),2))}}const Fn={xd:"yyyy-MM-dd",Sd:"%h:%m:%s",kd:" ",yd:"default"};class Wn{constructor(t={}){const i=Object.assign(Object.assign({},Fn),t);this.Cd=new Ln(i.xd,i.yd),this.Td=new Nn(i.Sd),this.Pd=i.kd}q_(t){return`${this.Cd.q_(t)}${this.Pd}${this.Td.q_(t)}`}}function jn(t){return 60*t*60*1e3}function Hn(t){return 60*t*1e3}const $n=[{Rd:(Un=1,1e3*Un),Dd:10},{Rd:Hn(1),Dd:20},{Rd:Hn(5),Dd:21},{Rd:Hn(30),Dd:22},{Rd:jn(1),Dd:30},{Rd:jn(3),Dd:31},{Rd:jn(6),Dd:32},{Rd:jn(12),Dd:33}];var Un;function qn(t,i){if(t.getUTCFullYear()!==i.getUTCFullYear())return 70;if(t.getUTCMonth()!==i.getUTCMonth())return 60;if(t.getUTCDate()!==i.getUTCDate())return 50;for(let n=$n.length-1;n>=0;--n)if(Math.floor(i.getTime()/$n[n].Rd)!==Math.floor(t.getTime()/$n[n].Rd))return $n[n].Dd;return 0}function Yn(t){let i=t;if(D(t)&&(i=Xn(t)),!An(i))throw new Error("time must be of type BusinessDay");const n=new Date(Date.UTC(i.year,i.month-1,i.day,0,0,0,0));return{Od:Math.round(n.getTime()/1e3),Vd:i}}function Zn(t){if(!zn(t))throw new Error("time must be of type isUTCTimestamp");return{Od:t}}function Xn(t){const i=new Date(t);if(isNaN(i.getTime()))throw new Error(`Invalid date string=${t}, expected format=yyyy-mm-dd`);return{day:i.getUTCDate(),month:i.getUTCMonth()+1,year:i.getUTCFullYear()}}function Kn(t){D(t.time)&&(t.time=Xn(t.time))}class Gn{options(){return this.cn}setOptions(t){this.cn=t,this.updateFormatter(t.localization)}preprocessData(t){Array.isArray(t)?function(t){t.forEach(Kn)}(t):Kn(t)}createConverterToInternalObj(t){return f(function(t){return 0===t.length?null:An(t[0].time)||D(t[0].time)?Yn:Zn}(t))}key(t){return"object"==typeof t&&"Od"in t?t.Od:this.key(this.convertHorzItemToInternal(t))}cacheKey(t){const i=t;return void 0===i.Vd?new Date(1e3*i.Od).getTime():new Date(Date.UTC(i.Vd.year,i.Vd.month-1,i.Vd.day)).getTime()}convertHorzItemToInternal(t){return zn(i=t)?Zn(i):An(i)?Yn(i):Yn(Xn(i));var i}updateFormatter(t){if(!this.cn)return;const i=t.dateFormat;this.cn.timeScale.timeVisible?this.Bd=new Wn({xd:i,Sd:this.cn.timeScale.secondsVisible?"%h:%m:%s":"%h:%m",kd:" ",yd:t.locale}):this.Bd=new Ln(i,t.locale)}formatHorzItem(t){const i=t;return this.Bd.q_(new Date(1e3*i.Od))}formatTickmark(t,i){const n=function(t,i,n){switch(t){case 0:case 10:return i?n?4:3:2;case 20:case 21:case 22:case 30:case 31:case 32:case 33:return i?3:2;case 50:return 2;case 60:return 1;case 70:return 0}}(t.weight,this.cn.timeScale.timeVisible,this.cn.timeScale.secondsVisible),s=this.cn.timeScale;if(void 0!==s.tickMarkFormatter){const e=s.tickMarkFormatter(t.originalTime,n,i.locale);if(null!==e)return e}return function(t,i,n){const s={};switch(i){case 0:s.year="numeric";break;case 1:s.month="short";break;case 2:s.day="numeric";break;case 3:s.hour12=!1,s.hour="2-digit",s.minute="2-digit";break;case 4:s.hour12=!1,s.hour="2-digit",s.minute="2-digit",s.second="2-digit"}const e=void 0===t.Vd?new Date(1e3*t.Od):new Date(Date.UTC(t.Vd.year,t.Vd.month-1,t.Vd.day));return new Date(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds()).toLocaleString(n,s)}(t.time,n,i.locale)}maxTickMarkWeight(t){let i=t.reduce(xn,t[0]).weight;return i>30&&i<50&&(i=30),i}fillWeightsForPoints(t,i){!function(t,i=0){if(0===t.length)return;let n=0===i?null:t[i-1].time.Od,s=null!==n?new Date(1e3*n):null,e=0;for(let r=i;r1){const i=Math.ceil(e/(t.length-1)),n=new Date(1e3*(t[0].time.Od-i));t[0].timeWeight=qn(new Date(1e3*t[0].time.Od),n)}}(t,i)}static Ad(t){return T({localization:{dateFormat:"dd MMM 'yy"}},null!=t?t:{})}}function Jn(t){var i=t.width,n=t.height;if(i<0)throw new Error("Negative width is not allowed for Size");if(n<0)throw new Error("Negative height is not allowed for Size");return{width:i,height:n}}function Qn(t,i){return t.width===i.width&&t.height===i.height}var ts=function(){function t(t){var i=this;this._resolutionListener=function(){return i._onResolutionChanged()},this._resolutionMediaQueryList=null,this._observers=[],this._window=t,this._installResolutionListener()}return t.prototype.dispose=function(){this._uninstallResolutionListener(),this._window=null},Object.defineProperty(t.prototype,"value",{get:function(){return this._window.devicePixelRatio},enumerable:!1,configurable:!0}),t.prototype.subscribe=function(t){var i=this,n={next:t};return this._observers.push(n),{unsubscribe:function(){i._observers=i._observers.filter((function(t){return t!==n}))}}},t.prototype._installResolutionListener=function(){if(null!==this._resolutionMediaQueryList)throw new Error("Resolution listener is already installed");var t=this._window.devicePixelRatio;this._resolutionMediaQueryList=this._window.matchMedia("all and (resolution: ".concat(t,"dppx)")),this._resolutionMediaQueryList.addListener(this._resolutionListener)},t.prototype._uninstallResolutionListener=function(){null!==this._resolutionMediaQueryList&&(this._resolutionMediaQueryList.removeListener(this._resolutionListener),this._resolutionMediaQueryList=null)},t.prototype._reinstallResolutionListener=function(){this._uninstallResolutionListener(),this._installResolutionListener()},t.prototype._onResolutionChanged=function(){var t=this;this._observers.forEach((function(i){return i.next(t._window.devicePixelRatio)})),this._reinstallResolutionListener()},t}();var is=function(){function t(t,i,n){var s;this._canvasElement=null,this._bitmapSizeChangedListeners=[],this._suggestedBitmapSize=null,this._suggestedBitmapSizeChangedListeners=[],this._devicePixelRatioObservable=null,this._canvasElementResizeObserver=null,this._canvasElement=t,this._canvasElementClientSize=Jn({width:this._canvasElement.clientWidth,height:this._canvasElement.clientHeight}),this._transformBitmapSize=null!=i?i:function(t){return t},this._allowResizeObserver=null===(s=null==n?void 0:n.allowResizeObserver)||void 0===s||s,this._chooseAndInitObserver()}return t.prototype.dispose=function(){var t,i;if(null===this._canvasElement)throw new Error("Object is disposed");null===(t=this._canvasElementResizeObserver)||void 0===t||t.disconnect(),this._canvasElementResizeObserver=null,null===(i=this._devicePixelRatioObservable)||void 0===i||i.dispose(),this._devicePixelRatioObservable=null,this._suggestedBitmapSizeChangedListeners.length=0,this._bitmapSizeChangedListeners.length=0,this._canvasElement=null},Object.defineProperty(t.prototype,"canvasElement",{get:function(){if(null===this._canvasElement)throw new Error("Object is disposed");return this._canvasElement},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"canvasElementClientSize",{get:function(){return this._canvasElementClientSize},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"bitmapSize",{get:function(){return Jn({width:this.canvasElement.width,height:this.canvasElement.height})},enumerable:!1,configurable:!0}),t.prototype.resizeCanvasElement=function(t){this._canvasElementClientSize=Jn(t),this.canvasElement.style.width="".concat(this._canvasElementClientSize.width,"px"),this.canvasElement.style.height="".concat(this._canvasElementClientSize.height,"px"),this._invalidateBitmapSize()},t.prototype.subscribeBitmapSizeChanged=function(t){this._bitmapSizeChangedListeners.push(t)},t.prototype.unsubscribeBitmapSizeChanged=function(t){this._bitmapSizeChangedListeners=this._bitmapSizeChangedListeners.filter((function(i){return i!==t}))},Object.defineProperty(t.prototype,"suggestedBitmapSize",{get:function(){return this._suggestedBitmapSize},enumerable:!1,configurable:!0}),t.prototype.subscribeSuggestedBitmapSizeChanged=function(t){this._suggestedBitmapSizeChangedListeners.push(t)},t.prototype.unsubscribeSuggestedBitmapSizeChanged=function(t){this._suggestedBitmapSizeChangedListeners=this._suggestedBitmapSizeChangedListeners.filter((function(i){return i!==t}))},t.prototype.applySuggestedBitmapSize=function(){if(null!==this._suggestedBitmapSize){var t=this._suggestedBitmapSize;this._suggestedBitmapSize=null,this._resizeBitmap(t),this._emitSuggestedBitmapSizeChanged(t,this._suggestedBitmapSize)}},t.prototype._resizeBitmap=function(t){var i=this.bitmapSize;Qn(i,t)||(this.canvasElement.width=t.width,this.canvasElement.height=t.height,this._emitBitmapSizeChanged(i,t))},t.prototype._emitBitmapSizeChanged=function(t,i){var n=this;this._bitmapSizeChangedListeners.forEach((function(s){return s.call(n,t,i)}))},t.prototype._suggestNewBitmapSize=function(t){var i=this._suggestedBitmapSize,n=Jn(this._transformBitmapSize(t,this._canvasElementClientSize)),s=Qn(this.bitmapSize,n)?null:n;null===i&&null===s||null!==i&&null!==s&&Qn(i,s)||(this._suggestedBitmapSize=s,this._emitSuggestedBitmapSizeChanged(i,s))},t.prototype._emitSuggestedBitmapSizeChanged=function(t,i){var n=this;this._suggestedBitmapSizeChangedListeners.forEach((function(s){return s.call(n,t,i)}))},t.prototype._chooseAndInitObserver=function(){var t=this;this._allowResizeObserver?new Promise((function(t){var i=new ResizeObserver((function(n){t(n.every((function(t){return"devicePixelContentBoxSize"in t}))),i.disconnect()}));i.observe(document.body,{box:"device-pixel-content-box"})})).catch((function(){return!1})).then((function(i){return i?t._initResizeObserver():t._initDevicePixelRatioObservable()})):this._initDevicePixelRatioObservable()},t.prototype._initDevicePixelRatioObservable=function(){var t=this;if(null!==this._canvasElement){var i=ns(this._canvasElement);if(null===i)throw new Error("No window is associated with the canvas");this._devicePixelRatioObservable=function(t){return new ts(t)}(i),this._devicePixelRatioObservable.subscribe((function(){return t._invalidateBitmapSize()})),this._invalidateBitmapSize()}},t.prototype._invalidateBitmapSize=function(){var t,i;if(null!==this._canvasElement){var n=ns(this._canvasElement);if(null!==n){var s=null!==(i=null===(t=this._devicePixelRatioObservable)||void 0===t?void 0:t.value)&&void 0!==i?i:n.devicePixelRatio,e=this._canvasElement.getClientRects(),r=void 0!==e[0]?function(t,i){return Jn({width:Math.round(t.left*i+t.width*i)-Math.round(t.left*i),height:Math.round(t.top*i+t.height*i)-Math.round(t.top*i)})}(e[0],s):Jn({width:this._canvasElementClientSize.width*s,height:this._canvasElementClientSize.height*s});this._suggestNewBitmapSize(r)}}},t.prototype._initResizeObserver=function(){var t=this;null!==this._canvasElement&&(this._canvasElementResizeObserver=new ResizeObserver((function(i){var n=i.find((function(i){return i.target===t._canvasElement}));if(n&&n.devicePixelContentBoxSize&&n.devicePixelContentBoxSize[0]){var s=n.devicePixelContentBoxSize[0],e=Jn({width:s.inlineSize,height:s.blockSize});t._suggestNewBitmapSize(e)}})),this._canvasElementResizeObserver.observe(this._canvasElement,{box:"device-pixel-content-box"}))},t}();function ns(t){return t.ownerDocument.defaultView}var ss=function(){function t(t,i,n){if(0===i.width||0===i.height)throw new TypeError("Rendering target could only be created on a media with positive width and height");if(this._mediaSize=i,0===n.width||0===n.height)throw new TypeError("Rendering target could only be created using a bitmap with positive integer width and height");this._bitmapSize=n,this._context=t}return t.prototype.useMediaCoordinateSpace=function(t){try{return this._context.save(),this._context.setTransform(1,0,0,1,0,0),this._context.scale(this._horizontalPixelRatio,this._verticalPixelRatio),t({context:this._context,mediaSize:this._mediaSize})}finally{this._context.restore()}},t.prototype.useBitmapCoordinateSpace=function(t){try{return this._context.save(),this._context.setTransform(1,0,0,1,0,0),t({context:this._context,mediaSize:this._mediaSize,bitmapSize:this._bitmapSize,horizontalPixelRatio:this._horizontalPixelRatio,verticalPixelRatio:this._verticalPixelRatio})}finally{this._context.restore()}},Object.defineProperty(t.prototype,"_horizontalPixelRatio",{get:function(){return this._bitmapSize.width/this._mediaSize.width},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"_verticalPixelRatio",{get:function(){return this._bitmapSize.height/this._mediaSize.height},enumerable:!1,configurable:!0}),t}();function es(t,i){var n=t.canvasElementClientSize;if(0===n.width||0===n.height)return null;var s=t.bitmapSize;if(0===s.width||0===s.height)return null;var e=t.canvasElement.getContext("2d",i);return null===e?null:new ss(e,n,s)}const rs="undefined"!=typeof window;function hs(){return!!rs&&window.navigator.userAgent.toLowerCase().indexOf("firefox")>-1}function ls(){return!!rs&&/iPhone|iPad|iPod/.test(window.navigator.platform)}function as(t){return t+t%2}function os(t,i){return t.zd-i.zd}function _s(t,i,n){const s=(t.zd-i.zd)/(t.ot-i.ot);return Math.sign(s)*Math.min(Math.abs(s),n)}class us{constructor(t,i,n,s){this.Ed=null,this.Id=null,this.Ld=null,this.Nd=null,this.Fd=null,this.Wd=0,this.jd=0,this.Hd=t,this.$d=i,this.Ud=n,this.rs=s}qd(t,i){if(null!==this.Ed){if(this.Ed.ot===i)return void(this.Ed.zd=t);if(Math.abs(this.Ed.zd-t)50)return;let n=0;const s=_s(this.Ed,this.Id,this.$d),e=os(this.Ed,this.Id),r=[s],h=[e];if(n+=e,null!==this.Ld){const t=_s(this.Id,this.Ld,this.$d);if(Math.sign(t)===Math.sign(s)){const i=os(this.Id,this.Ld);if(r.push(t),h.push(i),n+=i,null!==this.Nd){const t=_s(this.Ld,this.Nd,this.$d);if(Math.sign(t)===Math.sign(s)){const i=os(this.Ld,this.Nd);r.push(t),h.push(i),n+=i}}}}let l=0;for(let t=0;t160?"dark":"light"}nf(){return this.Jd.W().layout.attributionLogo}ef(){const t=new URL(location.href);return t.hostname?"&utm_source="+t.hostname+t.pathname:""}Qd(){this.if()&&(this.tf(),this.en=this.nf(),this.en&&(this.Kd=this.sf(),this.Xd=document.createElement("style"),this.Xd.innerText="a#tv-attr-logo{--fill:#131722;--stroke:#fff;position:absolute;left:10px;bottom:10px;height:19px;width:35px;margin:0;padding:0;border:0;z-index:3;}a#tv-attr-logo[data-dark]{--fill:#D1D4DC;--stroke:#131722;}",this.Zd=document.createElement("a"),this.Zd.href=`https://www.tradingview.com/?utm_medium=lwc-link&utm_campaign=lwc-chart${this.ef()}`,this.Zd.title="Charting by TradingView",this.Zd.id="tv-attr-logo",this.Zd.target="_blank",this.Zd.innerHTML='',this.Zd.toggleAttribute("data-dark","dark"===this.Kd),this.Gd.appendChild(this.Xd),this.Gd.appendChild(this.Zd)))}}function ds(t,i){const n=f(t.ownerDocument).createElement("canvas");t.appendChild(n);const s=function(t,i){if("device-pixel-content-box"===i.type)return new is(t,i.transform,i.options);throw new Error("Unsupported binding target")}(n,{type:"device-pixel-content-box",options:{allowResizeObserver:!1},transform:(t,i)=>({width:Math.max(t.width,i.width),height:Math.max(t.height,i.height)})});return s.resizeCanvasElement(i),s}function fs(t){var i;t.width=1,t.height=1,null===(i=t.getContext("2d"))||void 0===i||i.clearRect(0,0,1,1)}function vs(t,i,n,s){t.wl&&t.wl(i,n,s)}function ps(t,i,n,s){t.X(i,n,s)}function ms(t,i,n,s){const e=t(n,s);for(const t of e){const n=t.gt();null!==n&&i(n)}}function bs(t){rs&&void 0!==window.chrome&&t.addEventListener("mousedown",(t=>{if(1===t.button)return t.preventDefault(),!1}))}class ws{constructor(t,i,n){this.rf=0,this.hf=null,this.lf={nt:Number.NEGATIVE_INFINITY,st:Number.POSITIVE_INFINITY},this.af=0,this._f=null,this.uf={nt:Number.NEGATIVE_INFINITY,st:Number.POSITIVE_INFINITY},this.cf=null,this.df=!1,this.ff=null,this.vf=null,this.pf=!1,this.mf=!1,this.bf=!1,this.wf=null,this.gf=null,this.Mf=null,this.xf=null,this.Sf=null,this.kf=null,this.yf=null,this.Cf=0,this.Tf=!1,this.Pf=!1,this.Rf=!1,this.Df=0,this.Of=null,this.Vf=!ls(),this.Bf=t=>{this.Af(t)},this.zf=t=>{if(this.Ef(t)){const i=this.If(t);if(++this.af,this._f&&this.af>1){const{Lf:n}=this.Nf(xs(t),this.uf);n<30&&!this.bf&&this.Ff(i,this.jf.Wf),this.Hf()}}else{const i=this.If(t);if(++this.rf,this.hf&&this.rf>1){const{Lf:n}=this.Nf(xs(t),this.lf);n<5&&!this.mf&&this.$f(i,this.jf.Uf),this.qf()}}},this.Yf=t,this.jf=i,this.cn=n,this.Zf()}S(){null!==this.wf&&(this.wf(),this.wf=null),null!==this.gf&&(this.gf(),this.gf=null),null!==this.xf&&(this.xf(),this.xf=null),null!==this.Sf&&(this.Sf(),this.Sf=null),null!==this.kf&&(this.kf(),this.kf=null),null!==this.Mf&&(this.Mf(),this.Mf=null),this.Xf(),this.qf()}Kf(t){this.xf&&this.xf();const i=this.Gf.bind(this);if(this.xf=()=>{this.Yf.removeEventListener("mousemove",i)},this.Yf.addEventListener("mousemove",i),this.Ef(t))return;const n=this.If(t);this.$f(n,this.jf.Jf),this.Vf=!0}qf(){null!==this.hf&&clearTimeout(this.hf),this.rf=0,this.hf=null,this.lf={nt:Number.NEGATIVE_INFINITY,st:Number.POSITIVE_INFINITY}}Hf(){null!==this._f&&clearTimeout(this._f),this.af=0,this._f=null,this.uf={nt:Number.NEGATIVE_INFINITY,st:Number.POSITIVE_INFINITY}}Gf(t){if(this.Rf||null!==this.vf)return;if(this.Ef(t))return;const i=this.If(t);this.$f(i,this.jf.Qf),this.Vf=!0}tv(t){const i=ks(t.changedTouches,f(this.Of));if(null===i)return;if(this.Df=Ss(t),null!==this.yf)return;if(this.Pf)return;this.Tf=!0;const n=this.Nf(xs(i),f(this.vf)),{iv:s,nv:e,Lf:r}=n;if(this.pf||!(r<5)){if(!this.pf){const t=.5*s,i=e>=t&&!this.cn.sv(),n=t>e&&!this.cn.ev();i||n||(this.Pf=!0),this.pf=!0,this.bf=!0,this.Xf(),this.Hf()}if(!this.Pf){const n=this.If(t,i);this.Ff(n,this.jf.rv),Ms(t)}}}hv(t){if(0!==t.button)return;const i=this.Nf(xs(t),f(this.ff)),{Lf:n}=i;if(n>=5&&(this.mf=!0,this.qf()),this.mf){const i=this.If(t);this.$f(i,this.jf.lv)}}Nf(t,i){const n=Math.abs(i.nt-t.nt),s=Math.abs(i.st-t.st);return{iv:n,nv:s,Lf:n+s}}av(t){let i=ks(t.changedTouches,f(this.Of));if(null===i&&0===t.touches.length&&(i=t.changedTouches[0]),null===i)return;this.Of=null,this.Df=Ss(t),this.Xf(),this.vf=null,this.kf&&(this.kf(),this.kf=null);const n=this.If(t,i);if(this.Ff(n,this.jf.ov),++this.af,this._f&&this.af>1){const{Lf:t}=this.Nf(xs(i),this.uf);t<30&&!this.bf&&this.Ff(n,this.jf.Wf),this.Hf()}else this.bf||(this.Ff(n,this.jf._v),this.jf._v&&Ms(t));0===this.af&&Ms(t),0===t.touches.length&&this.df&&(this.df=!1,Ms(t))}Af(t){if(0!==t.button)return;const i=this.If(t);if(this.ff=null,this.Rf=!1,this.Sf&&(this.Sf(),this.Sf=null),hs()){this.Yf.ownerDocument.documentElement.removeEventListener("mouseleave",this.Bf)}if(!this.Ef(t))if(this.$f(i,this.jf.uv),++this.rf,this.hf&&this.rf>1){const{Lf:n}=this.Nf(xs(t),this.lf);n<5&&!this.mf&&this.$f(i,this.jf.Uf),this.qf()}else this.mf||this.$f(i,this.jf.cv)}Xf(){null!==this.cf&&(clearTimeout(this.cf),this.cf=null)}dv(t){if(null!==this.Of)return;const i=t.changedTouches[0];this.Of=i.identifier,this.Df=Ss(t);const n=this.Yf.ownerDocument.documentElement;this.bf=!1,this.pf=!1,this.Pf=!1,this.vf=xs(i),this.kf&&(this.kf(),this.kf=null);{const i=this.tv.bind(this),s=this.av.bind(this);this.kf=()=>{n.removeEventListener("touchmove",i),n.removeEventListener("touchend",s)},n.addEventListener("touchmove",i,{passive:!1}),n.addEventListener("touchend",s,{passive:!1}),this.Xf(),this.cf=setTimeout(this.fv.bind(this,t),240)}const s=this.If(t,i);this.Ff(s,this.jf.vv),this._f||(this.af=0,this._f=setTimeout(this.Hf.bind(this),500),this.uf=xs(i))}pv(t){if(0!==t.button)return;const i=this.Yf.ownerDocument.documentElement;hs()&&i.addEventListener("mouseleave",this.Bf),this.mf=!1,this.ff=xs(t),this.Sf&&(this.Sf(),this.Sf=null);{const t=this.hv.bind(this),n=this.Af.bind(this);this.Sf=()=>{i.removeEventListener("mousemove",t),i.removeEventListener("mouseup",n)},i.addEventListener("mousemove",t),i.addEventListener("mouseup",n)}if(this.Rf=!0,this.Ef(t))return;const n=this.If(t);this.$f(n,this.jf.mv),this.hf||(this.rf=0,this.hf=setTimeout(this.qf.bind(this),500),this.lf=xs(t))}Zf(){this.Yf.addEventListener("mouseenter",this.Kf.bind(this)),this.Yf.addEventListener("touchcancel",this.Xf.bind(this));{const t=this.Yf.ownerDocument,i=t=>{this.jf.bv&&(t.composed&&this.Yf.contains(t.composedPath()[0])||t.target&&this.Yf.contains(t.target)||this.jf.bv())};this.gf=()=>{t.removeEventListener("touchstart",i)},this.wf=()=>{t.removeEventListener("mousedown",i)},t.addEventListener("mousedown",i),t.addEventListener("touchstart",i,{passive:!0})}ls()&&(this.Mf=()=>{this.Yf.removeEventListener("dblclick",this.zf)},this.Yf.addEventListener("dblclick",this.zf)),this.Yf.addEventListener("mouseleave",this.wv.bind(this)),this.Yf.addEventListener("touchstart",this.dv.bind(this),{passive:!0}),bs(this.Yf),this.Yf.addEventListener("mousedown",this.pv.bind(this)),this.gv(),this.Yf.addEventListener("touchmove",(()=>{}),{passive:!1})}gv(){void 0===this.jf.Mv&&void 0===this.jf.xv&&void 0===this.jf.Sv||(this.Yf.addEventListener("touchstart",(t=>this.kv(t.touches)),{passive:!0}),this.Yf.addEventListener("touchmove",(t=>{if(2===t.touches.length&&null!==this.yf&&void 0!==this.jf.xv){const i=gs(t.touches[0],t.touches[1])/this.Cf;this.jf.xv(this.yf,i),Ms(t)}}),{passive:!1}),this.Yf.addEventListener("touchend",(t=>{this.kv(t.touches)})))}kv(t){1===t.length&&(this.Tf=!1),2!==t.length||this.Tf||this.df?this.yv():this.Cv(t)}Cv(t){const i=this.Yf.getBoundingClientRect()||{left:0,top:0};this.yf={nt:(t[0].clientX-i.left+(t[1].clientX-i.left))/2,st:(t[0].clientY-i.top+(t[1].clientY-i.top))/2},this.Cf=gs(t[0],t[1]),void 0!==this.jf.Mv&&this.jf.Mv(),this.Xf()}yv(){null!==this.yf&&(this.yf=null,void 0!==this.jf.Sv&&this.jf.Sv())}wv(t){if(this.xf&&this.xf(),this.Ef(t))return;if(!this.Vf)return;const i=this.If(t);this.$f(i,this.jf.Tv),this.Vf=!ls()}fv(t){const i=ks(t.touches,f(this.Of));if(null===i)return;const n=this.If(t,i);this.Ff(n,this.jf.Pv),this.bf=!0,this.df=!0}Ef(t){return t.sourceCapabilities&&void 0!==t.sourceCapabilities.firesTouchEvents?t.sourceCapabilities.firesTouchEvents:Ss(t){"touchstart"!==t.type&&Ms(t)}}}}function gs(t,i){const n=t.clientX-i.clientX,s=t.clientY-i.clientY;return Math.sqrt(n*n+s*s)}function Ms(t){t.cancelable&&t.preventDefault()}function xs(t){return{nt:t.pageX,st:t.pageY}}function Ss(t){return t.timeStamp||performance.now()}function ks(t,i){for(let n=0;n{var s,e,r,h;return(null!==(e=null===(s=n.Dt())||void 0===s?void 0:s.Ta())&&void 0!==e?e:"")!==i?[]:null!==(h=null===(r=n.ca)||void 0===r?void 0:r.call(n,t))&&void 0!==h?h:[]}}function Ps(t,i,n,s){if(!t.length)return;let e=0;const r=n/2,h=t[0].At(s,!0);let l=1===i?r-(t[0].Oi()-h/2):t[0].Oi()-h/2-r;l=Math.max(0,l);for(let r=1;ru-o:_n)&&l>0){const s=1===i?-1-r:r-n,h=Math.min(s,l);for(let n=e;n{this.Wv||this.tn.Hv().$t().$h()},this.$v=()=>{this.Wv||this.tn.Hv().$t().$h()},this.tn=t,this.cn=i,this.So=i.layout,this.Oc=n,this.Uv="left"===s,this.qv=Ts("normal",s),this.Yv=Ts("top",s),this.Zv=Ts("bottom",s),this.Xv=document.createElement("div"),this.Xv.style.height="100%",this.Xv.style.overflow="hidden",this.Xv.style.width="25px",this.Xv.style.left="0",this.Xv.style.position="relative",this.Kv=ds(this.Xv,Jn({width:16,height:16})),this.Kv.subscribeSuggestedBitmapSizeChanged(this.jv);const e=this.Kv.canvasElement;e.style.position="absolute",e.style.zIndex="1",e.style.left="0",e.style.top="0",this.Gv=ds(this.Xv,Jn({width:16,height:16})),this.Gv.subscribeSuggestedBitmapSizeChanged(this.$v);const r=this.Gv.canvasElement;r.style.position="absolute",r.style.zIndex="2",r.style.left="0",r.style.top="0";const h={mv:this.Jv.bind(this),vv:this.Jv.bind(this),lv:this.Qv.bind(this),rv:this.Qv.bind(this),bv:this.tp.bind(this),uv:this.ip.bind(this),ov:this.ip.bind(this),Uf:this.np.bind(this),Wf:this.np.bind(this),Jf:this.sp.bind(this),Tv:this.ep.bind(this)};this.rp=new ws(this.Gv.canvasElement,h,{sv:()=>!this.cn.handleScroll.vertTouchDrag,ev:()=>!0})}S(){this.rp.S(),this.Gv.unsubscribeSuggestedBitmapSizeChanged(this.$v),fs(this.Gv.canvasElement),this.Gv.dispose(),this.Kv.unsubscribeSuggestedBitmapSizeChanged(this.jv),fs(this.Kv.canvasElement),this.Kv.dispose(),null!==this.Ii&&this.Ii.Xo().p(this),this.Ii=null}hp(){return this.Xv}P(){return this.So.fontSize}lp(){const t=this.Oc.W();return this.Jr!==t.R&&(this.Nv.ir(),this.Jr=t.R),t}ap(){if(null===this.Ii)return 0;let t=0;const i=this.lp(),n=f(this.Kv.canvasElement.getContext("2d"));n.save();const s=this.Ii.ja();n.font=this.op(),s.length>0&&(t=Math.max(this.Nv.xi(n,s[0].no),this.Nv.xi(n,s[s.length-1].no)));const e=this._p();for(let i=e.length;i--;){const s=this.Nv.xi(n,e[i].Kt());s>t&&(t=s)}const r=this.Ii.Ct();if(null!==r&&null!==this.Iv&&2!==this.cn.crosshair.mode){const i=this.Ii.pn(1,r),s=this.Ii.pn(this.Iv.height-2,r);t=Math.max(t,this.Nv.xi(n,this.Ii.Fi(Math.floor(Math.min(i,s))+.11111111111111,r)),this.Nv.xi(n,this.Ii.Fi(Math.ceil(Math.max(i,s))-.11111111111111,r)))}n.restore();const h=t||34;return as(Math.ceil(i.C+i.T+i.A+i.I+5+h))}up(t){null!==this.Iv&&Qn(this.Iv,t)||(this.Iv=t,this.Wv=!0,this.Kv.resizeCanvasElement(t),this.Gv.resizeCanvasElement(t),this.Wv=!1,this.Xv.style.width=`${t.width}px`,this.Xv.style.height=`${t.height}px`)}cp(){return f(this.Iv).width}Gi(t){this.Ii!==t&&(null!==this.Ii&&this.Ii.Xo().p(this),this.Ii=t,t.Xo().l(this.do.bind(this),this))}Dt(){return this.Ii}ir(){const t=this.tn.dp();this.tn.Hv().$t().I_(t,f(this.Dt()))}fp(t){if(null===this.Iv)return;if(1!==t){this.vp(),this.Kv.applySuggestedBitmapSize();const t=es(this.Kv);null!==t&&(t.useBitmapCoordinateSpace((t=>{this.pp(t),this.Ae(t)})),this.tn.mp(t,this.Zv),this.bp(t),this.tn.mp(t,this.qv),this.wp(t))}this.Gv.applySuggestedBitmapSize();const i=es(this.Gv);null!==i&&(i.useBitmapCoordinateSpace((({context:t,bitmapSize:i})=>{t.clearRect(0,0,i.width,i.height)})),this.gp(i),this.tn.mp(i,this.Yv))}Mp(){return this.Kv.bitmapSize}xp(t,i,n){const s=this.Mp();s.width>0&&s.height>0&&t.drawImage(this.Kv.canvasElement,i,n)}bt(){var t;null===(t=this.Ii)||void 0===t||t.ja()}Jv(t){if(null===this.Ii||this.Ii.Ni()||!this.cn.handleScale.axisPressedMouseMove.price)return;const i=this.tn.Hv().$t(),n=this.tn.dp();this.Lv=!0,i.D_(n,this.Ii,t.localY)}Qv(t){if(null===this.Ii||!this.cn.handleScale.axisPressedMouseMove.price)return;const i=this.tn.Hv().$t(),n=this.tn.dp(),s=this.Ii;i.O_(n,s,t.localY)}tp(){if(null===this.Ii||!this.cn.handleScale.axisPressedMouseMove.price)return;const t=this.tn.Hv().$t(),i=this.tn.dp(),n=this.Ii;this.Lv&&(this.Lv=!1,t.V_(i,n))}ip(t){if(null===this.Ii||!this.cn.handleScale.axisPressedMouseMove.price)return;const i=this.tn.Hv().$t(),n=this.tn.dp();this.Lv=!1,i.V_(n,this.Ii)}np(t){this.cn.handleScale.axisDoubleClickReset.price&&this.ir()}sp(t){if(null===this.Ii)return;!this.tn.Hv().$t().W().handleScale.axisPressedMouseMove.price||this.Ii.gh()||this.Ii.Oo()||this.Sp(1)}ep(t){this.Sp(0)}_p(){const t=[],i=null===this.Ii?void 0:this.Ii;return(n=>{for(let s=0;s{t.fillStyle=n.borderColor;const l=Math.max(1,Math.floor(h)),a=Math.floor(.5*h),o=Math.round(s.T*r);t.beginPath();for(const n of i)t.rect(Math.floor(e*r),Math.round(n.Ia*h)-a,o,l);t.fill()})),t.useMediaCoordinateSpace((({context:t})=>{var r;t.font=this.op(),t.fillStyle=null!==(r=n.textColor)&&void 0!==r?r:this.So.textColor,t.textAlign=this.Uv?"right":"left",t.textBaseline="middle";const h=this.Uv?Math.round(e-s.A):Math.round(e+s.T+s.A),l=i.map((i=>this.Nv.Mi(t,i.no)));for(let n=i.length;n--;){const s=i[n];t.fillText(s.no,h,s.Ia+l[n])}}))}vp(){if(null===this.Iv||null===this.Ii)return;const t=[],i=this.Ii.$o().slice(),n=this.tn.dp(),s=this.lp();this.Ii===n.vr()&&this.tn.dp().$o().forEach((t=>{n.dr(t)&&i.push(t)}));const e=this.Ii;i.forEach((i=>{i.Rn(n,e).forEach((i=>{i.Vi(null),i.Bi()&&t.push(i)}))})),t.forEach((t=>t.Vi(t.ki())));this.Ii.W().alignLabels&&this.kp(t,s)}kp(t,i){if(null===this.Iv)return;const n=this.Iv.height/2,s=t.filter((t=>t.ki()<=n)),e=t.filter((t=>t.ki()>n));s.sort(((t,i)=>i.ki()-t.ki())),e.sort(((t,i)=>t.ki()-i.ki()));for(const n of t){const t=Math.floor(n.At(i)/2),s=n.ki();s>-t&&sthis.Iv.height-t&&s{if(i.Ai()){i.gt(f(this.Ii)).X(t,n,this.Nv,s)}}))}gp(t){if(null===this.Iv||null===this.Ii)return;const i=this.tn.Hv().$t(),n=[],s=this.tn.dp(),e=i.Yc().Rn(s,this.Ii);e.length&&n.push(e);const r=this.lp(),h=this.Uv?"right":"left";n.forEach((i=>{i.forEach((i=>{i.gt(f(this.Ii)).X(t,r,this.Nv,h)}))}))}Sp(t){this.Xv.style.cursor=1===t?"ns-resize":"default"}do(){const t=this.ap();this.Fv{this.Wv||null===this.Lp||this.$i().$h()},this.$v=()=>{this.Wv||null===this.Lp||this.$i().$h()},this.Jd=t,this.Lp=i,this.Lp.F_().l(this.Np.bind(this),this,!0),this.Fp=document.createElement("td"),this.Fp.style.padding="0",this.Fp.style.position="relative";const n=document.createElement("div");n.style.width="100%",n.style.height="100%",n.style.position="relative",n.style.overflow="hidden",this.Wp=document.createElement("td"),this.Wp.style.padding="0",this.jp=document.createElement("td"),this.jp.style.padding="0",this.Fp.appendChild(n),this.Kv=ds(n,Jn({width:16,height:16})),this.Kv.subscribeSuggestedBitmapSizeChanged(this.jv);const s=this.Kv.canvasElement;s.style.position="absolute",s.style.zIndex="1",s.style.left="0",s.style.top="0",this.Gv=ds(n,Jn({width:16,height:16})),this.Gv.subscribeSuggestedBitmapSizeChanged(this.$v);const e=this.Gv.canvasElement;e.style.position="absolute",e.style.zIndex="2",e.style.left="0",e.style.top="0",this.Hp=document.createElement("tr"),this.Hp.appendChild(this.Wp),this.Hp.appendChild(this.Fp),this.Hp.appendChild(this.jp),this.$p(),this.rp=new ws(this.Gv.canvasElement,this,{sv:()=>null===this.Ap&&!this.Jd.W().handleScroll.vertTouchDrag,ev:()=>null===this.Ap&&!this.Jd.W().handleScroll.horzTouchDrag})}S(){null!==this.yp&&this.yp.S(),null!==this.Cp&&this.Cp.S(),this.Tp=null,this.Gv.unsubscribeSuggestedBitmapSizeChanged(this.$v),fs(this.Gv.canvasElement),this.Gv.dispose(),this.Kv.unsubscribeSuggestedBitmapSizeChanged(this.jv),fs(this.Kv.canvasElement),this.Kv.dispose(),null!==this.Lp&&this.Lp.F_().p(this),this.rp.S()}dp(){return f(this.Lp)}Up(t){var i,n;null!==this.Lp&&this.Lp.F_().p(this),this.Lp=t,null!==this.Lp&&this.Lp.F_().l(As.prototype.Np.bind(this),this,!0),this.$p(),this.Jd.qp().indexOf(this)===this.Jd.qp().length-1?(this.Tp=null!==(i=this.Tp)&&void 0!==i?i:new cs(this.Fp,this.Jd),this.Tp.bt()):(null===(n=this.Tp)||void 0===n||n.tf(),this.Tp=null)}Hv(){return this.Jd}hp(){return this.Hp}$p(){if(null!==this.Lp&&(this.Yp(),0!==this.$i().wt().length)){if(null!==this.yp){const t=this.Lp.P_();this.yp.Gi(f(t))}if(null!==this.Cp){const t=this.Lp.R_();this.Cp.Gi(f(t))}}}Zp(){null!==this.yp&&this.yp.bt(),null!==this.Cp&&this.Cp.bt()}g_(){return null!==this.Lp?this.Lp.g_():0}M_(t){this.Lp&&this.Lp.M_(t)}Jf(t){if(!this.Lp)return;this.Xp();const i=t.localX,n=t.localY;this.Kp(i,n,t)}mv(t){this.Xp(),this.Gp(),this.Kp(t.localX,t.localY,t)}Qf(t){var i;if(!this.Lp)return;this.Xp();const n=t.localX,s=t.localY;this.Kp(n,s,t);const e=this.br(n,s);this.Jd.Jp(null!==(i=null==e?void 0:e.Ev)&&void 0!==i?i:null),this.$i().Wc(e&&{jc:e.jc,Av:e.Av})}cv(t){null!==this.Lp&&(this.Xp(),this.Qp(t))}Uf(t){null!==this.Lp&&this.tm(this.Op,t)}Wf(t){this.Uf(t)}lv(t){this.Xp(),this.im(t),this.Kp(t.localX,t.localY,t)}uv(t){null!==this.Lp&&(this.Xp(),this.Bp=!1,this.nm(t))}_v(t){null!==this.Lp&&this.Qp(t)}Pv(t){if(this.Bp=!0,null===this.Ap){const i={x:t.localX,y:t.localY};this.sm(i,i,t)}}Tv(t){null!==this.Lp&&(this.Xp(),this.Lp.$t().Wc(null),this.rm())}hm(){return this.Dp}lm(){return this.Op}Mv(){this.Vp=1,this.$i().Un()}xv(t,i){if(!this.Jd.W().handleScale.pinch)return;const n=5*(i-this.Vp);this.Vp=i,this.$i().Jc(t.nt,n)}vv(t){this.Bp=!1,this.zp=null!==this.Ap,this.Gp();const i=this.$i().Yc();null!==this.Ap&&i.yt()&&(this.Ep={x:i.Yt(),y:i.Zt()},this.Ap={x:t.localX,y:t.localY})}rv(t){if(null===this.Lp)return;const i=t.localX,n=t.localY;if(null===this.Ap)this.im(t);else{this.zp=!1;const s=f(this.Ep),e=s.x+(i-this.Ap.x),r=s.y+(n-this.Ap.y);this.Kp(e,r,t)}}ov(t){0===this.Hv().W().trackingMode.exitMode&&(this.zp=!0),this.am(),this.nm(t)}br(t,i){const n=this.Lp;return null===n?null:function(t,i,n){const s=t.$o(),e=function(t,i,n){var s,e;let r,h;for(const o of t){const t=null!==(e=null===(s=o.fa)||void 0===s?void 0:s.call(o,i,n))&&void 0!==e?e:[];for(const i of t)l=i.zOrder,(!(a=null==r?void 0:r.zOrder)||"top"===l&&"top"!==a||"normal"===l&&"bottom"===a)&&(r=i,h=o)}var l,a;return r&&h?{zv:r,jc:h}:null}(s,i,n);if("top"===(null==e?void 0:e.zv.zOrder))return ys(e);for(const r of s){if(e&&e.jc===r&&"bottom"!==e.zv.zOrder&&!e.zv.isBackground)return ys(e);const s=Cs(r.Pn(t),i,n);if(null!==s)return{jc:r,Vv:s.Vv,Av:s.Av};if(e&&e.jc===r&&"bottom"!==e.zv.zOrder&&e.zv.isBackground)return ys(e)}return(null==e?void 0:e.zv)?ys(e):null}(n,t,i)}om(t,i){f("left"===i?this.yp:this.Cp).up(Jn({width:t,height:this.Iv.height}))}_m(){return this.Iv}up(t){Qn(this.Iv,t)||(this.Iv=t,this.Wv=!0,this.Kv.resizeCanvasElement(t),this.Gv.resizeCanvasElement(t),this.Wv=!1,this.Fp.style.width=t.width+"px",this.Fp.style.height=t.height+"px")}um(){const t=f(this.Lp);t.T_(t.P_()),t.T_(t.R_());for(const i of t.Va())if(t.dr(i)){const n=i.Dt();null!==n&&t.T_(n),i.On()}}Mp(){return this.Kv.bitmapSize}xp(t,i,n){const s=this.Mp();s.width>0&&s.height>0&&t.drawImage(this.Kv.canvasElement,i,n)}fp(t){if(0===t)return;if(null===this.Lp)return;if(t>1&&this.um(),null!==this.yp&&this.yp.fp(t),null!==this.Cp&&this.Cp.fp(t),1!==t){this.Kv.applySuggestedBitmapSize();const t=es(this.Kv);null!==t&&(t.useBitmapCoordinateSpace((t=>{this.pp(t)})),this.Lp&&(this.dm(t,Ds),this.fm(t),this.vm(t),this.dm(t,Os),this.dm(t,Vs)))}this.Gv.applySuggestedBitmapSize();const i=es(this.Gv);null!==i&&(i.useBitmapCoordinateSpace((({context:t,bitmapSize:i})=>{t.clearRect(0,0,i.width,i.height)})),this.pm(i),this.dm(i,Bs))}bm(){return this.yp}wm(){return this.Cp}mp(t,i){this.dm(t,i)}Np(){null!==this.Lp&&this.Lp.F_().p(this),this.Lp=null}Qp(t){this.tm(this.Dp,t)}tm(t,i){const n=i.localX,s=i.localY;t.M()&&t.m(this.$i().St().Lu(n),{x:n,y:s},i)}pp({context:t,bitmapSize:i}){const{width:n,height:s}=i,e=this.$i(),r=e.q(),h=e.md();r===h?Y(t,0,0,n,s,h):K(t,0,0,n,s,r,h)}fm(t){const i=f(this.Lp).W_().Uh().gt();null!==i&&i.X(t,!1)}vm(t){const i=this.$i().qc();this.gm(t,Os,vs,i),this.gm(t,Os,ps,i)}pm(t){this.gm(t,Os,ps,this.$i().Yc())}dm(t,i){const n=f(this.Lp).$o();for(const s of n)this.gm(t,i,vs,s);for(const s of n)this.gm(t,i,ps,s)}gm(t,i,n,s){const e=f(this.Lp),r=e.$t().Fc(),h=null!==r&&r.jc===s,l=null!==r&&h&&void 0!==r.Av?r.Av.gr:void 0;ms(i,(i=>n(i,t,h,l)),s,e)}Yp(){if(null===this.Lp)return;const t=this.Jd,i=this.Lp.P_().W().visible,n=this.Lp.R_().W().visible;i||null===this.yp||(this.Wp.removeChild(this.yp.hp()),this.yp.S(),this.yp=null),n||null===this.Cp||(this.jp.removeChild(this.Cp.hp()),this.Cp.S(),this.Cp=null);const s=t.$t()._d();i&&null===this.yp&&(this.yp=new Rs(this,t.W(),s,"left"),this.Wp.appendChild(this.yp.hp())),n&&null===this.Cp&&(this.Cp=new Rs(this,t.W(),s,"right"),this.jp.appendChild(this.Cp.hp()))}Mm(t){return t.Rv&&this.Bp||null!==this.Ap}xm(t){return Math.max(0,Math.min(t,this.Iv.width-1))}Sm(t){return Math.max(0,Math.min(t,this.Iv.height-1))}Kp(t,i,n){this.$i().hd(this.xm(t),this.Sm(i),n,f(this.Lp))}rm(){this.$i().ad()}am(){this.zp&&(this.Ap=null,this.rm())}sm(t,i,n){this.Ap=t,this.zp=!1,this.Kp(i.x,i.y,n);const s=this.$i().Yc();this.Ep={x:s.Yt(),y:s.Zt()}}$i(){return this.Jd.$t()}nm(t){if(!this.Rp)return;const i=this.$i(),n=this.dp();if(i.z_(n,n.vn()),this.Pp=null,this.Rp=!1,i.sd(),null!==this.Ip){const t=performance.now(),n=i.St();this.Ip.Dr(n.ju(),t),this.Ip.Ju(t)||i.Zn(this.Ip)}}Xp(){this.Ap=null}Gp(){if(!this.Lp)return;if(this.$i().Un(),document.activeElement!==document.body&&document.activeElement!==document.documentElement)f(document.activeElement).blur();else{const t=document.getSelection();null!==t&&t.removeAllRanges()}!this.Lp.vn().Ni()&&this.$i().St().Ni()}im(t){if(null===this.Lp)return;const i=this.$i(),n=i.St();if(n.Ni())return;const s=this.Jd.W(),e=s.handleScroll,r=s.kineticScroll;if((!e.pressedMouseMove||t.Rv)&&(!e.horzTouchDrag&&!e.vertTouchDrag||!t.Rv))return;const h=this.Lp.vn(),l=performance.now();if(null!==this.Pp||this.Mm(t)||(this.Pp={x:t.clientX,y:t.clientY,Od:l,km:t.localX,ym:t.localY}),null!==this.Pp&&!this.Rp&&(this.Pp.x!==t.clientX||this.Pp.y!==t.clientY)){if(t.Rv&&r.touch||!t.Rv&&r.mouse){const t=n.he();this.Ip=new us(.2/t,7/t,.997,15/t),this.Ip.qd(n.ju(),this.Pp.Od)}else this.Ip=null;h.Ni()||i.B_(this.Lp,h,t.localY),i.td(t.localX),this.Rp=!0}this.Rp&&(h.Ni()||i.A_(this.Lp,h,t.localY),i.nd(t.localX),null!==this.Ip&&this.Ip.qd(n.ju(),l))}}class zs{constructor(t,i,n,s,e){this.ft=!0,this.Iv=Jn({width:0,height:0}),this.jv=()=>this.fp(3),this.Uv="left"===t,this.Oc=n._d,this.cn=i,this.Cm=s,this.Tm=e,this.Xv=document.createElement("div"),this.Xv.style.width="25px",this.Xv.style.height="100%",this.Xv.style.overflow="hidden",this.Kv=ds(this.Xv,Jn({width:16,height:16})),this.Kv.subscribeSuggestedBitmapSizeChanged(this.jv)}S(){this.Kv.unsubscribeSuggestedBitmapSizeChanged(this.jv),fs(this.Kv.canvasElement),this.Kv.dispose()}hp(){return this.Xv}_m(){return this.Iv}up(t){Qn(this.Iv,t)||(this.Iv=t,this.Kv.resizeCanvasElement(t),this.Xv.style.width=`${t.width}px`,this.Xv.style.height=`${t.height}px`,this.ft=!0)}fp(t){if(t<3&&!this.ft)return;if(0===this.Iv.width||0===this.Iv.height)return;this.ft=!1,this.Kv.applySuggestedBitmapSize();const i=es(this.Kv);null!==i&&i.useBitmapCoordinateSpace((t=>{this.pp(t),this.Ae(t)}))}Mp(){return this.Kv.bitmapSize}xp(t,i,n){const s=this.Mp();s.width>0&&s.height>0&&t.drawImage(this.Kv.canvasElement,i,n)}Ae({context:t,bitmapSize:i,horizontalPixelRatio:n,verticalPixelRatio:s}){if(!this.Cm())return;t.fillStyle=this.cn.timeScale.borderColor;const e=Math.floor(this.Oc.W().C*n),r=Math.floor(this.Oc.W().C*s),h=this.Uv?i.width-e:0;t.fillRect(h,0,e,r)}pp({context:t,bitmapSize:i}){Y(t,0,0,i.width,i.height,this.Tm())}}function Es(t){return i=>{var n,s;return null!==(s=null===(n=i.da)||void 0===n?void 0:n.call(i,t))&&void 0!==s?s:[]}}const Is=Es("normal"),Ls=Es("top"),Ns=Es("bottom");class Fs{constructor(t,i){this.Pm=null,this.Rm=null,this.k=null,this.Dm=!1,this.Iv=Jn({width:0,height:0}),this.Om=new C,this.Nv=new Jt(5),this.Wv=!1,this.jv=()=>{this.Wv||this.Jd.$t().$h()},this.$v=()=>{this.Wv||this.Jd.$t().$h()},this.Jd=t,this.U_=i,this.cn=t.W().layout,this.Zd=document.createElement("tr"),this.Vm=document.createElement("td"),this.Vm.style.padding="0",this.Bm=document.createElement("td"),this.Bm.style.padding="0",this.Xv=document.createElement("td"),this.Xv.style.height="25px",this.Xv.style.padding="0",this.Am=document.createElement("div"),this.Am.style.width="100%",this.Am.style.height="100%",this.Am.style.position="relative",this.Am.style.overflow="hidden",this.Xv.appendChild(this.Am),this.Kv=ds(this.Am,Jn({width:16,height:16})),this.Kv.subscribeSuggestedBitmapSizeChanged(this.jv);const n=this.Kv.canvasElement;n.style.position="absolute",n.style.zIndex="1",n.style.left="0",n.style.top="0",this.Gv=ds(this.Am,Jn({width:16,height:16})),this.Gv.subscribeSuggestedBitmapSizeChanged(this.$v);const s=this.Gv.canvasElement;s.style.position="absolute",s.style.zIndex="2",s.style.left="0",s.style.top="0",this.Zd.appendChild(this.Vm),this.Zd.appendChild(this.Xv),this.Zd.appendChild(this.Bm),this.zm(),this.Jd.$t().w_().l(this.zm.bind(this),this),this.rp=new ws(this.Gv.canvasElement,this,{sv:()=>!0,ev:()=>!this.Jd.W().handleScroll.horzTouchDrag})}S(){this.rp.S(),null!==this.Pm&&this.Pm.S(),null!==this.Rm&&this.Rm.S(),this.Gv.unsubscribeSuggestedBitmapSizeChanged(this.$v),fs(this.Gv.canvasElement),this.Gv.dispose(),this.Kv.unsubscribeSuggestedBitmapSizeChanged(this.jv),fs(this.Kv.canvasElement),this.Kv.dispose()}hp(){return this.Zd}Em(){return this.Pm}Im(){return this.Rm}mv(t){if(this.Dm)return;this.Dm=!0;const i=this.Jd.$t();!i.St().Ni()&&this.Jd.W().handleScale.axisPressedMouseMove.time&&i.Gc(t.localX)}vv(t){this.mv(t)}bv(){const t=this.Jd.$t();!t.St().Ni()&&this.Dm&&(this.Dm=!1,this.Jd.W().handleScale.axisPressedMouseMove.time&&t.rd())}lv(t){const i=this.Jd.$t();!i.St().Ni()&&this.Jd.W().handleScale.axisPressedMouseMove.time&&i.ed(t.localX)}rv(t){this.lv(t)}uv(){this.Dm=!1;const t=this.Jd.$t();t.St().Ni()&&!this.Jd.W().handleScale.axisPressedMouseMove.time||t.rd()}ov(){this.uv()}Uf(){this.Jd.W().handleScale.axisDoubleClickReset.time&&this.Jd.$t().Kn()}Wf(){this.Uf()}Jf(){this.Jd.$t().W().handleScale.axisPressedMouseMove.time&&this.Sp(1)}Tv(){this.Sp(0)}_m(){return this.Iv}Lm(){return this.Om}Nm(t,i,n){Qn(this.Iv,t)||(this.Iv=t,this.Wv=!0,this.Kv.resizeCanvasElement(t),this.Gv.resizeCanvasElement(t),this.Wv=!1,this.Xv.style.width=`${t.width}px`,this.Xv.style.height=`${t.height}px`,this.Om.m(t)),null!==this.Pm&&this.Pm.up(Jn({width:i,height:t.height})),null!==this.Rm&&this.Rm.up(Jn({width:n,height:t.height}))}Fm(){const t=this.Wm();return Math.ceil(t.C+t.T+t.P+t.L+t.B+t.jm)}bt(){this.Jd.$t().St().ja()}Mp(){return this.Kv.bitmapSize}xp(t,i,n){const s=this.Mp();s.width>0&&s.height>0&&t.drawImage(this.Kv.canvasElement,i,n)}fp(t){if(0===t)return;if(1!==t){this.Kv.applySuggestedBitmapSize();const i=es(this.Kv);null!==i&&(i.useBitmapCoordinateSpace((t=>{this.pp(t),this.Ae(t),this.Hm(i,Ns)})),this.bp(i),this.Hm(i,Is)),null!==this.Pm&&this.Pm.fp(t),null!==this.Rm&&this.Rm.fp(t)}this.Gv.applySuggestedBitmapSize();const i=es(this.Gv);null!==i&&(i.useBitmapCoordinateSpace((({context:t,bitmapSize:i})=>{t.clearRect(0,0,i.width,i.height)})),this.$m([...this.Jd.$t().wt(),this.Jd.$t().Yc()],i),this.Hm(i,Ls))}Hm(t,i){const n=this.Jd.$t().wt();for(const s of n)ms(i,(i=>vs(i,t,!1,void 0)),s,void 0);for(const s of n)ms(i,(i=>ps(i,t,!1,void 0)),s,void 0)}pp({context:t,bitmapSize:i}){Y(t,0,0,i.width,i.height,this.Jd.$t().md())}Ae({context:t,bitmapSize:i,verticalPixelRatio:n}){if(this.Jd.W().timeScale.borderVisible){t.fillStyle=this.Um();const s=Math.max(1,Math.floor(this.Wm().C*n));t.fillRect(0,0,i.width,s)}}bp(t){const i=this.Jd.$t().St(),n=i.ja();if(!n||0===n.length)return;const s=this.U_.maxTickMarkWeight(n),e=this.Wm(),r=i.W();r.borderVisible&&r.ticksVisible&&t.useBitmapCoordinateSpace((({context:t,horizontalPixelRatio:i,verticalPixelRatio:s})=>{t.strokeStyle=this.Um(),t.fillStyle=this.Um();const r=Math.max(1,Math.floor(i)),h=Math.floor(.5*i);t.beginPath();const l=Math.round(e.T*s);for(let s=n.length;s--;){const e=Math.round(n[s].coord*i);t.rect(e-h,0,r,l)}t.fill()})),t.useMediaCoordinateSpace((({context:t})=>{const i=e.C+e.T+e.L+e.P/2;t.textAlign="center",t.textBaseline="middle",t.fillStyle=this.$(),t.font=this.op();for(const e of n)if(e.weight=s){const n=e.needAlignCoordinate?this.qm(t,e.coord,e.label):e.coord;t.fillText(e.label,n,i)}}))}qm(t,i,n){const s=this.Nv.xi(t,n),e=s/2,r=Math.floor(i-e)+.5;return r<0?i+=Math.abs(0-r):r+s>this.Iv.width&&(i-=Math.abs(this.Iv.width-(r+s))),i}$m(t,i){const n=this.Wm();for(const s of t)for(const t of s.Qi())t.gt().X(i,n)}Um(){return this.Jd.W().timeScale.borderColor}$(){return this.cn.textColor}j(){return this.cn.fontSize}op(){return E(this.j(),this.cn.fontFamily)}Ym(){return E(this.j(),this.cn.fontFamily,"bold")}Wm(){null===this.k&&(this.k={C:1,N:NaN,L:NaN,B:NaN,ji:NaN,T:5,P:NaN,R:"",Wi:new Jt,jm:0});const t=this.k,i=this.op();if(t.R!==i){const n=this.j();t.P=n,t.R=i,t.L=3*n/12,t.B=3*n/12,t.ji=9*n/12,t.N=0,t.jm=4*n/12,t.Wi.ir()}return this.k}Sp(t){this.Xv.style.cursor=1===t?"ew-resize":"default"}zm(){const t=this.Jd.$t(),i=t.W();i.leftPriceScale.visible||null===this.Pm||(this.Vm.removeChild(this.Pm.hp()),this.Pm.S(),this.Pm=null),i.rightPriceScale.visible||null===this.Rm||(this.Bm.removeChild(this.Rm.hp()),this.Rm.S(),this.Rm=null);const n={_d:this.Jd.$t()._d()},s=()=>i.leftPriceScale.borderVisible&&t.St().W().borderVisible,e=()=>t.md();i.leftPriceScale.visible&&null===this.Pm&&(this.Pm=new zs("left",i,n,s,e),this.Vm.appendChild(this.Pm.hp())),i.rightPriceScale.visible&&null===this.Rm&&(this.Rm=new zs("right",i,n,s,e),this.Bm.appendChild(this.Rm.hp()))}}const Ws=!!rs&&!!navigator.userAgentData&&navigator.userAgentData.brands.some((t=>t.brand.includes("Chromium")))&&!!rs&&((null===(js=null===navigator||void 0===navigator?void 0:navigator.userAgentData)||void 0===js?void 0:js.platform)?"Windows"===navigator.userAgentData.platform:navigator.userAgent.toLowerCase().indexOf("win")>=0);var js;class Hs{constructor(t,i,n){var s;this.Zm=[],this.Xm=0,this.ro=0,this.o_=0,this.Km=0,this.Gm=0,this.Jm=null,this.Qm=!1,this.Dp=new C,this.Op=new C,this.Pc=new C,this.tb=null,this.ib=null,this.Gd=t,this.cn=i,this.U_=n,this.Zd=document.createElement("div"),this.Zd.classList.add("tv-lightweight-charts"),this.Zd.style.overflow="hidden",this.Zd.style.direction="ltr",this.Zd.style.width="100%",this.Zd.style.height="100%",(s=this.Zd).style.userSelect="none",s.style.webkitUserSelect="none",s.style.msUserSelect="none",s.style.MozUserSelect="none",s.style.webkitTapHighlightColor="transparent",this.nb=document.createElement("table"),this.nb.setAttribute("cellspacing","0"),this.Zd.appendChild(this.nb),this.sb=this.eb.bind(this),$s(this.cn)&&this.rb(!0),this.$i=new Bn(this.Dc.bind(this),this.cn,n),this.$t().Zc().l(this.hb.bind(this),this),this.lb=new Fs(this,this.U_),this.nb.appendChild(this.lb.hp());const e=i.autoSize&&this.ab();let r=this.cn.width,h=this.cn.height;if(e||0===r||0===h){const i=t.getBoundingClientRect();r=r||i.width,h=h||i.height}this.ob(r,h),this._b(),t.appendChild(this.Zd),this.ub(),this.$i.St().sc().l(this.$i.Xl.bind(this.$i),this),this.$i.w_().l(this.$i.Xl.bind(this.$i),this)}$t(){return this.$i}W(){return this.cn}qp(){return this.Zm}cb(){return this.lb}S(){this.rb(!1),0!==this.Xm&&window.cancelAnimationFrame(this.Xm),this.$i.Zc().p(this),this.$i.St().sc().p(this),this.$i.w_().p(this),this.$i.S();for(const t of this.Zm)this.nb.removeChild(t.hp()),t.hm().p(this),t.lm().p(this),t.S();this.Zm=[],f(this.lb).S(),null!==this.Zd.parentElement&&this.Zd.parentElement.removeChild(this.Zd),this.Pc.S(),this.Dp.S(),this.Op.S(),this.fb()}ob(t,i,n=!1){if(this.ro===i&&this.o_===t)return;const s=function(t){const i=Math.floor(t.width),n=Math.floor(t.height);return Jn({width:i-i%2,height:n-n%2})}(Jn({width:t,height:i}));this.ro=s.height,this.o_=s.width;const e=this.ro+"px",r=this.o_+"px";f(this.Zd).style.height=e,f(this.Zd).style.width=r,this.nb.style.height=e,this.nb.style.width=r,n?this.pb(lt.es(),performance.now()):this.$i.Xl()}fp(t){void 0===t&&(t=lt.es());for(let i=0;i{let s=0;for(let e=0;e{f("left"===i?this.lb.Em():this.lb.Im()).xp(f(t),n,s)};if(this.cn.timeScale.visible){const i=this.lb.Mp();if(null!==t){let e=0;this.Mb()&&(r("left",e,n),e=f(s.bm()).Mp().width),this.lb.xp(t,e,n),e+=i.width,this.xb()&&r("right",e,n)}n+=i.height}return Jn({width:i,height:n})}Tb(){let t=0,i=0,n=0;for(const s of this.Zm)this.Mb()&&(i=Math.max(i,f(s.bm()).ap(),this.cn.leftPriceScale.minimumWidth)),this.xb()&&(n=Math.max(n,f(s.wm()).ap(),this.cn.rightPriceScale.minimumWidth)),t+=s.g_();i=as(i),n=as(n);const s=this.o_,e=this.ro,r=Math.max(s-i-n,0),h=this.cn.timeScale.visible;let l=h?Math.max(this.lb.Fm(),this.cn.timeScale.minimumHeight):0;var a;l=(a=l)+a%2;const o=0+l,_=e{t.Zp()})),3===(null===(n=this.Jm)||void 0===n?void 0:n.jn())&&(this.Jm.ts(t),this.Rb(),this.Db(this.Jm),this.Ob(this.Jm,i),t=this.Jm,this.Jm=null)),this.fp(t)}Ob(t,i){for(const n of t.Qn())this.ns(n,i)}Db(t){const i=this.$i.Uc();for(let n=0;n{if(this.Qm=!1,this.Xm=0,null!==this.Jm){const i=this.Jm;this.Jm=null,this.pb(i,t);for(const n of i.Qn())if(5===n.qn&&!n.Ot.Ju(t)){this.$t().Zn(n.Ot);break}}})))}Rb(){this._b()}_b(){const t=this.$i.Uc(),i=t.length,n=this.Zm.length;for(let t=i;t{const n=i.zn().hl(t);null!==n&&e.set(i,n)}))}let r;if(null!==t){const i=null===(s=this.$i.St().Ui(t))||void 0===s?void 0:s.originalTime;void 0!==i&&(r=i)}const h=this.$t().Fc(),l=null!==h&&h.jc instanceof Yi?h.jc:void 0,a=null!==h&&void 0!==h.Av?h.Av.wr:void 0;return{zb:r,se:null!=t?t:void 0,Eb:null!=i?i:void 0,Ib:l,Lb:e,Nb:a,Fb:null!=n?n:void 0}}Vb(t,i,n){this.Dp.m((()=>this.Ab(t,i,n)))}Bb(t,i,n){this.Op.m((()=>this.Ab(t,i,n)))}hb(t,i,n){this.Pc.m((()=>this.Ab(t,i,n)))}ub(){const t=this.cn.timeScale.visible?"":"none";this.lb.hp().style.display=t}Mb(){return this.Zm[0].dp().P_().W().visible}xb(){return this.Zm[0].dp().R_().W().visible}ab(){return"ResizeObserver"in window&&(this.tb=new ResizeObserver((t=>{const i=t.find((t=>t.target===this.Gd));i&&this.ob(i.contentRect.width,i.contentRect.height)})),this.tb.observe(this.Gd,{box:"border-box"}),!0)}fb(){null!==this.tb&&this.tb.disconnect(),this.tb=null}}function $s(t){return Boolean(t.handleScroll.mouseWheel||t.handleScale.mouseWheel)}function Us(t){return function(t){return void 0!==t.open}(t)||function(t){return void 0!==t.value}(t)}function qs(t,i){var n={};for(var s in t)Object.prototype.hasOwnProperty.call(t,s)&&i.indexOf(s)<0&&(n[s]=t[s]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var e=0;for(s=Object.getOwnPropertySymbols(t);efunction(t,i){return i?i(t):void 0===(n=t).open&&void 0===n.value;var n}(s,h)?te({ot:i,se:n,zb:e},s):te(t(i,n,s,e,r),s)}function ne(t){return{Candlestick:ie(Gs),Bar:ie(Ks),Area:ie(Zs),Baseline:ie(Xs),Histogram:ie(Ys),Line:ie(Ys),Custom:ie(Js)}[t]}function se(t){return{se:0,jb:new Map,ha:t}}function ee(t,i){if(void 0!==t&&0!==t.length)return{Hb:i.key(t[0].ot),$b:i.key(t[t.length-1].ot)}}function re(t){let i;return t.forEach((t=>{void 0===i&&(i=t.zb)})),d(i)}class he{constructor(t){this.Ub=new Map,this.qb=new Map,this.Yb=new Map,this.Zb=[],this.U_=t}S(){this.Ub.clear(),this.qb.clear(),this.Yb.clear(),this.Zb=[]}Xb(t,i){let n=0!==this.Ub.size,s=!1;const e=this.qb.get(t);if(void 0!==e)if(1===this.qb.size)n=!1,s=!0,this.Ub.clear();else for(const i of this.Zb)i.pointData.jb.delete(t)&&(s=!0);let r=[];if(0!==i.length){const n=i.map((t=>t.time)),e=this.U_.createConverterToInternalObj(i),h=ne(t.Jh()),l=t.ya(),a=t.Ca();r=i.map(((i,r)=>{const o=e(i.time),_=this.U_.key(o);let u=this.Ub.get(_);void 0===u&&(u=se(o),this.Ub.set(_,u),s=!0);const c=h(o,u.se,i,n[r],l,a);return u.jb.set(t,c),c}))}n&&this.Kb(),this.Gb(t,r);let h=-1;if(s){const t=[];this.Ub.forEach((i=>{t.push({timeWeight:0,time:i.ha,pointData:i,originalTime:re(i.jb)})})),t.sort(((t,i)=>this.U_.key(t.time)-this.U_.key(i.time))),h=this.Jb(t)}return this.Qb(t,h,function(t,i,n){const s=ee(t,n),e=ee(i,n);if(void 0!==s&&void 0!==e)return{Ql:s.$b>=e.$b&&s.Hb>=e.Hb}}(this.qb.get(t),e,this.U_))}fd(t){return this.Xb(t,[])}tw(t,i){const n=i;!function(t){void 0===t.zb&&(t.zb=t.time)}(n),this.U_.preprocessData(i);const s=this.U_.createConverterToInternalObj([i])(i.time),e=this.Yb.get(t);if(void 0!==e&&this.U_.key(s)this.U_.key(t.time)this.U_.key(s.ot)?Qs(i)&&n.push(i):Qs(i)?n[n.length-1]=i:n.splice(-1,1),this.Yb.set(t,i.ot)}Gb(t,i){0!==i.length?(this.qb.set(t,i.filter(Qs)),this.Yb.set(t,i[i.length-1].ot)):(this.qb.delete(t),this.Yb.delete(t))}Kb(){for(const t of this.Zb)0===t.pointData.jb.size&&this.Ub.delete(this.U_.key(t.time))}Jb(t){let i=-1;for(let n=0;n{0!==i.length&&(t=Math.max(t,i[i.length-1].se))})),t}Qb(t,i,n){const s={sw:new Map,St:{Iu:this.nw()}};if(-1!==i)this.qb.forEach(((i,e)=>{s.sw.set(e,{He:i,ew:e===t?n:void 0})})),this.qb.has(t)||s.sw.set(t,{He:[],ew:n}),s.St.rw=this.Zb,s.St.hw=i;else{const i=this.qb.get(t);s.sw.set(t,{He:i||[],ew:n})}return s}}function le(t,i){t.se=i,t.jb.forEach((t=>{t.se=i}))}function ae(t){const i={value:t.Ot[3],time:t.zb};return void 0!==t.Wb&&(i.customValues=t.Wb),i}function oe(t){const i=ae(t);return void 0!==t.O&&(i.color=t.O),i}function _e(t){const i=ae(t);return void 0!==t.lt&&(i.lineColor=t.lt),void 0!==t.Ts&&(i.topColor=t.Ts),void 0!==t.Ps&&(i.bottomColor=t.Ps),i}function ue(t){const i=ae(t);return void 0!==t.Pe&&(i.topLineColor=t.Pe),void 0!==t.Re&&(i.bottomLineColor=t.Re),void 0!==t.Se&&(i.topFillColor1=t.Se),void 0!==t.ke&&(i.topFillColor2=t.ke),void 0!==t.ye&&(i.bottomFillColor1=t.ye),void 0!==t.Ce&&(i.bottomFillColor2=t.Ce),i}function ce(t){const i={open:t.Ot[0],high:t.Ot[1],low:t.Ot[2],close:t.Ot[3],time:t.zb};return void 0!==t.Wb&&(i.customValues=t.Wb),i}function de(t){const i=ce(t);return void 0!==t.O&&(i.color=t.O),i}function fe(t){const i=ce(t),{O:n,Vt:s,Zh:e}=t;return void 0!==n&&(i.color=n),void 0!==s&&(i.borderColor=s),void 0!==e&&(i.wickColor=e),i}function ve(t){return{Area:_e,Line:oe,Baseline:ue,Histogram:oe,Bar:de,Candlestick:fe,Custom:pe}[t]}function pe(t){const i=t.zb;return Object.assign(Object.assign({},t.He),{time:i})}const me={vertLine:{color:"#9598A1",width:1,style:3,visible:!0,labelVisible:!0,labelBackgroundColor:"#131722"},horzLine:{color:"#9598A1",width:1,style:3,visible:!0,labelVisible:!0,labelBackgroundColor:"#131722"},mode:1},be={vertLines:{color:"#D6DCDE",style:0,visible:!0},horzLines:{color:"#D6DCDE",style:0,visible:!0}},we={background:{type:"solid",color:"#FFFFFF"},textColor:"#191919",fontSize:12,fontFamily:z,attributionLogo:!0},ge={autoScale:!0,mode:0,invertScale:!1,alignLabels:!0,borderVisible:!0,borderColor:"#2B2B43",entireTextOnly:!1,visible:!1,ticksVisible:!1,scaleMargins:{bottom:.1,top:.2},minimumWidth:0},Me={rightOffset:0,barSpacing:6,minBarSpacing:.5,fixLeftEdge:!1,fixRightEdge:!1,lockVisibleTimeRangeOnResize:!1,rightBarStaysOnScroll:!1,borderVisible:!0,borderColor:"#2B2B43",visible:!0,timeVisible:!1,secondsVisible:!0,shiftVisibleRangeOnNewBar:!0,allowShiftVisibleRangeOnWhitespaceReplacement:!1,ticksVisible:!1,uniformDistribution:!1,minimumHeight:0,allowBoldLabels:!0},xe={color:"rgba(0, 0, 0, 0)",visible:!1,fontSize:48,fontFamily:z,fontStyle:"",text:"",horzAlign:"center",vertAlign:"center"};function Se(){return{width:0,height:0,autoSize:!1,layout:we,crosshair:me,grid:be,overlayPriceScales:Object.assign({},ge),leftPriceScale:Object.assign(Object.assign({},ge),{visible:!1}),rightPriceScale:Object.assign(Object.assign({},ge),{visible:!0}),timeScale:Me,watermark:xe,localization:{locale:rs?navigator.language:"",dateFormat:"dd MMM 'yy"},handleScroll:{mouseWheel:!0,pressedMouseMove:!0,horzTouchDrag:!0,vertTouchDrag:!0},handleScale:{axisPressedMouseMove:{time:!0,price:!0},axisDoubleClickReset:{time:!0,price:!0},mouseWheel:!0,pinch:!0},kineticScroll:{mouse:!1,touch:!0},trackingMode:{exitMode:1}}}class ke{constructor(t,i){this.lw=t,this.aw=i}applyOptions(t){this.lw.$t().Hc(this.aw,t)}options(){return this.Ii().W()}width(){return ht(this.aw)?this.lw.gb(this.aw):0}Ii(){return f(this.lw.$t().$c(this.aw)).Dt}}function ye(t,i,n){const s=qs(t,["time","originalTime"]),e=Object.assign({time:i},s);return void 0!==n&&(e.originalTime=n),e}const Ce={color:"#FF0000",price:0,lineStyle:2,lineWidth:1,lineVisible:!0,axisLabelVisible:!0,title:"",axisLabelColor:"",axisLabelTextColor:""};class Te{constructor(t){this.Lh=t}applyOptions(t){this.Lh.Hh(t)}options(){return this.Lh.W()}ow(){return this.Lh}}class Pe{constructor(t,i,n,s,e){this._w=new C,this.Is=t,this.uw=i,this.cw=n,this.U_=e,this.dw=s}S(){this._w.S()}priceFormatter(){return this.Is.ma()}priceToCoordinate(t){const i=this.Is.Ct();return null===i?null:this.Is.Dt().Rt(t,i.Ot)}coordinateToPrice(t){const i=this.Is.Ct();return null===i?null:this.Is.Dt().pn(t,i.Ot)}barsInLogicalRange(t){if(null===t)return null;const i=new Mn(new bn(t.from,t.to)).hu(),n=this.Is.zn();if(n.Ni())return null;const s=n.hl(i.Os(),1),e=n.hl(i.ui(),-1),r=f(n.sl()),h=f(n.An());if(null!==s&&null!==e&&s.se>e.se)return{barsBefore:t.from-r,barsAfter:h-t.to};const l={barsBefore:null===s||s.se===r?t.from-r:s.se-r,barsAfter:null===e||e.se===h?h-t.to:h-e.se};return null!==s&&null!==e&&(l.from=s.zb,l.to=e.zb),l}setData(t){this.U_,this.Is.Jh(),this.uw.fw(this.Is,t),this.pw("full")}update(t){this.Is.Jh(),this.uw.mw(this.Is,t),this.pw("update")}dataByIndex(t,i){const n=this.Is.zn().hl(t,i);if(null===n)return null;return ve(this.seriesType())(n)}data(){const t=ve(this.seriesType());return this.Is.zn().ie().map((i=>t(i)))}subscribeDataChanged(t){this._w.l(t)}unsubscribeDataChanged(t){this._w.v(t)}setMarkers(t){this.U_;const i=t.map((t=>ye(t,this.U_.convertHorzItemToInternal(t.time),t.time)));this.Is.ia(i)}markers(){return this.Is.na().map((t=>ye(t,t.originalTime,void 0)))}applyOptions(t){this.Is.Hh(t)}options(){return V(this.Is.W())}priceScale(){return this.cw.priceScale(this.Is.Dt().Ta())}createPriceLine(t){const i=T(V(Ce),t),n=this.Is.sa(i);return new Te(n)}removePriceLine(t){this.Is.ea(t.ow())}seriesType(){return this.Is.Jh()}attachPrimitive(t){this.Is.Sa(t),t.attached&&t.attached({chart:this.dw,series:this,requestUpdate:()=>this.Is.$t().Xl()})}detachPrimitive(t){this.Is.ka(t),t.detached&&t.detached()}pw(t){this._w.M()&&this._w.m(t)}}class Re{constructor(t,i,n){this.bw=new C,this.pu=new C,this.Om=new C,this.$i=t,this.kl=t.St(),this.lb=i,this.kl.tc().l(this.ww.bind(this)),this.kl.nc().l(this.gw.bind(this)),this.lb.Lm().l(this.Mw.bind(this)),this.U_=n}S(){this.kl.tc().p(this),this.kl.nc().p(this),this.lb.Lm().p(this),this.bw.S(),this.pu.S(),this.Om.S()}scrollPosition(){return this.kl.ju()}scrollToPosition(t,i){i?this.kl.Gu(t,1e3):this.$i.Jn(t)}scrollToRealTime(){this.kl.Ku()}getVisibleRange(){const t=this.kl.Du();return null===t?null:{from:t.from.originalTime,to:t.to.originalTime}}setVisibleRange(t){const i={from:this.U_.convertHorzItemToInternal(t.from),to:this.U_.convertHorzItemToInternal(t.to)},n=this.kl.Au(i);this.$i.vd(n)}getVisibleLogicalRange(){const t=this.kl.Ru();return null===t?null:{from:t.Os(),to:t.ui()}}setVisibleLogicalRange(t){c(t.from<=t.to,"The from index cannot be after the to index."),this.$i.vd(t)}resetTimeScale(){this.$i.Kn()}fitContent(){this.$i.rc()}logicalToCoordinate(t){const i=this.$i.St();return i.Ni()?null:i.zt(t)}coordinateToLogical(t){return this.kl.Ni()?null:this.kl.Lu(t)}timeToCoordinate(t){const i=this.U_.convertHorzItemToInternal(t),n=this.kl.Da(i,!1);return null===n?null:this.kl.zt(n)}coordinateToTime(t){const i=this.$i.St(),n=i.Lu(t),s=i.Ui(n);return null===s?null:s.originalTime}width(){return this.lb._m().width}height(){return this.lb._m().height}subscribeVisibleTimeRangeChange(t){this.bw.l(t)}unsubscribeVisibleTimeRangeChange(t){this.bw.v(t)}subscribeVisibleLogicalRangeChange(t){this.pu.l(t)}unsubscribeVisibleLogicalRangeChange(t){this.pu.v(t)}subscribeSizeChange(t){this.Om.l(t)}unsubscribeSizeChange(t){this.Om.v(t)}applyOptions(t){this.kl.Hh(t)}options(){return Object.assign(Object.assign({},V(this.kl.W())),{barSpacing:this.kl.he()})}ww(){this.bw.M()&&this.bw.m(this.getVisibleRange())}gw(){this.pu.M()&&this.pu.m(this.getVisibleLogicalRange())}Mw(t){this.Om.m(t.width,t.height)}}function De(t){if(void 0===t||"custom"===t.type)return;const i=t;void 0!==i.minMove&&void 0===i.precision&&(i.precision=function(t){if(t>=1)return 0;let i=0;for(;i<8;i++){const n=Math.round(t);if(Math.abs(n-t)<1e-8)return i;t*=10}return i}(i.minMove))}function Oe(t){return function(t){if(O(t.handleScale)){const i=t.handleScale;t.handleScale={axisDoubleClickReset:{time:i,price:i},axisPressedMouseMove:{time:i,price:i},mouseWheel:i,pinch:i}}else if(void 0!==t.handleScale){const{axisPressedMouseMove:i,axisDoubleClickReset:n}=t.handleScale;O(i)&&(t.handleScale.axisPressedMouseMove={time:i,price:i}),O(n)&&(t.handleScale.axisDoubleClickReset={time:n,price:n})}const i=t.handleScroll;O(i)&&(t.handleScroll={horzTouchDrag:i,vertTouchDrag:i,mouseWheel:i,pressedMouseMove:i})}(t),t}class Ve{constructor(t,i,n){this.xw=new Map,this.Sw=new Map,this.kw=new C,this.yw=new C,this.Cw=new C,this.Tw=new he(i);const s=void 0===n?V(Se()):T(V(Se()),Oe(n));this.U_=i,this.lw=new Hs(t,s,i),this.lw.hm().l((t=>{this.kw.M()&&this.kw.m(this.Pw(t()))}),this),this.lw.lm().l((t=>{this.yw.M()&&this.yw.m(this.Pw(t()))}),this),this.lw.Zc().l((t=>{this.Cw.M()&&this.Cw.m(this.Pw(t()))}),this);const e=this.lw.$t();this.Rw=new Re(e,this.lw.cb(),this.U_)}remove(){this.lw.hm().p(this),this.lw.lm().p(this),this.lw.Zc().p(this),this.Rw.S(),this.lw.S(),this.xw.clear(),this.Sw.clear(),this.kw.S(),this.yw.S(),this.Cw.S(),this.Tw.S()}resize(t,i,n){this.autoSizeActive()||this.lw.ob(t,i,n)}addCustomSeries(t,i){const n=v(t),s=Object.assign(Object.assign({},h),n.defaultOptions());return this.Dw("Custom",s,i,n)}addAreaSeries(t){return this.Dw("Area",s,t)}addBaselineSeries(t){return this.Dw("Baseline",e,t)}addBarSeries(t){return this.Dw("Bar",i,t)}addCandlestickSeries(i={}){return function(t){void 0!==t.borderColor&&(t.borderUpColor=t.borderColor,t.borderDownColor=t.borderColor),void 0!==t.wickColor&&(t.wickUpColor=t.wickColor,t.wickDownColor=t.wickColor)}(i),this.Dw("Candlestick",t,i)}addHistogramSeries(t){return this.Dw("Histogram",r,t)}addLineSeries(t){return this.Dw("Line",n,t)}removeSeries(t){const i=d(this.xw.get(t)),n=this.Tw.fd(i);this.lw.$t().fd(i),this.Ow(n),this.xw.delete(t),this.Sw.delete(i)}fw(t,i){this.Ow(this.Tw.Xb(t,i))}mw(t,i){this.Ow(this.Tw.tw(t,i))}subscribeClick(t){this.kw.l(t)}unsubscribeClick(t){this.kw.v(t)}subscribeCrosshairMove(t){this.Cw.l(t)}unsubscribeCrosshairMove(t){this.Cw.v(t)}subscribeDblClick(t){this.yw.l(t)}unsubscribeDblClick(t){this.yw.v(t)}priceScale(t){return new ke(this.lw,t)}timeScale(){return this.Rw}applyOptions(t){this.lw.Hh(Oe(t))}options(){return this.lw.W()}takeScreenshot(){return this.lw.bb()}autoSizeActive(){return this.lw.Sb()}chartElement(){return this.lw.kb()}paneSize(){const t=this.lw.Cb();return{height:t.height,width:t.width}}setCrosshairPosition(t,i,n){const s=this.xw.get(n);if(void 0===s)return;const e=this.lw.$t().cr(s);null!==e&&this.lw.$t().ld(t,i,e)}clearCrosshairPosition(){this.lw.$t().ad(!0)}Dw(t,i,n={},s){De(n.priceFormat);const e=T(V(l),V(i),n),r=this.lw.$t().ud(t,e,s),h=new Pe(r,this,this,this,this.U_);return this.xw.set(h,r),this.Sw.set(r,h),h}Ow(t){const i=this.lw.$t();i.od(t.St.Iu,t.St.rw,t.St.hw),t.sw.forEach(((t,i)=>i.J(t.He,t.ew))),i.Fu()}Vw(t){return d(this.Sw.get(t))}Pw(t){const i=new Map;t.Lb.forEach(((t,n)=>{const s=n.Jh(),e=ve(s)(t);if("Custom"!==s)c(Us(e));else{const t=n.Ca();c(!t||!1===t(e))}i.set(this.Vw(n),e)}));const n=void 0!==t.Ib&&this.Sw.has(t.Ib)?this.Vw(t.Ib):void 0;return{time:t.zb,logical:t.se,point:t.Eb,hoveredSeries:n,hoveredObjectId:t.Nb,seriesData:i,sourceEvent:t.Fb}}}function Be(t,i,n){let s;if(D(t)){const i=document.getElementById(t);c(null!==i,`Cannot find element in DOM with id=${t}`),s=i}else s=t;const e=new Ve(s,i,n);return i.setOptions(e.options()),e}function Ae(t,i){return Be(t,new Gn,Gn.Ad(i))}function ze(){return Gn}const Ee=Object.assign(Object.assign({},l),h);function Ie(){return"4.2.2"}export{On as ColorType,et as CrosshairMode,Rn as LastPriceAnimationMode,o as LineStyle,a as LineType,Bi as MismatchDirection,Dn as PriceLineSource,cn as PriceScaleMode,Vn as TickMarkType,Pn as TrackingModeExitMode,Ae as createChart,Be as createChartEx,Ee as customSeriesDefaultOptions,ze as defaultHorzScaleBehavior,An as isBusinessDay,zn as isUTCTimestamp,Ie as version}; diff --git a/src/website/packages/lightweight-charts/v4.2.2/types.d.ts b/src/website/packages/lightweight-charts/v4.2.2/types.d.ts new file mode 100644 index 000000000..39fbd07c7 --- /dev/null +++ b/src/website/packages/lightweight-charts/v4.2.2/types.d.ts @@ -0,0 +1,3882 @@ +// Generated by dts-bundle-generator v8.0.1 + +type CanvasRenderingTarget2D = any; + +export declare const customSeriesDefaultOptions: CustomSeriesOptions; +/** + * Represents a type of color. + */ +export declare enum ColorType { + /** Solid color */ + Solid = "solid", + /** Vertical gradient color */ + VerticalGradient = "gradient", +} +/** + * Represents the crosshair mode. + */ +export declare enum CrosshairMode { + /** + * This mode allows crosshair to move freely on the chart. + */ + Normal = 0, + /** + * This mode sticks crosshair's horizontal line to the price value of a single-value series or to the close price of OHLC-based series. + */ + Magnet = 1, + /** + * This mode disables rendering of the crosshair. + */ + Hidden = 2, +} +/** + * Represents the type of the last price animation for series such as area or line. + */ +export declare enum LastPriceAnimationMode { + /** + * Animation is always disabled + */ + Disabled = 0, + /** + * Animation is always enabled. + */ + Continuous = 1, + /** + * Animation is active after new data. + */ + OnDataUpdate = 2, +} +/** + * Represents the possible line styles. + */ +export declare enum LineStyle { + /** + * A solid line. + */ + Solid = 0, + /** + * A dotted line. + */ + Dotted = 1, + /** + * A dashed line. + */ + Dashed = 2, + /** + * A dashed line with bigger dashes. + */ + LargeDashed = 3, + /** + * A dotted line with more space between dots. + */ + SparseDotted = 4, +} +/** + * Represents the possible line types. + */ +export declare enum LineType { + /** + * A line. + */ + Simple = 0, + /** + * A stepped line. + */ + WithSteps = 1, + /** + * A curved line. + */ + Curved = 2, +} +/** + * Search direction if no data found at provided index + */ +export declare enum MismatchDirection { + /** + * Search the nearest left item + */ + NearestLeft = -1, + /** + * Do not search + */ + None = 0, + /** + * Search the nearest right item + */ + NearestRight = 1, +} +/** + * Represents the source of data to be used for the horizontal price line. + */ +export declare enum PriceLineSource { + /** + * Use the last bar data. + */ + LastBar = 0, + /** + * Use the last visible data of the chart viewport. + */ + LastVisible = 1, +} +/** + * Represents the price scale mode. + */ +export declare enum PriceScaleMode { + /** + * Price scale shows prices. Price range changes linearly. + */ + Normal = 0, + /** + * Price scale shows prices. Price range changes logarithmically. + */ + Logarithmic = 1, + /** + * Price scale shows percentage values according the first visible value of the price scale. + * The first visible value is 0% in this mode. + */ + Percentage = 2, + /** + * The same as percentage mode, but the first value is moved to 100. + */ + IndexedTo100 = 3, +} +/** + * Represents the type of a tick mark on the time axis. + */ +export declare enum TickMarkType { + /** + * The start of the year (e.g. it's the first tick mark in a year). + */ + Year = 0, + /** + * The start of the month (e.g. it's the first tick mark in a month). + */ + Month = 1, + /** + * A day of the month. + */ + DayOfMonth = 2, + /** + * A time without seconds. + */ + Time = 3, + /** + * A time with seconds. + */ + TimeWithSeconds = 4, +} +/** + * Determine how to exit the tracking mode. + * + * By default, mobile users will long press to deactivate the scroll and have the ability to check values and dates. + * Another press is required to activate the scroll, be able to move left/right, zoom, etc. + */ +export declare enum TrackingModeExitMode { + /** + * Tracking Mode will be deactivated on touch end event. + */ + OnTouchEnd = 0, + /** + * Tracking Mode will be deactivated on the next tap event. + */ + OnNextTap = 1, +} +/** + * This function is the simplified main entry point of the Lightweight Charting Library with time points for the horizontal scale. + * + * @param container - ID of HTML element or element itself + * @param options - Any subset of options to be applied at start. + * @returns An interface to the created chart + */ +export declare function createChart( + container: string | HTMLElement, + options?: DeepPartial, +): IChartApi; +/** + * This function is the main entry point of the Lightweight Charting Library. If you are using time values + * for the horizontal scale then it is recommended that you rather use the {@link createChart} function. + * + * @template HorzScaleItem - type of points on the horizontal scale + * @template THorzScaleBehavior - type of horizontal axis strategy that encapsulate all the specific behaviors of the horizontal scale type + * + * @param container - ID of HTML element or element itself + * @param horzScaleBehavior - Horizontal scale behavior + * @param options - Any subset of options to be applied at start. + * @returns An interface to the created chart + */ +export declare function createChartEx< + HorzScaleItem, + THorzScaleBehavior extends IHorzScaleBehavior, +>( + container: string | HTMLElement, + horzScaleBehavior: THorzScaleBehavior, + options?: DeepPartial>, +): IChartApiBase; +/** + * Provides the default implementation of the horizontal scale (time-based) that can be used as a base for extending the horizontal scale with custom behavior. + * This allows for the introduction of custom functionality without re-implementing the entire {@link IHorzScaleBehavior}<{@link Time}> interface. + * + * For further details, refer to the {@link createChartEx} chart constructor method. + * + * @returns An uninitialized class implementing the {@link IHorzScaleBehavior}<{@link Time}> interface + */ +export declare function defaultHorzScaleBehavior(): new () => IHorzScaleBehavior