diff --git a/.gitignore b/.gitignore index 3cc4a7322..bad2e7c64 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ # Builds target websites/dist -vecid-to-indexes.js +bridge/ /ids.txt # Copies diff --git a/Cargo.lock b/Cargo.lock index cd979dea8..2db07077d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,6 +54,28 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocative" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fac2ce611db8b8cee9b2aa886ca03c924e9da5e5295d0dbd0526e5d0b0710f7" +dependencies = [ + "allocative_derive", + "ctor", + "parking_lot 0.11.2", +] + +[[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.106", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -539,6 +561,8 @@ dependencies = [ name = "brk_computer" version = "0.0.98" dependencies = [ + "allocative", + "allocative_derive", "bitcoin", "bitcoincore-rpc", "brk_error", @@ -546,13 +570,17 @@ dependencies = [ "brk_indexer", "brk_logger", "brk_parser", + "brk_store", "brk_structs", "derive_deref", + "inferno", + "jiff", "log", "num_enum", "pco", "rayon", "serde", + "serde_json", "vecdb", "zerocopy", "zerocopy-derive", @@ -1068,6 +1096,8 @@ dependencies = [ name = "brk_structs" version = "0.0.98" dependencies = [ + "allocative", + "allocative_derive", "bitcoin", "bitcoincore-rpc", "brk_error", @@ -1118,6 +1148,12 @@ version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" +[[package]] +name = "bytemuck" +version = "1.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" + [[package]] name = "byteorder" version = "1.5.0" @@ -1202,6 +1238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -1459,6 +1496,16 @@ dependencies = [ "smallvec", ] +[[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.7" @@ -1550,7 +1597,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core", + "parking_lot_core 0.9.11", ] [[package]] @@ -2289,6 +2336,28 @@ dependencies = [ "cfb", ] +[[package]] +name = "inferno" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96d2465363ed2d81857759fc864cf6bb7997f79327aec028d65bd7989393685" +dependencies = [ + "ahash", + "clap", + "crossbeam-channel", + "crossbeam-utils", + "dashmap", + "env_logger", + "indexmap 2.11.0", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml", + "rgb", + "str_stack", +] + [[package]] name = "inotify" version = "0.11.0" @@ -2309,6 +2378,15 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "interval-heap" version = "0.0.5" @@ -2472,7 +2550,7 @@ checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ "bitflags 2.9.4", "libc", - "redox_syscall", + "redox_syscall 0.5.17", ] [[package]] @@ -2698,6 +2776,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[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-integer" version = "0.1.46" @@ -3262,6 +3350,17 @@ dependencies = [ "unicode-width", ] +[[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" version = "0.12.4" @@ -3269,7 +3368,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.11", +] + +[[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]] @@ -3280,7 +3393,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.17", "smallvec", "windows-targets 0.52.6", ] @@ -3531,6 +3644,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + [[package]] name = "quick_cache" version = "0.6.16" @@ -3540,7 +3662,7 @@ dependencies = [ "ahash", "equivalent", "hashbrown 0.15.5", - "parking_lot", + "parking_lot 0.12.4", ] [[package]] @@ -3665,6 +3787,15 @@ dependencies = [ "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.17" @@ -3733,6 +3864,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "rgb" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.14" @@ -3935,13 +4075,13 @@ checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" [[package]] name = "seqdb" version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade192d6f8a1e58c43428c8e194016124f20744f75cb14e2fcc0c79309fe4f91" dependencies = [ + "allocative", + "allocative_derive", "libc", "log", "memmap2", - "parking_lot", + "parking_lot 0.12.4", "rayon", "zerocopy", "zerocopy-derive", @@ -4172,6 +4312,12 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" +[[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" @@ -4785,12 +4931,12 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23" [[package]] name = "vecdb" version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3e103eeb634851690d815a886ba8fe1ab21d2f520d915c411979bc76de8419" dependencies = [ + "allocative", + "allocative_derive", "ctrlc", "log", - "parking_lot", + "parking_lot 0.12.4", "pco", "rayon", "seqdb", @@ -4805,8 +4951,6 @@ dependencies = [ [[package]] name = "vecdb_derive" version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdf71ae8eeaf2f187cee599e8e34a49bf6fd0536977bd009d94be35e3801754" dependencies = [ "quote", "syn 2.0.106", @@ -4929,6 +5073,22 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[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-util" version = "0.1.10" @@ -4938,6 +5098,12 @@ dependencies = [ "windows-sys 0.60.2", ] +[[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.61.2" diff --git a/Cargo.toml b/Cargo.toml index f025b535b..bcf7f2b8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,8 @@ debug = true inherits = "release" [workspace.dependencies] +allocative = { version = "0.3.4", features = ["parking_lot"] } +allocative_derive = "0.3.3" axum = "0.8.4" bitcoin = { version = "0.32.7", features = ["serde"] } bitcoincore-rpc = "0.19.0" @@ -53,8 +55,8 @@ serde_bytes = "0.11.17" serde_derive = "1.0.219" serde_json = { version = "1.0.143", features = ["float_roundtrip"] } tokio = { version = "1.47.1", features = ["rt-multi-thread"] } -# vecdb = { path = "../seqdb/crates/vecdb", features = ["derive"]} -vecdb = { version = "0.2.9", features = ["derive"]} +vecdb = { path = "../seqdb/crates/vecdb", features = ["derive"]} +# vecdb = { version = "0.2.9", features = ["derive"]} zerocopy = "0.8.26" zerocopy-derive = "0.8.26" diff --git a/TODO.md b/TODO.md index 233174667..9cc6cd20b 100644 --- a/TODO.md +++ b/TODO.md @@ -64,11 +64,9 @@ - miners - maybe xpubs - charts - - improve some names and colors - - remove `sum` series when it's a duplicate of the `base` (in subsidy for example) + - improve names and colors - selected unit sometimes changes when going back end forth - add support for custom charts - - separate z-score charts from "realized price" (with their own prices), have 4y, 2y and 1y - price scale format depends on unit, hide digits for sats for example (if/when possible) - table - pagination diff --git a/crates/brk_bundler/src/lib.rs b/crates/brk_bundler/src/lib.rs index 482460417..6eda405cf 100644 --- a/crates/brk_bundler/src/lib.rs +++ b/crates/brk_bundler/src/lib.rs @@ -35,7 +35,9 @@ pub async fn bundle(websites_path: &Path, source_folder: &str, watch: bool) -> i ..Default::default() }); - bundler.write().await.unwrap(); + if let Err(error) = bundler.write().await { + error!("{error:?}"); + } let absolute_source_index_path = source_path.join("index.html").absolutize(); let absolute_source_index_path_clone = absolute_source_index_path.clone(); diff --git a/crates/brk_cli/src/bridge.rs b/crates/brk_cli/src/bridge.rs index 9b679f566..908ba6b77 100644 --- a/crates/brk_cli/src/bridge.rs +++ b/crates/brk_cli/src/bridge.rs @@ -1,19 +1,20 @@ use std::{fs, io, path::Path}; +use brk_computer::pools; use brk_interface::{Index, Interface}; use brk_server::VERSION; use crate::website::Website; -const SCRIPTS: &str = "scripts"; +const BRIDGE_PATH: &str = "scripts/bridge"; #[allow(clippy::upper_case_acronyms)] pub trait Bridge { - fn generate_bridge_file(&self, website: Website, websites_path: &Path) -> io::Result<()>; + fn generate_bridge_files(&self, website: Website, websites_path: &Path) -> io::Result<()>; } impl Bridge for Interface<'static> { - fn generate_bridge_file(&self, website: Website, websites_path: &Path) -> io::Result<()> { + fn generate_bridge_files(&self, website: Website, websites_path: &Path) -> io::Result<()> { if website.is_none() { return Ok(()); } @@ -24,88 +25,130 @@ impl Bridge for Interface<'static> { return Ok(()); } - let path = path.join(SCRIPTS); + let path = path.join(BRIDGE_PATH); fs::create_dir_all(&path)?; - let path = path.join(Path::new("vecid-to-indexes.js")); + generate_vecs_file(self, &path)?; + generate_pools_file(&path) + } +} - let indexes = Index::all(); +fn generate_pools_file(parent: &Path) -> io::Result<()> { + let path = parent.join(Path::new("pools.js")); - let mut contents = format!( - "// + let pools = pools(); + + let mut contents = "// +// File auto-generated, any modifications will be overwritten +// +" + .to_string(); + + contents += " +/** @typedef {ReturnType} Pools */ +/** @typedef {keyof Pools} Pool */ + +export function createPools() { +return /** @type {const} */ ({ +"; + + let mut sorted_pools: Vec<_> = pools.iter().collect(); + sorted_pools.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase())); + + contents += &sorted_pools + .iter() + .map(|pool| { + let id = pool.serialized_id(); + format!(" {id}: \"{}\",", pool.name) + }) + .collect::>() + .join("\n"); + + contents += "\n });\n}\n"; + + fs::write(path, contents) +} + +fn generate_vecs_file(interface: &Interface<'static>, parent: &Path) -> io::Result<()> { + let path = parent.join(Path::new("vecs.js")); + + let indexes = Index::all(); + + let mut contents = format!( + "// // File auto-generated, any modifications will be overwritten // export const VERSION = \"v{VERSION}\"; " - ); + ); - contents += &indexes + contents += &indexes + .iter() + .enumerate() + .map(|(i_of_i, i)| { + // let lowered = i.to_string().to_lowercase(); + format!("/** @typedef {{{i_of_i}}} {i} */",) + }) + .collect::>() + .join("\n"); + + contents += &format!( + "\n\n/** @typedef {{{}}} Index */\n", + indexes .iter() - .enumerate() - .map(|(i_of_i, i)| { - // let lowered = i.to_string().to_lowercase(); - format!("/** @typedef {{{i_of_i}}} {i} */",) - }) + .map(|i| i.to_string()) .collect::>() - .join("\n"); + .join(" | ") + ); - contents += &format!( - "\n\n/** @typedef {{{}}} Index */\n", - indexes - .iter() - .map(|i| i.to_string()) - .collect::>() - .join(" | ") - ); - - contents += " + contents += " /** @typedef {ReturnType} Indexes */ export function createIndexes() { - return { +return { "; - contents += &indexes - .iter() - .enumerate() - .map(|(i_of_i, i)| { - let lowered = i.to_string().to_lowercase(); - format!(" {lowered}: /** @satisfies {{{i}}} */ ({i_of_i}),",) - }) - .collect::>() - .join("\n"); + contents += &indexes + .iter() + .enumerate() + .map(|(i_of_i, i)| { + let lowered = i.to_string().to_lowercase(); + format!(" {lowered}: /** @satisfies {{{i}}} */ ({i_of_i}),",) + }) + .collect::>() + .join("\n"); - contents += " };\n}\n"; + contents += " };\n}\n"; - contents += " + contents += " /** @typedef {ReturnType} VecIdToIndexes /** @typedef {keyof VecIdToIndexes} VecId */ /** - * @returns {Record} - */ +* @returns {Record} +*/ export function createVecIdToIndexes() { - return { +return { "; - self.id_to_index_to_vec() - .iter() - .for_each(|(id, index_to_vec)| { - let indexes = index_to_vec - .keys() - .map(|i| (*i as u8).to_string()) - // .map(|i| i.to_string()) - .collect::>() - .join(", "); + interface + .id_to_index_to_vec() + .iter() + .for_each(|(id, index_to_vec)| { + let indexes = index_to_vec + .keys() + .map(|i| (*i as u8).to_string()) + // .map(|i| i.to_string()) + .collect::>() + .join(", "); - contents += &format!(" \"{id}\": [{indexes}],\n"); - }); + contents += &format!(" \"{id}\": [{indexes}],\n"); + }); - contents += " };\n}\n"; + contents += " };\n}\n"; - fs::write(path, contents) - } + fs::write(path, contents) } diff --git a/crates/brk_cli/src/config.rs b/crates/brk_cli/src/config.rs index 703508633..83711ce69 100644 --- a/crates/brk_cli/src/config.rs +++ b/crates/brk_cli/src/config.rs @@ -6,7 +6,6 @@ use std::{ use bitcoincore_rpc::{self, Auth, Client}; use brk_fetcher::Fetcher; use clap::Parser; -use clap_derive::Parser; use color_eyre::eyre::eyre; use serde::{Deserialize, Deserializer, Serialize}; diff --git a/crates/brk_cli/src/lib.rs b/crates/brk_cli/src/lib.rs index 4c2e38b9f..8ef1edfab 100644 --- a/crates/brk_cli/src/lib.rs +++ b/crates/brk_cli/src/lib.rs @@ -107,7 +107,7 @@ pub fn run() -> color_eyre::Result<()> { downloaded_websites_path }; - interface.generate_bridge_file(website, websites_path.as_path())?; + interface.generate_bridge_files(website, websites_path.as_path())?; Some(bundle(&websites_path, website.to_folder_name(), true).await?) } else { @@ -135,6 +135,8 @@ pub fn run() -> color_eyre::Result<()> { let starting_indexes = indexer.index(&parser, rpc, &exit, config.check_collisions()).unwrap(); + // dbg!(&starting_indexes); + computer.compute(&indexer, starting_indexes, &exit).unwrap(); info!("Waiting for new blocks..."); diff --git a/crates/brk_cli/src/website.rs b/crates/brk_cli/src/website.rs index fbb16dfd3..8a71c4e9b 100644 --- a/crates/brk_cli/src/website.rs +++ b/crates/brk_cli/src/website.rs @@ -2,6 +2,7 @@ use clap_derive::ValueEnum; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, ValueEnum)] +#[serde(rename_all = "lowercase")] pub enum Website { None, Bitview, diff --git a/crates/brk_computer/Cargo.toml b/crates/brk_computer/Cargo.toml index e440246c9..b356a9213 100644 --- a/crates/brk_computer/Cargo.toml +++ b/crates/brk_computer/Cargo.toml @@ -10,6 +10,8 @@ rust-version.workspace = true build = "build.rs" [dependencies] +allocative = { workspace = true } +allocative_derive = { workspace = true } bitcoin = { workspace = true } bitcoincore-rpc = { workspace = true } brk_structs = { workspace = true } @@ -17,14 +19,18 @@ brk_error = { workspace = true } brk_fetcher = { workspace = true } brk_indexer = { workspace = true } brk_logger = { workspace = true } +brk_store = { workspace = true } brk_parser = { workspace = true } vecdb = { workspace = true } derive_deref = { workspace = true } +inferno = "0.12.3" +jiff = { workspace = true } log = { workspace = true } num_enum = "0.7.4" pco = "0.4.6" rayon = { workspace = true } serde = { workspace = true } +serde_json = { workspace = true } zerocopy = { workspace = true } zerocopy-derive = { workspace = true } diff --git a/crates/brk_computer/examples/pools.rs b/crates/brk_computer/examples/pools.rs index 506a0c657..d850b41db 100644 --- a/crates/brk_computer/examples/pools.rs +++ b/crates/brk_computer/examples/pools.rs @@ -4,7 +4,8 @@ use brk_computer::{Computer, pools}; use brk_error::Result; use brk_fetcher::Fetcher; use brk_indexer::Indexer; -use vecdb::Exit; +use brk_structs::{AddressBytes, OutputIndex, OutputType}; +use vecdb::{AnyIterableVec, Exit, VecIterator}; fn main() -> Result<()> { brk_logger::init(Some(Path::new(".log")))?; @@ -27,20 +28,89 @@ fn main() -> Result<()> { let mut res: BTreeMap<&'static str, usize> = BTreeMap::default(); - let mut height_to_first_txindex_iter = indexer.vecs.height_to_first_txindex.iter(); - // let mut i = indexer.vecs.txz + let vecs = indexer.vecs; + let stores = indexer.stores; - indexer - .stores + let mut height_to_first_txindex_iter = vecs.height_to_first_txindex.iter(); + let mut txindex_to_first_outputindex_iter = vecs.txindex_to_first_outputindex.iter(); + let mut txindex_to_output_count_iter = computer.indexes.txindex_to_output_count.iter(); + let mut outputindex_to_outputtype_iter = vecs.outputindex_to_outputtype.iter(); + let mut outputindex_to_typeindex_iter = vecs.outputindex_to_typeindex.iter(); + let mut p2pk65addressindex_to_p2pk65bytes_iter = + vecs.p2pk65addressindex_to_p2pk65bytes.iter(); + let mut p2pk33addressindex_to_p2pk33bytes_iter = + vecs.p2pk33addressindex_to_p2pk33bytes.iter(); + let mut p2pkhaddressindex_to_p2pkhbytes_iter = + vecs.p2pkhaddressindex_to_p2pkhbytes.iter(); + let mut p2shaddressindex_to_p2shbytes_iter = vecs.p2shaddressindex_to_p2shbytes.iter(); + let mut p2wpkhaddressindex_to_p2wpkhbytes_iter = + vecs.p2wpkhaddressindex_to_p2wpkhbytes.iter(); + let mut p2wshaddressindex_to_p2wshbytes_iter = + vecs.p2wshaddressindex_to_p2wshbytes.iter(); + let mut p2traddressindex_to_p2trbytes_iter = vecs.p2traddressindex_to_p2trbytes.iter(); + let mut p2aaddressindex_to_p2abytes_iter = vecs.p2aaddressindex_to_p2abytes.iter(); + + let unknown = pools.get_unknown(); + + stores .height_to_coinbase_tag .iter() - .for_each(|(_, coinbase_tag)| { - let pool = pools.find_from_coinbase_tag(&coinbase_tag); - if let Some(pool) = pool { - *res.entry(pool.name).or_default() += 1; - } else { - *res.entry(pools.get_unknown().name).or_default() += 1; - } + .for_each(|(height, coinbase_tag)| { + let txindex = height_to_first_txindex_iter.unwrap_get_inner(height); + let outputindex = txindex_to_first_outputindex_iter.unwrap_get_inner(txindex); + let outputcount = txindex_to_output_count_iter.unwrap_get_inner(txindex); + + let pool = (*outputindex..(*outputindex + *outputcount)) + .map(OutputIndex::from) + .find_map(|outputindex| { + let outputtype = + outputindex_to_outputtype_iter.unwrap_get_inner(outputindex); + let typeindex = + outputindex_to_typeindex_iter.unwrap_get_inner(outputindex); + + let address = match outputtype { + OutputType::P2PK65 => Some(AddressBytes::from( + p2pk65addressindex_to_p2pk65bytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2PK33 => Some(AddressBytes::from( + p2pk33addressindex_to_p2pk33bytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2PKH => Some(AddressBytes::from( + p2pkhaddressindex_to_p2pkhbytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2SH => Some(AddressBytes::from( + p2shaddressindex_to_p2shbytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2WPKH => Some(AddressBytes::from( + p2wpkhaddressindex_to_p2wpkhbytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2WSH => Some(AddressBytes::from( + p2wshaddressindex_to_p2wshbytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2TR => Some(AddressBytes::from( + p2traddressindex_to_p2trbytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2A => Some(AddressBytes::from( + p2aaddressindex_to_p2abytes_iter + .unwrap_get_inner(typeindex.into()), + )), + _ => None, + }; + + address + .and_then(|address| pools.find_from_address(&address.to_string())) + }) + .or_else(|| pools.find_from_coinbase_tag(&coinbase_tag)) + .unwrap_or(unknown); + + *res.entry(pool.name).or_default() += 1; }); let mut v = res.into_iter().map(|(k, v)| (v, k)).collect::>(); diff --git a/crates/brk_computer/src/chain.rs b/crates/brk_computer/src/chain.rs index 9db733e1e..9797f81b0 100644 --- a/crates/brk_computer/src/chain.rs +++ b/crates/brk_computer/src/chain.rs @@ -1,5 +1,6 @@ use std::path::Path; +use allocative::Allocative; use brk_error::Result; use brk_indexer::Indexer; use brk_structs::{ @@ -30,7 +31,7 @@ const TARGET_BLOCKS_PER_SEMESTER: u64 = 2 * TARGET_BLOCKS_PER_QUARTER; const TARGET_BLOCKS_PER_YEAR: u64 = 2 * TARGET_BLOCKS_PER_SEMESTER; const TARGET_BLOCKS_PER_DECADE: u64 = 10 * TARGET_BLOCKS_PER_YEAR; -#[derive(Clone)] +#[derive(Clone, Allocative)] pub struct Vecs { db: Database, @@ -51,6 +52,9 @@ pub struct Vecs { pub halvingepoch_to_timestamp: EagerVec, pub timeindexes_to_timestamp: ComputedVecsFromDateIndex, pub indexes_to_block_count: ComputedVecsFromHeight, + pub indexes_to_1w_block_count: ComputedVecsFromDateIndex, + pub indexes_to_1m_block_count: ComputedVecsFromDateIndex, + pub indexes_to_1y_block_count: ComputedVecsFromDateIndex, pub indexes_to_block_interval: ComputedVecsFromHeight, pub indexes_to_block_size: ComputedVecsFromHeight, pub indexes_to_block_vbytes: ComputedVecsFromHeight, @@ -389,6 +393,30 @@ impl Vecs { indexes, VecBuilderOptions::default().add_sum().add_cumulative(), )?, + indexes_to_1w_block_count: ComputedVecsFromDateIndex::forced_import( + &db, + "1w_block_count", + Source::Compute, + version + VERSION + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, + indexes_to_1m_block_count: ComputedVecsFromDateIndex::forced_import( + &db, + "1m_block_count", + Source::Compute, + version + VERSION + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, + indexes_to_1y_block_count: ComputedVecsFromDateIndex::forced_import( + &db, + "1y_block_count", + Source::Compute, + version + VERSION + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, indexes_to_block_weight: ComputedVecsFromHeight::forced_import( &db, "block_weight", @@ -937,6 +965,54 @@ impl Vecs { }, )?; + self.indexes_to_1w_block_count.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |v, _, _, starting_indexes, exit| { + v.compute_sum( + starting_indexes.dateindex, + self.indexes_to_block_count.dateindex.unwrap_sum(), + 7, + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_1m_block_count.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |v, _, _, starting_indexes, exit| { + v.compute_sum( + starting_indexes.dateindex, + self.indexes_to_block_count.dateindex.unwrap_sum(), + 30, + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_1y_block_count.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |v, _, _, starting_indexes, exit| { + v.compute_sum( + starting_indexes.dateindex, + self.indexes_to_block_count.dateindex.unwrap_sum(), + 365, + exit, + )?; + Ok(()) + }, + )?; + let mut height_to_timestamp_iter = indexer.vecs.height_to_timestamp.iter(); self.height_to_interval.compute_transform( starting_indexes.height, @@ -1769,6 +1845,9 @@ impl Vecs { self.indexes_to_hash_rate_1y_sma.vecs(), self.timeindexes_to_timestamp.vecs(), self.indexes_to_block_count.vecs(), + self.indexes_to_1w_block_count.vecs(), + self.indexes_to_1m_block_count.vecs(), + self.indexes_to_1y_block_count.vecs(), self.indexes_to_block_interval.vecs(), self.indexes_to_block_size.vecs(), self.indexes_to_block_vbytes.vecs(), diff --git a/crates/brk_computer/src/grouped/builder_eager.rs b/crates/brk_computer/src/grouped/builder_eager.rs index 05310e3c0..672b5cdce 100644 --- a/crates/brk_computer/src/grouped/builder_eager.rs +++ b/crates/brk_computer/src/grouped/builder_eager.rs @@ -1,3 +1,4 @@ +use allocative::Allocative; use brk_error::{Error, Result}; use brk_structs::{CheckedSub, StoredU64, Version}; use vecdb::{ @@ -9,7 +10,7 @@ use crate::utils::get_percentile; use super::ComputedType; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Allocative)] pub struct EagerVecBuilder where I: StoredIndex, diff --git a/crates/brk_computer/src/grouped/builder_lazy.rs b/crates/brk_computer/src/grouped/builder_lazy.rs index 3a6bca800..50fff16b3 100644 --- a/crates/brk_computer/src/grouped/builder_lazy.rs +++ b/crates/brk_computer/src/grouped/builder_lazy.rs @@ -1,3 +1,4 @@ +use allocative::Allocative; use brk_structs::Version; use vecdb::{ AnyBoxedIterableVec, AnyCloneableIterableVec, AnyCollectableVec, FromCoarserIndex, @@ -9,7 +10,7 @@ use crate::grouped::{EagerVecBuilder, VecBuilderOptions}; use super::ComputedType; #[allow(clippy::type_complexity)] -#[derive(Clone)] +#[derive(Clone, Allocative)] pub struct LazyVecBuilder where I: StoredIndex, diff --git a/crates/brk_computer/src/grouped/from_dateindex.rs b/crates/brk_computer/src/grouped/from_dateindex.rs index 562230ae2..8c07ae78d 100644 --- a/crates/brk_computer/src/grouped/from_dateindex.rs +++ b/crates/brk_computer/src/grouped/from_dateindex.rs @@ -1,3 +1,4 @@ +use allocative::Allocative; use brk_error::Result; use brk_indexer::Indexer; @@ -10,7 +11,7 @@ use crate::{Indexes, grouped::LazyVecBuilder, indexes}; use super::{ComputedType, EagerVecBuilder, Source, VecBuilderOptions}; -#[derive(Clone)] +#[derive(Clone, Allocative)] pub struct ComputedVecsFromDateIndex where T: ComputedType + PartialOrd, diff --git a/crates/brk_computer/src/grouped/from_height.rs b/crates/brk_computer/src/grouped/from_height.rs index 7b55656d2..fb66658f2 100644 --- a/crates/brk_computer/src/grouped/from_height.rs +++ b/crates/brk_computer/src/grouped/from_height.rs @@ -1,3 +1,4 @@ +use allocative::Allocative; use brk_error::Result; use brk_indexer::Indexer; @@ -15,7 +16,7 @@ use crate::{ use super::{ComputedType, EagerVecBuilder, VecBuilderOptions}; -#[derive(Clone)] +#[derive(Clone, Allocative)] pub struct ComputedVecsFromHeight where T: ComputedType + PartialOrd, diff --git a/crates/brk_computer/src/grouped/from_txindex.rs b/crates/brk_computer/src/grouped/from_txindex.rs index 3780036dd..c286d1f73 100644 --- a/crates/brk_computer/src/grouped/from_txindex.rs +++ b/crates/brk_computer/src/grouped/from_txindex.rs @@ -1,3 +1,4 @@ +use allocative::Allocative; use brk_error::Result; use brk_indexer::Indexer; use brk_structs::{ @@ -17,7 +18,7 @@ use crate::{ use super::{ComputedType, EagerVecBuilder, VecBuilderOptions}; -#[derive(Clone)] +#[derive(Clone, Allocative)] pub struct ComputedVecsFromTxindex where T: ComputedType + PartialOrd, diff --git a/crates/brk_computer/src/grouped/value_from_height.rs b/crates/brk_computer/src/grouped/value_from_height.rs index d38a2b88e..6ba42a744 100644 --- a/crates/brk_computer/src/grouped/value_from_height.rs +++ b/crates/brk_computer/src/grouped/value_from_height.rs @@ -1,3 +1,4 @@ +use allocative::Allocative; use brk_error::Result; use brk_indexer::Indexer; use brk_structs::{Bitcoin, Dollars, Height, Sats, Version}; @@ -12,7 +13,7 @@ use crate::{ use super::{ComputedVecsFromHeight, VecBuilderOptions}; -#[derive(Clone)] +#[derive(Clone, Allocative)] pub struct ComputedValueVecsFromHeight { pub sats: ComputedVecsFromHeight, pub bitcoin: ComputedVecsFromHeight, diff --git a/crates/brk_computer/src/grouped/value_from_txindex.rs b/crates/brk_computer/src/grouped/value_from_txindex.rs index ed3a7fa3a..fd7ab2cf4 100644 --- a/crates/brk_computer/src/grouped/value_from_txindex.rs +++ b/crates/brk_computer/src/grouped/value_from_txindex.rs @@ -1,3 +1,4 @@ +use allocative::Allocative; use brk_error::Result; use brk_indexer::Indexer; use brk_structs::{Bitcoin, Close, Dollars, Height, Sats, TxIndex, Version}; @@ -10,7 +11,7 @@ use crate::{Indexes, grouped::Source, indexes, price}; use super::{ComputedVecsFromTxindex, VecBuilderOptions}; -#[derive(Clone)] +#[derive(Clone, Allocative)] pub struct ComputedValueVecsFromTxindex { pub sats: ComputedVecsFromTxindex, pub bitcoin_txindex: LazyVecFrom1, diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index 966332a29..adbab160a 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -1,6 +1,6 @@ #![doc = include_str!("../README.md")] -use std::{path::Path, thread}; +use std::path::Path; use brk_error::Result; use brk_fetcher::Fetcher; @@ -34,6 +34,7 @@ pub struct Computer { pub indexes: indexes::Vecs, pub constants: constants::Vecs, pub market: market::Vecs, + pub pools: pools::Vecs, pub price: Option, pub chain: chain::Vecs, pub stateful: stateful::Vecs, @@ -41,7 +42,7 @@ pub struct Computer { pub cointime: cointime::Vecs, } -const VERSION: Version = Version::TWO; +const VERSION: Version = Version::new(4); impl Computer { /// Do NOT import multiple times or things will break !!! @@ -86,6 +87,12 @@ impl Computer { &indexes, price.as_ref(), )?, + pools: pools::Vecs::forced_import( + &computed_path, + VERSION + Version::ZERO, + &indexes, + price.as_ref(), + )?, cointime: cointime::Vecs::forced_import( &computed_path, VERSION + Version::ZERO, @@ -125,8 +132,8 @@ impl Computer { )?; } - thread::scope(|scope| -> Result<()> { - let chain = scope.spawn(|| { + std::thread::scope(|scope| -> Result<()> { + let chain = scope.spawn(|| -> Result<()> { info!("Computing chain..."); self.chain.compute( indexer, @@ -134,23 +141,33 @@ impl Computer { &starting_indexes, self.price.as_ref(), exit, - ) - }); - - let market = scope.spawn(|| -> Result<()> { - if let Some(price) = self.price.as_ref() { - info!("Computing market..."); - self.market - .compute(indexer, &self.indexes, price, &starting_indexes, exit)?; - } + )?; Ok(()) }); + if let Some(price) = self.price.as_ref() { + info!("Computing market..."); + self.market + .compute(indexer, &self.indexes, price, &starting_indexes, exit)?; + } + + // let _ = generate_allocation_files(&self.pools); + chain.join().unwrap()?; - market.join().unwrap()?; Ok(()) })?; + self.pools.compute( + indexer, + &self.indexes, + &starting_indexes, + &self.chain, + self.price.as_ref(), + exit, + )?; + + return Ok(()); + info!("Computing stateful..."); self.stateful.compute( indexer, @@ -183,6 +200,7 @@ impl Computer { self.chain.vecs(), self.stateful.vecs(), self.cointime.vecs(), + self.pools.vecs(), self.fetched.as_ref().map_or(vec![], |v| v.vecs()), self.price.as_ref().map_or(vec![], |v| v.vecs()), ] @@ -195,3 +213,36 @@ impl Computer { Box::leak(Box::new(self.clone())) } } + +// pub fn generate_allocation_files(monitored: &pools::Vecs) -> Result<()> { +// info!("Generating Allocative files..."); + +// let mut flamegraph = allocative::FlameGraphBuilder::default(); +// flamegraph.visit_root(monitored); +// let output = flamegraph.finish(); + +// let folder = format!( +// "at-{}", +// jiff::Timestamp::now().strftime("%Y-%m-%d_%Hh%Mm%Ss"), +// ); + +// let path = std::path::PathBuf::from(&format!("./target/flamegraph/{folder}")); +// std::fs::create_dir_all(&path)?; + +// // fs::write(path.join("flamegraph.src"), &output.flamegraph())?; + +// let mut fg_svg = Vec::new(); +// inferno::flamegraph::from_reader( +// &mut inferno::flamegraph::Options::default(), +// output.flamegraph().write().as_bytes(), +// &mut fg_svg, +// )?; + +// std::fs::write(path.join("flamegraph.svg"), &fg_svg)?; + +// std::fs::write(path.join("warnings.txt"), output.warnings())?; + +// info!("Successfully generated Allocative files"); + +// Ok(()) +// } diff --git a/crates/brk_computer/src/pools/id.rs b/crates/brk_computer/src/pools/id.rs index 064adb43b..5ac435933 100644 --- a/crates/brk_computer/src/pools/id.rs +++ b/crates/brk_computer/src/pools/id.rs @@ -1,101 +1,123 @@ +use allocative::Allocative; use num_enum::{FromPrimitive, IntoPrimitive}; use serde::{Deserialize, Serialize}; +use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; +// Created from the list in `pools.rs` +// Can be used as index for said list #[allow(clippy::upper_case_acronyms)] -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, FromPrimitive, IntoPrimitive)] -#[repr(u16)] +#[derive( + Debug, + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Serialize, + Deserialize, + FromPrimitive, + IntoPrimitive, + FromBytes, + IntoBytes, + Immutable, + KnownLayout, + Allocative, +)] +#[serde(rename_all = "lowercase")] +#[repr(u8)] pub enum PoolId { #[default] Unknown, BlockFills, - Ultimuspool, + UltimusPool, TerraPool, Luxor, - OneTHash, - BTCCom, + OneThash, + BtcCom, Bitfarms, HuobiPool, WayiCn, CanoePool, - BTCTop, + BtcTop, BitcoinCom, - OneSevenFiveBtc, - GBMiners, + Pool175btc, + GbMiners, AXbt, - ASICMiner, + AsicMiner, BitMinter, BitcoinRussia, - BTCServ, + BtcServ, SimplecoinUs, - BTCGuild, + BtcGuild, Eligius, OzCoin, - EclipseMC, - MaxBTC, + EclipseMc, + MaxBtc, TripleMining, CoinLab, - FiftyBTC, - GHashIO, - STMiningCorp, + Pool50btc, + GhashIo, + StMiningCorp, Bitparking, - MMPool, + Mmpool, Polmine, - KnCMiner, + KncMiner, Bitalo, F2Pool, - HHTT, + Hhtt, MegaBigPower, MtRed, - NMCbit, + NmcBit, YourbtcNet, GiveMeCoins, BraiinsPool, AntPool, MultiCoinCo, - BCPoolIo, + BcpoolIo, Cointerra, KanoPool, - SoloCK, - CKPool, + SoloCk, + CkPool, NiceHash, BitClub, BitcoinAffiliateNetwork, - BTCC, - BWPool, - EXXAndBW, + Btcc, + BwPool, + ExxBw, Bitsolo, BitFury, TwentyOneInc, - DigitalBTC, + DigitalBtc, EightBaochi, - MyBTCcoinPool, - TBDice, - HASHPOOL, + MyBtcCoinPool, + TbDice, + HashPool, Nexious, BravoMining, HotPool, - OKExPool, - BCMonster, + OkExPool, + BcMonster, OneHash, Bixin, - TATMASPool, - ViaBTC, - ConnectBTC, - BATPOOL, + TatmasPool, + ViaBtc, + ConnectBtc, + BatPool, Waterhole, - DCExploration, - DCEX, - BTPOOL, + DcExploration, + Dcex, + BtPool, FiftyEightCoin, - BitcoinIndiaLowercase, + BitcoinIndia, ShawnP0wers, - PHashIO, + PHashIo, RigPool, - HAOZHUZHU, + HaoZhuZhu, SevenPool, MiningKings, - HashBX, - DPOOL, + HashBx, + DPool, Rawpool, Haominer, Helix, @@ -114,62 +136,152 @@ pub enum PoolId { BinancePool, Minerium, LubianCom, - OKKONG, - AAOPool, - EMCDPool, - FoundryUSA, - SBICrypto, + Okkong, + AaoPool, + EmcdPool, + FoundryUsa, + SbiCrypto, ArkPool, - PureBTCCom, - MARAPool, + PureBtcCom, + MaraPool, KuCoinPool, EntrustCharityPool, - OKMINER, + OkMiner, Titan, - PEGAPool, - BTCNuggets, + PegaPool, + BtcNuggets, CloudHashing, DigitalXMintsy, Telco214, - BTCPoolParty, + BtcPoolParty, Multipool, TransactionCoinMining, - BTCDig, - TrickysBTCPool, - BTCMP, + BtcDig, + TrickysBtcPool, + BtcMp, Eobot, - UNOMP, + Unomp, Patels, GoGreenLight, - BitcoinIndiaCamel, // duplicate-ish entry preserved with slight name change - EkanemBTC, - CanoeUppercase, - TigerLowercase, - OneM1X, + EkanemBtc, + Canoe, + Tiger, + OneM1x, Zulupool, - SECPOOL, - OCEAN, + SecPool, + Ocean, WhitePool, - Wiz, - Mononaut, - Rijndael, Wk057, FutureBitApolloSolo, - Emzy, - Knorrium, CarbonNegative, - PortlandHODL, + PortlandHodl, Phoenix, Neopool, MaxiPool, - DrDetroit, BitFuFuPool, LuckyPool, MiningDutch, PublicPool, MiningSquared, InnopolisTech, - Nymkappa, - BTCLab, + BtcLab, Parasite, + Dummy158, + Dummy159, + Dummy160, + Dummy161, + Dummy162, + Dummy163, + Dummy164, + Dummy165, + Dummy166, + Dummy167, + Dummy168, + Dummy169, + Dummy170, + Dummy171, + Dummy172, + Dummy173, + Dummy174, + Dummy175, + Dummy176, + Dummy177, + Dummy178, + Dummy179, + Dummy180, + Dummy181, + Dummy182, + Dummy183, + Dummy184, + Dummy185, + Dummy186, + Dummy187, + Dummy188, + Dummy189, + Dummy190, + Dummy191, + Dummy192, + Dummy193, + Dummy194, + Dummy195, + Dummy196, + Dummy197, + Dummy198, + Dummy199, + Dummy200, + Dummy201, + Dummy202, + Dummy203, + Dummy204, + Dummy205, + Dummy206, + Dummy207, + Dummy208, + Dummy209, + Dummy210, + Dummy211, + Dummy212, + Dummy213, + Dummy214, + Dummy215, + Dummy216, + Dummy217, + Dummy218, + Dummy219, + Dummy220, + Dummy221, + Dummy222, + Dummy223, + Dummy224, + Dummy225, + Dummy226, + Dummy227, + Dummy228, + Dummy229, + Dummy230, + Dummy231, + Dummy232, + Dummy233, + Dummy234, + Dummy235, + Dummy236, + Dummy237, + Dummy238, + Dummy239, + Dummy240, + Dummy241, + Dummy242, + Dummy243, + Dummy244, + Dummy245, + Dummy246, + Dummy247, + Dummy248, + Dummy249, + Dummy250, + Dummy251, + Dummy252, + Dummy253, + Dummy254, + Dummy255, } diff --git a/crates/brk_computer/src/pools/mod.rs b/crates/brk_computer/src/pools/mod.rs index f08336125..33cbd029d 100644 --- a/crates/brk_computer/src/pools/mod.rs +++ b/crates/brk_computer/src/pools/mod.rs @@ -1,7 +1,240 @@ +use std::{collections::BTreeMap, path::Path}; + +use allocative::Allocative; +use brk_error::Result; +use brk_indexer::Indexer; +use brk_store::AnyStore; +use brk_structs::{AddressBytes, Height, OutputIndex, OutputType}; +use rayon::prelude::*; +use vecdb::{ + AnyCollectableVec, AnyIterableVec, AnyStoredVec, AnyVec, Database, Exit, GenericStoredVec, + PAGE_SIZE, RawVec, StoredIndex, VecIterator, Version, +}; + mod id; mod pool; +#[allow(clippy::module_inception)] mod pools; +mod vecs; pub use id::*; pub use pool::*; pub use pools::*; + +use crate::{ + chain, + indexes::{self, Indexes}, + price, +}; + +#[derive(Clone, Allocative)] +pub struct Vecs { + db: Database, + pools: &'static Pools, + height_to_pool: RawVec, + + vecs: BTreeMap, +} + +impl Vecs { + pub fn forced_import( + parent_path: &Path, + parent_version: Version, + indexes: &indexes::Vecs, + price: Option<&price::Vecs>, + ) -> Result { + let db = Database::open(&parent_path.join("pools"))?; + db.set_min_len(PAGE_SIZE * 1_000_000)?; + let pools = pools(); + + let version = parent_version + Version::new(3) + Version::new(pools.len() as u64); + + let this = Self { + height_to_pool: RawVec::forced_import(&db, "pool", version + Version::ZERO)?, + vecs: pools + .iter() + .map(|pool| { + vecs::Vecs::forced_import( + &db, + pool.id, + pools, + version + Version::ZERO, + indexes, + price, + ) + .map(|vecs| (pool.id, vecs)) + }) + .collect::>>()?, + pools, + db, + }; + + this.db.retain_regions( + this.vecs() + .into_iter() + .flat_map(|v| v.region_names()) + .collect(), + )?; + + Ok(this) + } + + pub fn compute( + &mut self, + indexer: &Indexer, + indexes: &indexes::Vecs, + starting_indexes: &Indexes, + chain: &chain::Vecs, + price: Option<&price::Vecs>, + exit: &Exit, + ) -> Result<()> { + self.compute_(indexer, indexes, starting_indexes, chain, price, exit)?; + self.db.flush_then_punch()?; + Ok(()) + } + + fn compute_( + &mut self, + indexer: &Indexer, + indexes: &indexes::Vecs, + starting_indexes: &Indexes, + chain: &chain::Vecs, + price: Option<&price::Vecs>, + exit: &Exit, + ) -> Result<()> { + self.compute_height_to_pool(indexer, indexes, starting_indexes, exit)?; + + self.vecs.par_iter_mut().try_for_each(|(_, vecs)| { + vecs.compute( + indexer, + indexes, + starting_indexes, + &self.height_to_pool, + chain, + price, + exit, + ) + })?; + + Ok(()) + } + + fn compute_height_to_pool( + &mut self, + indexer: &Indexer, + indexes: &indexes::Vecs, + starting_indexes: &Indexes, + exit: &Exit, + ) -> Result<()> { + self.height_to_pool.validate_computed_version_or_reset( + self.height_to_pool.version() + indexer.stores.height_to_coinbase_tag.version(), + )?; + + let mut height_to_first_txindex_iter = indexer.vecs.height_to_first_txindex.iter(); + let mut txindex_to_first_outputindex_iter = + indexer.vecs.txindex_to_first_outputindex.iter(); + let mut txindex_to_output_count_iter = indexes.txindex_to_output_count.iter(); + let mut outputindex_to_outputtype_iter = indexer.vecs.outputindex_to_outputtype.iter(); + let mut outputindex_to_typeindex_iter = indexer.vecs.outputindex_to_typeindex.iter(); + let mut p2pk65addressindex_to_p2pk65bytes_iter = + indexer.vecs.p2pk65addressindex_to_p2pk65bytes.iter(); + let mut p2pk33addressindex_to_p2pk33bytes_iter = + indexer.vecs.p2pk33addressindex_to_p2pk33bytes.iter(); + let mut p2pkhaddressindex_to_p2pkhbytes_iter = + indexer.vecs.p2pkhaddressindex_to_p2pkhbytes.iter(); + let mut p2shaddressindex_to_p2shbytes_iter = + indexer.vecs.p2shaddressindex_to_p2shbytes.iter(); + let mut p2wpkhaddressindex_to_p2wpkhbytes_iter = + indexer.vecs.p2wpkhaddressindex_to_p2wpkhbytes.iter(); + let mut p2wshaddressindex_to_p2wshbytes_iter = + indexer.vecs.p2wshaddressindex_to_p2wshbytes.iter(); + let mut p2traddressindex_to_p2trbytes_iter = + indexer.vecs.p2traddressindex_to_p2trbytes.iter(); + let mut p2aaddressindex_to_p2abytes_iter = indexer.vecs.p2aaddressindex_to_p2abytes.iter(); + + let unknown = self.pools.get_unknown(); + + let min = starting_indexes + .height + .unwrap_to_usize() + .min(self.height_to_pool.len()); + + indexer + .stores + .height_to_coinbase_tag + .iter() + .skip(min) + .try_for_each(|(height, coinbase_tag)| -> Result<()> { + let txindex = height_to_first_txindex_iter.unwrap_get_inner(height); + let outputindex = txindex_to_first_outputindex_iter.unwrap_get_inner(txindex); + let outputcount = txindex_to_output_count_iter.unwrap_get_inner(txindex); + + let pool = (*outputindex..(*outputindex + *outputcount)) + .map(OutputIndex::from) + .find_map(|outputindex| { + let outputtype = + outputindex_to_outputtype_iter.unwrap_get_inner(outputindex); + let typeindex = outputindex_to_typeindex_iter.unwrap_get_inner(outputindex); + + let address = match outputtype { + OutputType::P2PK65 => Some(AddressBytes::from( + p2pk65addressindex_to_p2pk65bytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2PK33 => Some(AddressBytes::from( + p2pk33addressindex_to_p2pk33bytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2PKH => Some(AddressBytes::from( + p2pkhaddressindex_to_p2pkhbytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2SH => Some(AddressBytes::from( + p2shaddressindex_to_p2shbytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2WPKH => Some(AddressBytes::from( + p2wpkhaddressindex_to_p2wpkhbytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2WSH => Some(AddressBytes::from( + p2wshaddressindex_to_p2wshbytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2TR => Some(AddressBytes::from( + p2traddressindex_to_p2trbytes_iter + .unwrap_get_inner(typeindex.into()), + )), + OutputType::P2A => Some(AddressBytes::from( + p2aaddressindex_to_p2abytes_iter.unwrap_get_inner(typeindex.into()), + )), + _ => None, + }; + + address + .and_then(|address| self.pools.find_from_address(&address.to_string())) + }) + .or_else(|| self.pools.find_from_coinbase_tag(&coinbase_tag)) + .unwrap_or(unknown); + + self.height_to_pool.push_if_needed(height, pool.id)?; + Ok(()) + })?; + + self.height_to_pool.safe_flush(exit)?; + Ok(()) + } + + pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> { + [ + self.vecs + .iter() + .flat_map(|(_, vecs)| vecs.vecs()) + .collect::>(), + vec![&self.height_to_pool], + ] + .into_iter() + .flatten() + .collect::>() + } +} diff --git a/crates/brk_computer/src/pools/pool.rs b/crates/brk_computer/src/pools/pool.rs index 2f2281cdc..6e39e1ed6 100644 --- a/crates/brk_computer/src/pools/pool.rs +++ b/crates/brk_computer/src/pools/pool.rs @@ -1,10 +1,44 @@ -use serde::{Deserialize, Serialize}; +use allocative::Allocative; use crate::pools::PoolId; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Allocative)] pub struct Pool { pub id: PoolId, + pub name: &'static str, + pub addresses: Box<[&'static str]>, + pub tags: Box<[&'static str]>, + pub tags_lowercase: Box<[String]>, + pub link: &'static str, +} + +impl Pool { + pub fn serialized_id(&self) -> String { + let value = serde_json::to_value(self.id).unwrap(); + value.as_str().unwrap().to_string() + } +} + +impl From<(usize, JSONPool)> for Pool { + fn from((index, pool): (usize, JSONPool)) -> Self { + Self { + id: (index as u8).into(), + name: pool.name, + addresses: pool.addresses, + tags_lowercase: pool + .tags + .iter() + .map(|t| t.to_lowercase()) + .collect::>() + .into_boxed_slice(), + tags: pool.tags, + link: pool.link, + } + } +} + +#[derive(Debug)] +pub struct JSONPool { pub name: &'static str, pub addresses: Box<[&'static str]>, pub tags: Box<[&'static str]>, diff --git a/crates/brk_computer/src/pools/pools.rs b/crates/brk_computer/src/pools/pools.rs index dd42d9b05..f548c0909 100644 --- a/crates/brk_computer/src/pools/pools.rs +++ b/crates/brk_computer/src/pools/pools.rs @@ -1,29 +1,54 @@ -use std::sync::OnceLock; +use std::{slice::Iter, sync::OnceLock}; + +use allocative::Allocative; + +use crate::{JSONPool, PoolId}; use super::super::Pool; -pub struct Pools([Pool; 166]); +const POOL_COUNT: usize = 158; + +#[derive(Debug, Allocative)] +pub struct Pools([Pool; POOL_COUNT]); impl Pools { pub fn find_from_coinbase_tag(&self, coinbase_tag: &str) -> Option<&Pool> { + let coinbase_tag = coinbase_tag.to_lowercase(); self.0.iter().find(|pool| { - pool.tags + pool.tags_lowercase .iter() .any(|pool_tag| coinbase_tag.contains(pool_tag)) }) } + pub fn find_from_address(&self, address: &str) -> Option<&Pool> { + self.0.iter().find(|pool| pool.addresses.contains(&address)) + } + pub fn get_unknown(&self) -> &Pool { &self.0[0] } + + pub fn get(&self, id: PoolId) -> &Pool { + let i: u8 = id.into(); + &self.0[i as usize] + } + + pub fn iter(&self) -> Iter<'_, Pool> { + self.0.iter() + } + + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + self.0.len() + } } pub fn pools() -> &'static Pools { static POOLS: OnceLock = OnceLock::new(); POOLS.get_or_init(|| { - Pools([ - Pool { - id: 0.into(), + Pools::from([ + JSONPool { name: "Unknown", addresses: Box::new([]), tags: Box::new([]), @@ -31,15 +56,13 @@ pub fn pools() -> &'static Pools { }, // Source: // https://github.com/mempool/mining-pools/blob/master/pools-v2.json - Pool { - id: 1.into(), + JSONPool { name: "BlockFills", addresses: Box::new(["1PzVut5X6Nx7Mv4JHHKPtVM9Jr9LJ4Rbry"]), tags: Box::new(["/BlockfillsPool/"]), link: "https://www.blockfills.com/mining", }, - Pool { - id: 2.into(), + JSONPool { name: "ULTIMUSPOOL", addresses: Box::new([ "1EMVSMe1VJUuqv7D7SFzctnVXk4KdjXATi", @@ -48,8 +71,7 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/ultimus/"]), link: "https://www.ultimuspool.com", }, - Pool { - id: 3.into(), + JSONPool { name: "Terra Pool", addresses: Box::new([ "32P5KVSbZYAkVmSHxDd2oBXaSk372rbV7L", @@ -59,8 +81,7 @@ pub fn pools() -> &'static Pools { tags: Box::new(["terrapool.io", "Validated with Clean Energy"]), link: "https://terrapool.io", }, - Pool { - id: 4.into(), + JSONPool { name: "Luxor", addresses: Box::new([ "1MkCDCzHpBsYQivp8MxjY5AkTGG1f2baoe", @@ -70,8 +91,7 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/LUXOR/", "Luxor Tech"]), link: "https://mining.luxor.tech", }, - Pool { - id: 5.into(), + JSONPool { name: "1THash", addresses: Box::new([ "147SwRQdpCfj5p8PnfsXV2SsVVpVcz3aPq", @@ -80,8 +100,7 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/1THash&58COIN/", "/1THash/"]), link: "https://www.1thash.top", }, - Pool { - id: 6.into(), + JSONPool { name: "BTC.com", addresses: Box::new([ "1Bf9sZvBHPFGVPX71WX2njhd1NXKv5y7v5", @@ -93,15 +112,13 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/BTC.COM/", "/BTC.com/", "btccom"]), link: "https://pool.btc.com", }, - Pool { - id: 7.into(), + JSONPool { name: "Bitfarms", addresses: Box::new(["3GvEGtnvgeBJ3p3EpdZhvUkxY4pDARkbjd"]), tags: Box::new(["BITFARMS"]), link: "https://www.bitfarms.io", }, - Pool { - id: 8.into(), + JSONPool { name: "Huobi.pool", addresses: Box::new([ "18Zcyxqna6h7Z7bRjhKvGpr8HSfieQWXqj", @@ -112,71 +129,61 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/HuoBi/", "/Huobi/"]), link: "https://www.hpt.com", }, - Pool { - id: 9.into(), + JSONPool { name: "WAYI.CN", addresses: Box::new([]), tags: Box::new(["/E2M & BTC.TOP/"]), link: "https://www.easy2mine.com", }, - Pool { - id: 10.into(), + JSONPool { name: "CanoePool", addresses: Box::new(["1GP8eWArgpwRum76saJS4cZKCHWJHs9PQo"]), tags: Box::new(["/CANOE/", "/canoepool/"]), link: "https://btc.canoepool.com", }, - Pool { - id: 11.into(), + JSONPool { name: "BTC.TOP", addresses: Box::new(["1Hz96kJKF2HLPGY15JWLB5m9qGNxvt8tHJ"]), tags: Box::new(["/BTC.TOP/"]), link: "https://btc.top", }, - Pool { - id: 12.into(), + JSONPool { name: "Bitcoin.com", addresses: Box::new([]), tags: Box::new(["pool.bitcoin.com"]), link: "https://www.bitcoin.com", }, - Pool { - id: 13.into(), + JSONPool { name: "175btc", addresses: Box::new([]), tags: Box::new(["Mined By 175btc.com"]), link: "https://www.175btc.com", }, - Pool { - id: 14.into(), + JSONPool { name: "GBMiners", addresses: Box::new([]), tags: Box::new(["/mined by gbminers/"]), link: "https://gbminers.com", }, - Pool { - id: 15.into(), + JSONPool { name: "A-XBT", addresses: Box::new(["1MFsp2txCPwMMBJjNNeKaduGGs8Wi1Ce7X"]), tags: Box::new(["/A-XBT/"]), link: "https://www.a-xbt.com", }, - Pool { - id: 16.into(), + JSONPool { name: "ASICMiner", addresses: Box::new([]), tags: Box::new(["ASICMiner"]), link: "https://www.asicminer.co", }, - Pool { - id: 17.into(), + JSONPool { name: "BitMinter", addresses: Box::new(["19PkHafEN18mquJ9ChwZt5YEFoCdPP5vYB"]), tags: Box::new(["BitMinter"]), link: "https://bitminter.com", }, - Pool { - id: 18.into(), + JSONPool { name: "BitcoinRussia", addresses: Box::new([ "14R2r9FkyDmyxGB9xUVwVLdgsX9YfdVamk", @@ -185,43 +192,37 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/Bitcoin-Russia.ru/"]), link: "https://bitcoin-russia.ru", }, - Pool { - id: 19.into(), + JSONPool { name: "BTCServ", addresses: Box::new([]), tags: Box::new(["btcserv"]), link: "https://btcserv.net", }, - Pool { - id: 20.into(), + JSONPool { name: "simplecoin.us", addresses: Box::new([]), tags: Box::new(["simplecoin"]), link: "https://simplecoin.us", }, - Pool { - id: 21.into(), + JSONPool { name: "BTC Guild", addresses: Box::new([]), tags: Box::new(["BTC Guild"]), link: "https://www.btcguild.com", }, - Pool { - id: 22.into(), + JSONPool { name: "Eligius", addresses: Box::new([]), tags: Box::new(["Eligius"]), link: "https://eligius.st", }, - Pool { - id: 23.into(), + JSONPool { name: "OzCoin", addresses: Box::new([]), tags: Box::new(["ozco.in", "ozcoin"]), link: "https://ozcoin.net", }, - Pool { - id: 24.into(), + JSONPool { name: "EclipseMC", addresses: Box::new([ "15xiShqUqerfjFdyfgBH1K7Gwp6cbYmsTW", @@ -230,64 +231,55 @@ pub fn pools() -> &'static Pools { tags: Box::new(["EMC ", "EMC:"]), link: "https://eclipsemc.com", }, - Pool { - id: 25.into(), + JSONPool { name: "MaxBTC", addresses: Box::new([]), tags: Box::new(["MaxBTC"]), link: "https://maxbtc.com", }, - Pool { - id: 26.into(), + JSONPool { name: "TripleMining", addresses: Box::new([]), tags: Box::new(["Triplemining.com", "triplemining"]), link: "https://www.triplemining.com", }, - Pool { - id: 27.into(), + JSONPool { name: "CoinLab", addresses: Box::new([]), tags: Box::new(["CoinLab"]), link: "https://coinlab.com", }, - Pool { - id: 28.into(), + JSONPool { name: "50BTC", addresses: Box::new([]), tags: Box::new(["50BTC"]), link: "https://www.50btc.com", }, - Pool { - id: 29.into(), + JSONPool { name: "GHash.IO", addresses: Box::new(["1CjPR7Z5ZSyWk6WtXvSFgkptmpoi4UM9BC"]), tags: Box::new(["ghash.io"]), link: "https://ghash.io", }, - Pool { - id: 30.into(), + JSONPool { name: "ST Mining Corp", addresses: Box::new([]), tags: Box::new(["st mining corp"]), link: "https://bitcointalk.org/index.php?topic=77000.msg3207708#msg3207708", }, - Pool { - id: 31.into(), + JSONPool { name: "Bitparking", addresses: Box::new([]), tags: Box::new(["bitparking"]), link: "https://mmpool.bitparking.com", }, - Pool { - id: 32.into(), + JSONPool { name: "mmpool", addresses: Box::new([]), tags: Box::new(["mmpool"]), link: "https://mmpool.org/pool", }, - Pool { - id: 33.into(), + JSONPool { name: "Polmine", addresses: Box::new([ "13vWXwzNF5Ef9SUXNTdr7de7MqiV4G1gnL", @@ -300,22 +292,19 @@ pub fn pools() -> &'static Pools { tags: Box::new(["by polmine.pl", "bypmneU"]), link: "https://polmine.pl", }, - Pool { - id: 34.into(), + JSONPool { name: "KnCMiner", addresses: Box::new([]), tags: Box::new(["KnCMiner"]), link: "https://portal.kncminer.com/pool", }, - Pool { - id: 35.into(), + JSONPool { name: "Bitalo", addresses: Box::new(["1HTejfsPZQGi3afCMEZTn2xdmoNzp13n3F"]), tags: Box::new(["Bitalo"]), link: "https://bitalo.com/mining", }, - Pool { - id: 36.into(), + JSONPool { name: "F2Pool", addresses: Box::new([ "1KFHE7w8BhaENAswwryaoccDb6qcT6DbYY", @@ -324,50 +313,43 @@ pub fn pools() -> &'static Pools { tags: Box::new(["七彩神仙鱼", "F2Pool", "🐟"]), link: "https://www.f2pool.com", }, - Pool { - id: 37.into(), + JSONPool { name: "HHTT", addresses: Box::new([]), tags: Box::new(["HHTT"]), link: "https://hhtt.1209k.com", }, - Pool { - id: 38.into(), + JSONPool { name: "MegaBigPower", addresses: Box::new(["1K7znxRfkS8R1hcmyMvHDum1hAQreS4VQ4"]), tags: Box::new(["megabigpower.com"]), link: "https://megabigpower.com", }, - Pool { - id: 39.into(), + JSONPool { name: "Mt Red", addresses: Box::new([]), tags: Box::new(["/mtred/"]), link: "https://mtred.com", }, - Pool { - id: 40.into(), + JSONPool { name: "NMCbit", addresses: Box::new([]), tags: Box::new(["nmcbit.com"]), link: "https://nmcbit.com", }, - Pool { - id: 41.into(), + JSONPool { name: "Yourbtc.net", addresses: Box::new([]), tags: Box::new(["yourbtc.net"]), link: "https://yourbtc.net", }, - Pool { - id: 42.into(), + JSONPool { name: "Give Me Coins", addresses: Box::new([]), tags: Box::new(["Give-Me-Coins"]), link: "https://give-me-coins.com", }, - Pool { - id: 43.into(), + JSONPool { name: "Braiins Pool", addresses: Box::new([ "1AqTMY7kmHZxBuLUR5wJjPFUvqGs23sesr", @@ -376,8 +358,7 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/slush/"]), link: "https://braiins.com/pool", }, - Pool { - id: 44.into(), + JSONPool { name: "AntPool", addresses: Box::new([ "12dRugNcdxK39288NjcDV4GX7rMsKCGn6B", @@ -421,99 +402,85 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/AntPool/", "Mined By AntPool", "Mined by AntPool"]), link: "https://www.antpool.com", }, - Pool { - id: 45.into(), + JSONPool { name: "MultiCoin.co", addresses: Box::new([]), tags: Box::new(["Mined by MultiCoin.co"]), link: "https://multicoin.co", }, - Pool { - id: 46.into(), + JSONPool { name: "bcpool.io", addresses: Box::new([]), tags: Box::new(["bcpool.io"]), link: "https://bcpool.io", }, - Pool { - id: 47.into(), + JSONPool { name: "Cointerra", addresses: Box::new(["1BX5YoLwvqzvVwSrdD4dC32vbouHQn2tuF"]), tags: Box::new(["cointerra"]), link: "https://cointerra.com", }, - Pool { - id: 48.into(), + JSONPool { name: "KanoPool", addresses: Box::new([]), tags: Box::new(["Kano"]), link: "https://kano.is", }, - Pool { - id: 49.into(), + JSONPool { name: "Solo CK", addresses: Box::new([]), tags: Box::new(["/solo.ckpool.org/"]), link: "https://solo.ckpool.org", }, - Pool { - id: 50.into(), + JSONPool { name: "CKPool", addresses: Box::new([]), tags: Box::new(["/ckpool.org/"]), link: "https://ckpool.org", }, - Pool { - id: 51.into(), + JSONPool { name: "NiceHash", addresses: Box::new([]), tags: Box::new(["/NiceHashSolo", "/NiceHash/"]), link: "https://www.nicehash.com", }, - Pool { - id: 52.into(), + JSONPool { name: "BitClub", addresses: Box::new(["155fzsEBHy9Ri2bMQ8uuuR3tv1YzcDywd4"]), tags: Box::new(["/BitClub Network/"]), link: "https://bitclubpool.com", }, - Pool { - id: 53.into(), + JSONPool { name: "Bitcoin Affiliate Network", addresses: Box::new([]), tags: Box::new(["bitcoinaffiliatenetwork.com"]), link: "https://mining.bitcoinaffiliatenetwork.com", }, - Pool { - id: 54.into(), + JSONPool { name: "BTCC", addresses: Box::new(["152f1muMCNa7goXYhYAQC61hxEgGacmncB"]), tags: Box::new(["/BTCC/", "BTCChina Pool", "BTCChina.com", "btcchina.com"]), link: "https://pool.btcc.com", }, - Pool { - id: 55.into(), + JSONPool { name: "BWPool", addresses: Box::new(["1JLRXD8rjRgQtTS9MvfQALfHgGWau9L9ky"]), tags: Box::new(["BW Pool", "BWPool"]), link: "https://bwpool.net", }, - Pool { - id: 56.into(), + JSONPool { name: "EXX&BW", addresses: Box::new([]), tags: Box::new(["xbtc.exx.com&bw.com"]), link: "https://xbtc.exx.com", }, - Pool { - id: 57.into(), + JSONPool { name: "Bitsolo", addresses: Box::new(["18zRehBcA2YkYvsC7dfQiFJNyjmWvXsvon"]), tags: Box::new(["Bitsolo Pool"]), link: "https://bitsolo.net", }, - Pool { - id: 58.into(), + JSONPool { name: "BitFury", addresses: Box::new([ "14yfxkcpHnju97pecpM7fjuTkVdtbkcfE6", @@ -522,8 +489,7 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/BitFury/", "/Bitfury/"]), link: "https://bitfury.com", }, - Pool { - id: 59.into(), + JSONPool { name: "21 Inc.", addresses: Box::new([ "15rQXUSBQRubShPpiJfDLxmwS8ze2RUm4z", @@ -533,85 +499,73 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/pool34/"]), link: "https://21.co", }, - Pool { - id: 60.into(), + JSONPool { name: "digitalBTC", addresses: Box::new(["1MimPd6LrPKGftPRHWdfk8S3KYBfN4ELnD"]), tags: Box::new(["/agentD/"]), link: "https://digitalbtc.com", }, - Pool { - id: 61.into(), + JSONPool { name: "8baochi", addresses: Box::new(["1Hk9gD8xMo2XBUhE73y5zXEM8xqgffTB5f"]), tags: Box::new(["/八宝池 8baochi.com/"]), link: "https://8baochi.com", }, - Pool { - id: 62.into(), + JSONPool { name: "myBTCcoin Pool", addresses: Box::new(["151T7r1MhizzJV6dskzzUkUdr7V8JxV2Dx"]), tags: Box::new(["myBTCcoin Pool"]), link: "https://mybtccoin.com", }, - Pool { - id: 63.into(), + JSONPool { name: "TBDice", addresses: Box::new(["1BUiW44WuJ2jiJgXiyxJVFMN8bc1GLdXRk"]), tags: Box::new(["TBDice"]), link: "https://tbdice.org", }, - Pool { - id: 64.into(), + JSONPool { name: "HASHPOOL", addresses: Box::new([]), tags: Box::new(["HASHPOOL"]), link: "https://hashpool.com", }, - Pool { - id: 65.into(), + JSONPool { name: "Nexious", addresses: Box::new(["1GBo1f2tzVx5jScV9kJXPUP9RjvYXuNzV7"]), tags: Box::new(["/Nexious/"]), link: "https://nexious.com", }, - Pool { - id: 66.into(), + JSONPool { name: "Bravo Mining", addresses: Box::new([]), tags: Box::new(["/bravo-mining/"]), link: "https://www.bravo-mining.com", }, - Pool { - id: 67.into(), + JSONPool { name: "HotPool", addresses: Box::new(["17judvK4AC2M6KhaBbAEGw8CTKc9Pg8wup"]), tags: Box::new(["/HotPool/"]), link: "https://hotpool.co", }, - Pool { - id: 68.into(), + JSONPool { name: "OKExPool", addresses: Box::new([]), tags: Box::new(["/www.okex.com/"]), link: "https://www.okex.com", }, - Pool { - id: 69.into(), + JSONPool { name: "BCMonster", addresses: Box::new(["1E18BNyobcoiejcDYAz5SjbrzifNDEpM88"]), tags: Box::new(["/BCMonster/"]), link: "https://www.bcmonster.com", }, - Pool { - id: 70.into(), + JSONPool { name: "1Hash", addresses: Box::new(["1F1xcRt8H8Wa623KqmkEontwAAVqDSAWCV"]), tags: Box::new(["Mined by 1hash.com"]), link: "https://www.1hash.com", }, - Pool { - id: 71.into(), + JSONPool { name: "Bixin", addresses: Box::new([ "13hQVEstgo4iPQZv9C7VELnLWF7UWtF4Q3", @@ -620,78 +574,67 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/Bixin/", "/HaoBTC/", "HAOBTC"]), link: "https://haopool.com", }, - Pool { - id: 72.into(), + JSONPool { name: "TATMAS Pool", addresses: Box::new([]), tags: Box::new(["/ViaBTC/TATMAS Pool/"]), link: "https://tmsminer.com", }, - Pool { - id: 73.into(), + JSONPool { name: "ViaBTC", addresses: Box::new([]), tags: Box::new(["/ViaBTC/", "viabtc.com deploy"]), link: "https://viabtc.com", }, - Pool { - id: 74.into(), + JSONPool { name: "ConnectBTC", addresses: Box::new(["1KPQkehgYAqwiC6UCcbojM3mbGjURrQJF2"]), tags: Box::new(["/ConnectBTC - Home for Miners/"]), link: "https://www.connectbtc.com", }, - Pool { - id: 75.into(), + JSONPool { name: "BATPOOL", addresses: Box::new(["167ApWWxUSFQmz2jdz9xop3oAKdLejvMML"]), tags: Box::new(["/BATPOOL/"]), link: "https://www.batpool.com", }, - Pool { - id: 76.into(), + JSONPool { name: "Waterhole", addresses: Box::new(["1FLH1SoLv4U68yUERhDiWzrJn5TggMqkaZ"]), tags: Box::new(["/WATERHOLE.IO/"]), link: "https://btc.waterhole.io", }, - Pool { - id: 77.into(), + JSONPool { name: "DCExploration", addresses: Box::new([]), tags: Box::new(["/DCExploration/"]), link: "https://dcexploration.cn", }, - Pool { - id: 78.into(), + JSONPool { name: "DCEX", addresses: Box::new([]), tags: Box::new(["/DCEX/"]), link: "https://dcexploration.cn", }, - Pool { - id: 79.into(), + JSONPool { name: "BTPOOL", addresses: Box::new([]), tags: Box::new(["/BTPOOL/"]), link: "", }, - Pool { - id: 80.into(), + JSONPool { name: "58COIN", addresses: Box::new(["199EDJoCpqV672qESEkfFgEqNT1iR2gj3t"]), tags: Box::new(["/58coin.com/"]), link: "https://www.58coin.com", }, - Pool { - id: 81.into(), + JSONPool { name: "Bitcoin India", - addresses: Box::new([]), + addresses: Box::new(["1AZ6BkCo4zgTuuLpRStJH8iNsehXTMp456"]), tags: Box::new(["/Bitcoin-India/"]), link: "https://bitcoin-india.org", }, - Pool { - id: 82.into(), + JSONPool { name: "shawnp0wers", addresses: Box::new([ "12znnESiJ3bgCLftwwrg9wzQKN8fJtoBDa", @@ -700,36 +643,31 @@ pub fn pools() -> &'static Pools { tags: Box::new(["--Nug--"]), link: "https://www.brainofshawn.com", }, - Pool { - id: 83.into(), + JSONPool { name: "PHash.IO", addresses: Box::new([]), tags: Box::new(["/phash.cn/", "/phash.io/"]), link: "https://phash.io", }, - Pool { - id: 84.into(), + JSONPool { name: "RigPool", addresses: Box::new(["1JpKmtspBJQVXK67DJP64eBJcAPhDvJ9Er"]), tags: Box::new(["/RigPool.com/"]), link: "https://www.rigpool.com", }, - Pool { - id: 85.into(), + JSONPool { name: "HAOZHUZHU", addresses: Box::new(["19qa95rTbDziNCS9EexUbh2hVY4viUU9tt"]), tags: Box::new(["/haozhuzhu/"]), link: "https://haozhuzhu.com", }, - Pool { - id: 86.into(), + JSONPool { name: "7pool", addresses: Box::new(["1JLc3JxvpdL1g5zoX8sKLP4BkJQiwnJftU"]), tags: Box::new(["/$Mined by 7pool.com/"]), link: "https://7pool.com", }, - Pool { - id: 87.into(), + JSONPool { name: "MiningKings", addresses: Box::new([ "1ApE99VM5RJzMRRtwd2JMgmkGabtJqoMEz", @@ -739,22 +677,19 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/mined by poopbut/"]), link: "https://miningkings.com", }, - Pool { - id: 88.into(), + JSONPool { name: "HashBX", addresses: Box::new([]), tags: Box::new(["/Mined by HashBX.io/"]), link: "https://hashbx.io", }, - Pool { - id: 89.into(), + JSONPool { name: "DPOOL", addresses: Box::new(["1ACAgPuFFidYzPMXbiKptSrwT74Dg8hq2v"]), tags: Box::new(["/DPOOL.TOP/"]), link: "https://www.dpool.top", }, - Pool { - id: 90.into(), + JSONPool { name: "Rawpool", addresses: Box::new([ "1FbBbv5oYqFKwiPm4CAqvAy8345n8AQ74b", @@ -769,29 +704,25 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/Rawpool.com/"]), link: "https://www.rawpool.com", }, - Pool { - id: 91.into(), + JSONPool { name: "haominer", addresses: Box::new([]), tags: Box::new(["/haominer/"]), link: "https://haominer.com", }, - Pool { - id: 92.into(), + JSONPool { name: "Helix", addresses: Box::new([]), tags: Box::new(["/Helix/"]), link: "", }, - Pool { - id: 93.into(), + JSONPool { name: "Bitcoin-Ukraine", addresses: Box::new([]), tags: Box::new(["/Bitcoin-Ukraine.com.ua/"]), link: "https://bitcoin-ukraine.com.ua", }, - Pool { - id: 94.into(), + JSONPool { name: "Poolin", addresses: Box::new([ "14sA8jqYQgMRQV9zUtGFvpeMEw7YDn77SK", @@ -805,57 +736,49 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/poolin.com", "/poolin/"]), link: "https://www.poolin.com", }, - Pool { - id: 95.into(), + JSONPool { name: "SecretSuperstar", addresses: Box::new([]), tags: Box::new(["/SecretSuperstar/"]), link: "", }, - Pool { - id: 96.into(), + JSONPool { name: "tigerpool.net", addresses: Box::new([]), tags: Box::new(["/tigerpool.net"]), link: "", }, - Pool { - id: 97.into(), + JSONPool { name: "Sigmapool.com", addresses: Box::new(["12cKiMNhCtBhZRUBCnYXo8A4WQzMUtYjmR"]), tags: Box::new(["/Sigmapool.com/"]), link: "https://sigmapool.com", }, - Pool { - id: 98.into(), + JSONPool { name: "okpool.top", addresses: Box::new([]), tags: Box::new(["/www.okpool.top/"]), link: "https://www.okpool.top", }, - Pool { - id: 99.into(), + JSONPool { name: "Hummerpool", addresses: Box::new([]), tags: Box::new(["HummerPool", "Hummerpool"]), link: "https://www.hummerpool.com", }, - Pool { - id: 100.into(), + JSONPool { name: "Tangpool", addresses: Box::new(["12Taz8FFXQ3E2AGn3ZW1SZM5bLnYGX4xR6"]), tags: Box::new(["/Tangpool/"]), link: "https://www.tangpool.com", }, - Pool { - id: 101.into(), + JSONPool { name: "BytePool", addresses: Box::new(["39m5Wvn9ZqyhYmCYpsyHuGMt5YYw4Vmh1Z"]), tags: Box::new(["/bytepool.com/"]), link: "https://www.bytepool.com", }, - Pool { - id: 102.into(), + JSONPool { name: "SpiderPool", addresses: Box::new([ "125m2H43pwKpSZjLhMQHneuTwTJN5qRyYu", @@ -865,22 +788,19 @@ pub fn pools() -> &'static Pools { tags: Box::new(["SpiderPool"]), link: "https://www.spiderpool.com", }, - Pool { - id: 103.into(), + JSONPool { name: "NovaBlock", addresses: Box::new(["3Bmb9Jig8A5kHdDSxvDZ6eryj3AXd3swuJ"]), tags: Box::new(["/NovaBlock/"]), link: "https://novablock.com", }, - Pool { - id: 104.into(), + JSONPool { name: "MiningCity", addresses: Box::new(["11wC5KcbgrWRBb43cwADdVrxgyF8mndVC"]), tags: Box::new(["MiningCity"]), link: "https://www.miningcity.com", }, - Pool { - id: 105.into(), + JSONPool { name: "Binance Pool", addresses: Box::new([ "122pN8zvqTxJaA8fRY1PDBu4QYodqE5m2X", @@ -894,43 +814,37 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/Binance/", "binance"]), link: "https://pool.binance.com", }, - Pool { - id: 106.into(), + JSONPool { name: "Minerium", addresses: Box::new([]), tags: Box::new(["/Mined in the USA by: /Minerium.com/", "/Minerium.com/"]), link: "https://www.minerium.com", }, - Pool { - id: 107.into(), + JSONPool { name: "Lubian.com", addresses: Box::new(["34Jpa4Eu3ApoPVUKNTN2WeuXVVq1jzxgPi"]), tags: Box::new(["/Buffett/", "/lubian.com/"]), link: "https://www.lubian.com", }, - Pool { - id: 108.into(), + JSONPool { name: "OKKONG", addresses: Box::new(["16JHXJ7M2MubWNX9grnqbjUqJ5PHwcCWw2"]), tags: Box::new(["/hash.okkong.com/"]), link: "https://hash.okkong.com", }, - Pool { - id: 109.into(), + JSONPool { name: "AAO Pool", addresses: Box::new(["12QVFmJH2b4455YUHkMpEnWLeRY3eJ4Jb5"]), tags: Box::new(["/AAOPOOL/"]), link: "https://btc.tmspool.top", }, - Pool { - id: 110.into(), + JSONPool { name: "EMCDPool", addresses: Box::new(["1BDbsWi3Mrcjp1wdop3PWFNCNZtu4R7Hjy"]), tags: Box::new(["/EMCD/", "/one_more_mcd/", "get___emcd", "emcd"]), link: "https://pool.emcd.io", }, - Pool { - id: 111.into(), + JSONPool { name: "Foundry USA", addresses: Box::new([ "12KKDt4Mj7N5UAkQMN7LtPZMayenXHa8KL", @@ -941,29 +855,25 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/2cDw/", "Foundry USA Pool"]), link: "https://foundrydigital.com", }, - Pool { - id: 112.into(), + JSONPool { name: "SBI Crypto", addresses: Box::new([]), tags: Box::new(["/SBICrypto.com Pool/", "SBI Crypto", "SBICrypto"]), link: "https://sbicrypto.com", }, - Pool { - id: 113.into(), + JSONPool { name: "ArkPool", addresses: Box::new(["1QEiAhdHdMhBgVbDM7zUXWGkNhgEEJ6uLd"]), tags: Box::new(["/ArkPool/"]), link: "https://www.arkpool.com", }, - Pool { - id: 114.into(), + JSONPool { name: "PureBTC.COM", addresses: Box::new([]), tags: Box::new(["/PureBTC.COM/"]), link: "https://purebtc.com", }, - Pool { - id: 115.into(), + JSONPool { name: "MARA Pool", addresses: Box::new([ "15MdAHnkxt9TMC2Rj595hsg8Hnv693pPBB", @@ -972,64 +882,55 @@ pub fn pools() -> &'static Pools { tags: Box::new(["MARA Pool", "MARA Made in USA"]), link: "https://marapool.com", }, - Pool { - id: 116.into(), + JSONPool { name: "KuCoinPool", addresses: Box::new(["1ArTPjj6pV3aNRhLPjJVPYoxB98VLBzUmb"]), tags: Box::new(["KuCoinPool"]), link: "https://www.kucoin.com/mining-pool", }, - Pool { - id: 117.into(), + JSONPool { name: "Entrust Charity Pool", addresses: Box::new([]), tags: Box::new(["Entrustus"]), link: "pool.entustus.org", }, - Pool { - id: 118.into(), + JSONPool { name: "OKMINER", addresses: Box::new(["15xcAZ2HfaSwYbCV6GGbasBSAekBRRC5Q2"]), tags: Box::new(["okminer.com/euz"]), link: "https://okminer.com", }, - Pool { - id: 119.into(), + JSONPool { name: "Titan", addresses: Box::new(["14hLEtxozmmih6Gg5xrGZLfx51bEMj21NW"]), tags: Box::new(["Titan.io"]), link: "https://titan.io", }, - Pool { - id: 120.into(), + JSONPool { name: "PEGA Pool", addresses: Box::new(["1BGFwRzjCfRR7EvRHnzfHyFjGR8XiBDFKa"]), tags: Box::new(["/pegapool/"]), link: "https://www.pega-pool.com", }, - Pool { - id: 121.into(), + JSONPool { name: "BTC Nuggets", addresses: Box::new(["1BwZeHJo7b7M2op7VDfYnsmcpXsUYEcVHm"]), tags: Box::new([]), link: "https://104.197.8.250", }, - Pool { - id: 122.into(), + JSONPool { name: "CloudHashing", addresses: Box::new(["1ALA5v7h49QT7WYLcRsxcXqXUqEqaWmkvw"]), tags: Box::new([]), link: "https://cloudhashing.com", }, - Pool { - id: 123.into(), + JSONPool { name: "digitalX Mintsy", addresses: Box::new(["1NY15MK947MLzmPUa2gL7UgyR8prLh2xfu"]), tags: Box::new([]), link: "https://www.mintsy.co", }, - Pool { - id: 124.into(), + JSONPool { name: "Telco 214", addresses: Box::new([ "13Sd8Y7nUao3z4bJFkZvCRXpFqHvLy49YY", @@ -1048,50 +949,43 @@ pub fn pools() -> &'static Pools { tags: Box::new([]), link: "https://www.telco214.com", }, - Pool { - id: 125.into(), + JSONPool { name: "BTC Pool Party", addresses: Box::new(["1PmRrdp1YSkp1LxPyCfcmBHDEipG5X4eJB"]), tags: Box::new([]), link: "https://btcpoolparty.com", }, - Pool { - id: 126.into(), + JSONPool { name: "Multipool", addresses: Box::new(["1MeffGLauEj2CZ18hRQqUauTXb9JAuLbGw"]), tags: Box::new([]), link: "https://www.multipool.us", }, - Pool { - id: 127.into(), + JSONPool { name: "transactioncoinmining", addresses: Box::new(["1qtKetXKgqa7j1KrB19HbvfRiNUncmakk"]), tags: Box::new([]), link: "https://sha256.transactioncoinmining.com", }, - Pool { - id: 128.into(), + JSONPool { name: "BTCDig", addresses: Box::new(["15MxzsutVroEE9XiDckLxUHTCDAEZgPZJi"]), tags: Box::new([]), link: "https://btcdig.com", }, - Pool { - id: 129.into(), + JSONPool { name: "Tricky's BTC Pool", addresses: Box::new(["1AePMyovoijxvHuKhTqWvpaAkRCF4QswC6"]), tags: Box::new([]), link: "https://pool.wemine.uk", }, - Pool { - id: 130.into(), + JSONPool { name: "BTCMP", addresses: Box::new(["1jKSjMLnDNup6NPgCjveeP9tUn4YpT94Y"]), tags: Box::new([]), link: "https://www.btcmp.com", }, - Pool { - id: 131.into(), + JSONPool { name: "Eobot", addresses: Box::new([ "16GsNC3q6KgVXkUX7j7aPxSUdHrt1sN2yN", @@ -1100,15 +994,13 @@ pub fn pools() -> &'static Pools { tags: Box::new([]), link: "https://eobot.com", }, - Pool { - id: 132.into(), + JSONPool { name: "UNOMP", addresses: Box::new(["1BRY8AD7vSNUEE75NjzfgiG18mWjGQSRuJ"]), tags: Box::new([]), link: "https://199.115.116.7:8925", }, - Pool { - id: 133.into(), + JSONPool { name: "Patels", addresses: Box::new([ "197miJmttpCt2ubVs6DDtGBYFDroxHmvVB", @@ -1117,127 +1009,73 @@ pub fn pools() -> &'static Pools { tags: Box::new([]), link: "https://patelsminingpool.com", }, - Pool { - id: 134.into(), + JSONPool { name: "GoGreenLight", addresses: Box::new(["18EPLvrs2UE11kWBB3ABS7Crwj5tTBYPoa"]), tags: Box::new([]), link: "https://www.gogreenlight.se", }, - Pool { - id: 135.into(), - name: "BitcoinIndia", - addresses: Box::new(["1AZ6BkCo4zgTuuLpRStJH8iNsehXTMp456"]), - tags: Box::new([]), - link: "https://pool.bitcoin-india.org", - }, - Pool { - id: 136.into(), + JSONPool { name: "EkanemBTC", addresses: Box::new(["1Cs5RT9SRk1hxsdzivAfkjesNmVVJqfqkw"]), tags: Box::new([]), link: "https://ekanembtc.com", }, - Pool { - id: 137.into(), + JSONPool { name: "CANOE", addresses: Box::new(["1Afcpc2FpPnREU6i52K3cicmHdvYRAH9Wo"]), tags: Box::new([]), link: "https://www.canoepool.com", }, - Pool { - id: 138.into(), + JSONPool { name: "tiger", addresses: Box::new(["1LsFmhnne74EmU4q4aobfxfrWY4wfMVd8w"]), tags: Box::new([]), link: "", }, - Pool { - id: 139.into(), + JSONPool { name: "1M1X", addresses: Box::new(["1M1Xw2rczxkF3p3wiNHaTmxvbpZZ7M6vaa"]), tags: Box::new([]), link: "", }, - Pool { - id: 140.into(), + JSONPool { name: "Zulupool", addresses: Box::new(["1ZULUPooLEQfkrTgynLV4uHyMgQYx71ip"]), tags: Box::new(["ZULUPooL", "ZU_test"]), link: "https://beta.zulupool.com/", }, - Pool { - id: 141.into(), + JSONPool { name: "SECPOOL", addresses: Box::new(["3Awm3FNpmwrbvAFVThRUFqgpbVuqWisni9"]), tags: Box::new(["SecPool"]), link: "https://www.secpool.com", }, - Pool { - id: 142.into(), + JSONPool { name: "OCEAN", addresses: Box::new(["37dvwZZoT3D7RXpTCpN2yKzMmNs2i2Fd1n"]), tags: Box::new(["OCEAN.XYZ"]), link: "https://ocean.xyz/", }, - Pool { - id: 143.into(), + JSONPool { name: "WhitePool", addresses: Box::new(["14VkxDwSAUWrzYTxV49HnYhKLWTJ3pCoUS"]), tags: Box::new(["WhitePool"]), link: "https://whitebit.com/mining-pool", }, - Pool { - id: 144.into(), - name: "wiz", - addresses: Box::new(["tb1q548z58kqvwyjqwy8vc2ntmg33d7s2wyfv7ukq4"]), - tags: Box::new(["/@wiz/"]), - link: "https://wiz.biz/", - }, - Pool { - id: 145.into(), - name: "mononaut", - addresses: Box::new(["mjP97q5BWtdpdsJLkEJvQWgLe9zw4MMVU6"]), - tags: Box::new(["🐵🚀"]), - link: "https://twitter.com/mononautical", - }, - Pool { - id: 146.into(), - name: "rijndael", - addresses: Box::new(["tb1qg8zlznrvns9u46muxamxjh7sa8wry3vutzaujm"]), - tags: Box::new(["rijndael's toaster"]), - link: "https://twitter.com/rot13maxi", - }, - Pool { - id: 147.into(), + JSONPool { name: "wk057", addresses: Box::new(["1WizkidqARMLvjGUpfDQFRcEbnHpL55kK"]), tags: Box::new(["wizkid057's block"]), link: "", }, - Pool { - id: 148.into(), + JSONPool { name: "FutureBit Apollo Solo", addresses: Box::new([]), tags: Box::new(["Apollo", "/mined by a Solo FutureBit Apollo/"]), link: "https://www.futurebit.io", }, - Pool { - id: 149.into(), - name: "emzy", - addresses: Box::new(["tb1qmf7xdqc5nvzhturuzc46qtq5kywdf3p76cpq53"]), - tags: Box::new(["Emzy was here."]), - link: "https://twitter.com/emzy", - }, - Pool { - id: 150.into(), - name: "knorrium", - addresses: Box::new(["tb1qtfqp4g7n7wc3sr6c2cuzsq62px4pfsxgsv2krx"]), - tags: Box::new(["knorrium"]), - link: "https://twitter.com/knorrium", - }, - Pool { - id: 151.into(), + JSONPool { name: "Carbon Negative", addresses: Box::new([ "33SAB6pzbhEGPbfY6NVgRDV7jVfspZ3A3Z", @@ -1246,15 +1084,13 @@ pub fn pools() -> &'static Pools { tags: Box::new([]), link: "https://github.com/bitcoin-data/mining-pools/issues/48", }, - Pool { - id: 152.into(), + JSONPool { name: "Portland.HODL", addresses: Box::new([]), tags: Box::new(["Portland.HODL"]), link: "", }, - Pool { - id: 153.into(), + JSONPool { name: "Phoenix", addresses: Box::new([ "37cGvBD4qufoZQHopGS7XstxRUzx5cNuy1", @@ -1264,57 +1100,43 @@ pub fn pools() -> &'static Pools { tags: Box::new(["/Phoenix/"]), link: "https://phoenixpool.com", }, - Pool { - id: 154.into(), + JSONPool { name: "Neopool", addresses: Box::new(["1HCAb2h89bUinm6QZrAPpfbk4ySBrT2V4w"]), tags: Box::new(["/Neopool/"]), link: "https://neopool.com/", }, - Pool { - id: 155.into(), + JSONPool { name: "MaxiPool", addresses: Box::new(["36r3YqAXWpyqNcczjCBdHrYZ3m8X56WDzx"]), tags: Box::new(["/MaxiPool/"]), link: "https://maxipool.org/", }, - Pool { - id: 156.into(), - name: "DrDetroit", - addresses: Box::new(["tb1qtcruplnz89xw5f86kw8sj7x9r23d5yffrysx2p"]), - tags: Box::new(["DrDetroit"]), - link: "https://x.com/bankhatin", - }, - Pool { - id: 157.into(), + JSONPool { name: "BitFuFuPool", addresses: Box::new(["3JP3zF7LoeoAotqkNGdvX5szUyNPwd937d"]), tags: Box::new(["/BitFuFu/"]), link: "https://www.bitfufu.com/pool", }, - Pool { - id: 158.into(), + JSONPool { name: "luckyPool", addresses: Box::new(["1DnPPFQPrfyNTiHPXhDFyqNnW9T62GEhB1"]), tags: Box::new(["Lucky pool"]), link: "", }, - Pool { - id: 159.into(), + JSONPool { name: "Mining-Dutch", addresses: Box::new(["1AfPSq5ZbqBaxU5QAayLQJMcXV8HZt92eq"]), tags: Box::new(["/Mining-Dutch/"]), link: "https://www.mining-dutch.nl/", }, - Pool { - id: 160.into(), + JSONPool { name: "Public Pool", addresses: Box::new([]), tags: Box::new(["Public-Pool", "Public Pool on Umbrel"]), link: "https://web.public-pool.io/", }, - Pool { - id: 161.into(), + JSONPool { name: "Mining Squared", addresses: Box::new([ "3GdjWJdkJhtkxRZ3Ns1LstaoHNMBW8XsvU", @@ -1323,29 +1145,19 @@ pub fn pools() -> &'static Pools { tags: Box::new(["MiningSquared", "BSquared Network", "/bsquared/"]), link: "https://pool.bsquared.network/", }, - Pool { - id: 162.into(), + JSONPool { name: "Innopolis Tech", addresses: Box::new(["bc1q75t4wewkmf3l9qg097zvtlh05v5pdz6699kv8k"]), tags: Box::new(["Innopolis", "Innopolis.tech"]), link: "https://innopolis.tech/", }, - Pool { - id: 163.into(), - name: "nymkappa", - addresses: Box::new(["tb1qdyy39724wqnqqqduv6zxsf56s2ec9lgypxs59h"]), - tags: Box::new(["/@nymkappa/"]), - link: "https://github.com/nymkappa", - }, - Pool { - id: 164.into(), + JSONPool { name: "BTCLab", addresses: Box::new([]), tags: Box::new(["BTCLab", "BTCLab.dev"]), link: "https://btclab.dev/", }, - Pool { - id: 165.into(), + JSONPool { name: "Parasite", addresses: Box::new([]), tags: Box::new(["parasite"]), @@ -1354,3 +1166,17 @@ pub fn pools() -> &'static Pools { ]) }) } + +impl From<[JSONPool; POOL_COUNT]> for Pools { + fn from(value: [JSONPool; POOL_COUNT]) -> Self { + Pools( + value + .into_iter() + .enumerate() + .map(|tuple| tuple.into()) + .collect::>() + .try_into() + .unwrap(), + ) + } +} diff --git a/crates/brk_computer/src/pools/vecs.rs b/crates/brk_computer/src/pools/vecs.rs new file mode 100644 index 000000000..fd003eb29 --- /dev/null +++ b/crates/brk_computer/src/pools/vecs.rs @@ -0,0 +1,467 @@ +use allocative::Allocative; +use brk_error::Result; +use brk_indexer::Indexer; +use brk_structs::{Height, Sats, StoredF32, StoredU16, StoredU32}; +use vecdb::{AnyCollectableVec, AnyIterableVec, Database, Exit, StoredIndex, VecIterator, Version}; + +use crate::{ + PoolId, Pools, chain, + grouped::{ + ComputedValueVecsFromHeight, ComputedVecsFromDateIndex, ComputedVecsFromHeight, Source, + VecBuilderOptions, + }, + indexes::{self, Indexes}, + price, +}; + +#[derive(Clone, Allocative)] +pub struct Vecs { + id: PoolId, + + indexes_to_blocks_mined: ComputedVecsFromHeight, + indexes_to_1w_blocks_mined: ComputedVecsFromDateIndex, + indexes_to_1m_blocks_mined: ComputedVecsFromDateIndex, + indexes_to_1y_blocks_mined: ComputedVecsFromDateIndex, + indexes_to_subsidy: ComputedValueVecsFromHeight, + indexes_to_fee: ComputedValueVecsFromHeight, + indexes_to_coinbase: ComputedValueVecsFromHeight, + indexes_to_dominance: ComputedVecsFromDateIndex, + indexes_to_1d_dominance: ComputedVecsFromDateIndex, + indexes_to_1w_dominance: ComputedVecsFromDateIndex, + indexes_to_1m_dominance: ComputedVecsFromDateIndex, + indexes_to_1y_dominance: ComputedVecsFromDateIndex, + indexes_to_days_since_block: ComputedVecsFromDateIndex, +} + +impl Vecs { + pub fn forced_import( + db: &Database, + id: PoolId, + pools: &Pools, + parent_version: Version, + indexes: &indexes::Vecs, + price: Option<&price::Vecs>, + ) -> Result { + let pool = pools.get(id); + let name = pool.serialized_id(); + let suffix = |s: &str| format!("{name}_{s}"); + let compute_dollars = price.is_some(); + let version = parent_version + Version::ZERO; + + Ok(Self { + id, + indexes_to_blocks_mined: ComputedVecsFromHeight::forced_import( + db, + &suffix("blocks_mined"), + Source::Compute, + version + Version::ZERO, + indexes, + VecBuilderOptions::default().add_sum().add_cumulative(), + )?, + indexes_to_1w_blocks_mined: ComputedVecsFromDateIndex::forced_import( + db, + &suffix("1w_blocks_mined"), + Source::Compute, + version + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, + indexes_to_1m_blocks_mined: ComputedVecsFromDateIndex::forced_import( + db, + &suffix("1m_blocks_mined"), + Source::Compute, + version + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, + indexes_to_1y_blocks_mined: ComputedVecsFromDateIndex::forced_import( + db, + &suffix("1y_blocks_mined"), + Source::Compute, + version + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, + indexes_to_subsidy: ComputedValueVecsFromHeight::forced_import( + db, + &suffix("subsidy"), + Source::Compute, + version + Version::ZERO, + VecBuilderOptions::default().add_sum().add_cumulative(), + compute_dollars, + indexes, + )?, + indexes_to_fee: ComputedValueVecsFromHeight::forced_import( + db, + &suffix("fee"), + Source::Compute, + version + Version::ZERO, + VecBuilderOptions::default().add_sum().add_cumulative(), + compute_dollars, + indexes, + )?, + indexes_to_coinbase: ComputedValueVecsFromHeight::forced_import( + db, + &suffix("coinbase"), + Source::Compute, + version + Version::ZERO, + VecBuilderOptions::default().add_sum().add_cumulative(), + compute_dollars, + indexes, + )?, + indexes_to_dominance: ComputedVecsFromDateIndex::forced_import( + db, + &suffix("dominance"), + Source::Compute, + version + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, + indexes_to_1d_dominance: ComputedVecsFromDateIndex::forced_import( + db, + &suffix("1d_dominance"), + Source::Compute, + version + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, + indexes_to_1w_dominance: ComputedVecsFromDateIndex::forced_import( + db, + &suffix("1w_dominance"), + Source::Compute, + version + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, + indexes_to_1m_dominance: ComputedVecsFromDateIndex::forced_import( + db, + &suffix("1m_dominance"), + Source::Compute, + version + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, + indexes_to_1y_dominance: ComputedVecsFromDateIndex::forced_import( + db, + &suffix("1y_dominance"), + Source::Compute, + version + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, + indexes_to_days_since_block: ComputedVecsFromDateIndex::forced_import( + db, + &suffix("days_since_block"), + Source::Compute, + version + Version::ZERO, + indexes, + VecBuilderOptions::default().add_last(), + )?, + }) + } + + #[allow(clippy::too_many_arguments)] + pub fn compute( + &mut self, + indexer: &Indexer, + indexes: &indexes::Vecs, + starting_indexes: &Indexes, + height_to_pool: &impl AnyIterableVec, + chain: &chain::Vecs, + price: Option<&price::Vecs>, + exit: &Exit, + ) -> Result<()> { + self.indexes_to_blocks_mined.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |vec, _, _, starting_indexes, exit| { + vec.compute_transform( + starting_indexes.height, + height_to_pool, + |(h, id, ..)| { + ( + h, + if id == self.id { + StoredU32::ONE + } else { + StoredU32::ZERO + }, + ) + }, + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_1w_blocks_mined.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |v, _, _, starting_indexes, exit| { + v.compute_sum( + starting_indexes.dateindex, + self.indexes_to_blocks_mined.dateindex.unwrap_sum(), + 7, + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_1m_blocks_mined.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |v, _, _, starting_indexes, exit| { + v.compute_sum( + starting_indexes.dateindex, + self.indexes_to_blocks_mined.dateindex.unwrap_sum(), + 30, + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_1y_blocks_mined.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |v, _, _, starting_indexes, exit| { + v.compute_sum( + starting_indexes.dateindex, + self.indexes_to_blocks_mined.dateindex.unwrap_sum(), + 365, + exit, + )?; + Ok(()) + }, + )?; + + let height_to_blocks_mined = self.indexes_to_blocks_mined.height.as_ref().unwrap(); + + self.indexes_to_subsidy.compute_all( + indexer, + indexes, + price, + starting_indexes, + exit, + |vec, _, _, starting_indexes, exit| { + vec.compute_transform2( + starting_indexes.height, + height_to_blocks_mined, + chain.indexes_to_subsidy.sats.height.as_ref().unwrap(), + |(h, mined, sats, ..)| { + ( + h, + if mined == StoredU32::ONE { + sats + } else { + Sats::ZERO + }, + ) + }, + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_fee.compute_all( + indexer, + indexes, + price, + starting_indexes, + exit, + |vec, _, _, starting_indexes, exit| { + vec.compute_transform2( + starting_indexes.height, + height_to_blocks_mined, + chain.indexes_to_fee.sats.height.unwrap_sum(), + |(h, mined, sats, ..)| { + ( + h, + if mined == StoredU32::ONE { + sats + } else { + Sats::ZERO + }, + ) + }, + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_coinbase.compute_all( + indexer, + indexes, + price, + starting_indexes, + exit, + |vec, _, _, starting_indexes, exit| { + vec.compute_transform2( + starting_indexes.height, + height_to_blocks_mined, + chain.indexes_to_coinbase.sats.height.as_ref().unwrap(), + |(h, mined, sats, ..)| { + ( + h, + if mined == StoredU32::ONE { + sats + } else { + Sats::ZERO + }, + ) + }, + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_dominance.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |vec, _, _, starting_indexes, exit| { + vec.compute_percentage( + starting_indexes.dateindex, + self.indexes_to_blocks_mined.dateindex.unwrap_cumulative(), + chain.indexes_to_block_count.dateindex.unwrap_cumulative(), + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_1d_dominance.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |vec, _, _, starting_indexes, exit| { + vec.compute_percentage( + starting_indexes.dateindex, + self.indexes_to_blocks_mined.dateindex.unwrap_sum(), + chain.indexes_to_block_count.dateindex.unwrap_sum(), + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_1w_dominance.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |vec, _, _, starting_indexes, exit| { + vec.compute_percentage( + starting_indexes.dateindex, + self.indexes_to_1w_blocks_mined.dateindex.as_ref().unwrap(), + chain.indexes_to_1w_block_count.dateindex.as_ref().unwrap(), + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_1m_dominance.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |vec, _, _, starting_indexes, exit| { + vec.compute_percentage( + starting_indexes.dateindex, + self.indexes_to_1m_blocks_mined.dateindex.as_ref().unwrap(), + chain.indexes_to_1m_block_count.dateindex.as_ref().unwrap(), + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_1y_dominance.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |vec, _, _, starting_indexes, exit| { + vec.compute_percentage( + starting_indexes.dateindex, + self.indexes_to_1y_blocks_mined.dateindex.as_ref().unwrap(), + chain.indexes_to_1y_block_count.dateindex.as_ref().unwrap(), + exit, + )?; + Ok(()) + }, + )?; + + self.indexes_to_days_since_block.compute_all( + indexer, + indexes, + starting_indexes, + exit, + |v, _, _, starting_indexes, exit| { + let mut prev = None; + v.compute_transform2( + starting_indexes.dateindex, + self.indexes_to_blocks_mined.dateindex.unwrap_sum(), + self.indexes_to_blocks_mined.dateindex.unwrap_cumulative(), + |(i, sum, cumulative, slf)| { + if prev.is_none() { + let i = i.unwrap_to_usize(); + prev.replace(if i > 0 { + slf.into_iter().unwrap_get_inner_(i - 1) + } else { + StoredU16::ZERO + }); + } + let days = if !cumulative.is_zero() && sum.is_zero() { + prev.unwrap() + StoredU16::ONE + } else { + StoredU16::ZERO + }; + prev.replace(days); + (i, days) + }, + exit, + )?; + Ok(()) + }, + )?; + + Ok(()) + } + + pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> { + [ + self.indexes_to_blocks_mined.vecs(), + self.indexes_to_1w_blocks_mined.vecs(), + self.indexes_to_1m_blocks_mined.vecs(), + self.indexes_to_1y_blocks_mined.vecs(), + self.indexes_to_subsidy.vecs(), + self.indexes_to_fee.vecs(), + self.indexes_to_coinbase.vecs(), + self.indexes_to_dominance.vecs(), + self.indexes_to_1d_dominance.vecs(), + self.indexes_to_1w_dominance.vecs(), + self.indexes_to_1m_dominance.vecs(), + self.indexes_to_1y_dominance.vecs(), + self.indexes_to_days_since_block.vecs(), + ] + .into_iter() + .flatten() + .collect() + } +} diff --git a/crates/brk_indexer/src/lib.rs b/crates/brk_indexer/src/lib.rs index e8cb19215..097e21f17 100644 --- a/crates/brk_indexer/src/lib.rs +++ b/crates/brk_indexer/src/lib.rs @@ -23,9 +23,12 @@ pub use indexes::*; pub use stores::*; pub use vecs::*; +// One version for all data sources +// Increment on change OR addition +const VERSION: Version = Version::new(21); + const SNAPSHOT_BLOCK_RANGE: usize = 1_000; const COLLISIONS_CHECKED_UP_TO: Height = Height::new(909_150); -const VERSION: Version = Version::ONE; #[derive(Clone)] pub struct Indexer { @@ -39,10 +42,10 @@ impl Indexer { let path = outputs_dir.join("indexed"); - let vecs = Vecs::forced_import(&path, VERSION + Version::ZERO)?; + let vecs = Vecs::forced_import(&path, VERSION)?; info!("Imported vecs"); - let stores = Stores::forced_import(&path, VERSION + Version::ZERO)?; + let stores = Stores::forced_import(&path, VERSION)?; info!("Imported stores"); Ok(Self { vecs, stores }) diff --git a/crates/brk_indexer/src/stores.rs b/crates/brk_indexer/src/stores.rs index 887b39dae..677ac7ade 100644 --- a/crates/brk_indexer/src/stores.rs +++ b/crates/brk_indexer/src/stores.rs @@ -27,8 +27,6 @@ pub struct Stores { ByAddressType>, } -const VERSION: Version = Version::ZERO; - impl Stores { pub fn forced_import(parent: &Path, version: Version) -> Result { let path = parent.join("stores"); @@ -49,34 +47,21 @@ impl Stores { &keyspace, &path, "addressbyteshash_to_typeindex", - version + VERSION + Version::ZERO, + version, None, ) }); let blockhashprefix_to_height = scope.spawn(|| { - Store::import( - &keyspace, - &path, - "blockhashprefix_to_height", - version + VERSION + Version::ZERO, - None, - ) - }); - let txidprefix_to_txindex = scope.spawn(|| { - Store::import( - &keyspace, - &path, - "txidprefix_to_txindex", - version + VERSION + Version::ZERO, - None, - ) + Store::import(&keyspace, &path, "blockhashprefix_to_height", version, None) }); + let txidprefix_to_txindex = scope + .spawn(|| Store::import(&keyspace, &path, "txidprefix_to_txindex", version, None)); let p2aaddressindex_with_outputindex = scope.spawn(|| { Store::import( &keyspace, &path, "p2aaddressindex_with_outputindex", - version + VERSION + Version::ZERO, + version, Some(false), ) }); @@ -85,7 +70,7 @@ impl Stores { &keyspace, &path, "p2pk33addressindex_with_outputindex", - version + VERSION + Version::ZERO, + version, Some(false), ) }); @@ -94,7 +79,7 @@ impl Stores { &keyspace, &path, "p2pk65addressindex_with_outputindex", - version + VERSION + Version::ZERO, + version, Some(false), ) }); @@ -103,7 +88,7 @@ impl Stores { &keyspace, &path, "p2pkhaddressindex_with_outputindex", - version + VERSION + Version::ZERO, + version, Some(false), ) }); @@ -112,7 +97,7 @@ impl Stores { &keyspace, &path, "p2shaddressindex_with_outputindex", - version + VERSION + Version::ZERO, + version, Some(false), ) }); @@ -121,7 +106,7 @@ impl Stores { &keyspace, &path, "p2traddressindex_with_outputindex", - version + VERSION + Version::ZERO, + version, Some(false), ) }); @@ -130,7 +115,7 @@ impl Stores { &keyspace, &path, "p2wpkhaddressindex_with_outputindex", - version + VERSION + Version::ZERO, + version, Some(false), ) }); @@ -139,18 +124,13 @@ impl Stores { &keyspace, &path, "p2wshaddressindex_with_outputindex", - version + VERSION + Version::ZERO, + version, Some(false), ) }); - let height_to_coinbase_tag = Store::import( - &keyspace, - &path, - "height_to_coinbase_tag", - version + VERSION + Version::ZERO, - None, - )?; + let height_to_coinbase_tag = + Store::import(&keyspace, &path, "height_to_coinbase_tag", version, None)?; Ok(Self { keyspace: keyspace.clone(), diff --git a/crates/brk_indexer/src/vecs.rs b/crates/brk_indexer/src/vecs.rs index d3ea73d5c..f217ab935 100644 --- a/crates/brk_indexer/src/vecs.rs +++ b/crates/brk_indexer/src/vecs.rs @@ -17,8 +17,6 @@ use vecdb::{ use crate::Indexes; -const VERSION: Version = Version::ZERO; - #[derive(Clone)] pub struct Vecs { db: Database, @@ -78,222 +76,118 @@ impl Vecs { db.set_min_len(PAGE_SIZE * 50_000_000)?; let this = Self { - emptyoutputindex_to_txindex: CompressedVec::forced_import( - &db, - "txindex", - version + VERSION + Version::ZERO, - )?, - height_to_blockhash: RawVec::forced_import( - &db, - "blockhash", - version + VERSION + Version::ZERO, - )?, - height_to_difficulty: CompressedVec::forced_import( - &db, - "difficulty", - version + VERSION + Version::ZERO, - )?, + emptyoutputindex_to_txindex: CompressedVec::forced_import(&db, "txindex", version)?, + height_to_blockhash: RawVec::forced_import(&db, "blockhash", version)?, + height_to_difficulty: CompressedVec::forced_import(&db, "difficulty", version)?, height_to_first_emptyoutputindex: CompressedVec::forced_import( &db, "first_emptyoutputindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_inputindex: CompressedVec::forced_import( &db, "first_inputindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_opreturnindex: CompressedVec::forced_import( &db, "first_opreturnindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_outputindex: CompressedVec::forced_import( &db, "first_outputindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_p2aaddressindex: CompressedVec::forced_import( &db, "first_p2aaddressindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_p2msoutputindex: CompressedVec::forced_import( &db, "first_p2msoutputindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_p2pk33addressindex: CompressedVec::forced_import( &db, "first_p2pk33addressindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_p2pk65addressindex: CompressedVec::forced_import( &db, "first_p2pk65addressindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_p2pkhaddressindex: CompressedVec::forced_import( &db, "first_p2pkhaddressindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_p2shaddressindex: CompressedVec::forced_import( &db, "first_p2shaddressindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_p2traddressindex: CompressedVec::forced_import( &db, "first_p2traddressindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_p2wpkhaddressindex: CompressedVec::forced_import( &db, "first_p2wpkhaddressindex", - version + VERSION + Version::ZERO, + version, )?, height_to_first_p2wshaddressindex: CompressedVec::forced_import( &db, "first_p2wshaddressindex", - version + VERSION + Version::ZERO, - )?, - height_to_first_txindex: CompressedVec::forced_import( - &db, - "first_txindex", - version + VERSION + Version::ZERO, + version, )?, + height_to_first_txindex: CompressedVec::forced_import(&db, "first_txindex", version)?, height_to_first_unknownoutputindex: CompressedVec::forced_import( &db, "first_unknownoutputindex", - version + VERSION + Version::ZERO, - )?, - height_to_timestamp: CompressedVec::forced_import( - &db, - "timestamp", - version + VERSION + Version::ZERO, - )?, - height_to_total_size: CompressedVec::forced_import( - &db, - "total_size", - version + VERSION + Version::ZERO, - )?, - height_to_weight: CompressedVec::forced_import( - &db, - "weight", - version + VERSION + Version::ZERO, - )?, - inputindex_to_outputindex: RawVec::forced_import( - &db, - "outputindex", - version + VERSION + Version::ZERO, - )?, - opreturnindex_to_txindex: CompressedVec::forced_import( - &db, - "txindex", - version + VERSION + Version::ZERO, - )?, - outputindex_to_outputtype: RawVec::forced_import( - &db, - "outputtype", - version + VERSION + Version::ZERO, - )?, - outputindex_to_typeindex: RawVec::forced_import( - &db, - "typeindex", - version + VERSION + Version::ZERO, - )?, - outputindex_to_value: RawVec::forced_import( - &db, - "value", - version + VERSION + Version::ZERO, - )?, - p2aaddressindex_to_p2abytes: RawVec::forced_import( - &db, - "p2abytes", - version + VERSION + Version::ZERO, - )?, - p2msoutputindex_to_txindex: CompressedVec::forced_import( - &db, - "txindex", - version + VERSION + Version::ZERO, - )?, - p2pk33addressindex_to_p2pk33bytes: RawVec::forced_import( - &db, - "p2pk33bytes", - version + VERSION + Version::ZERO, - )?, - p2pk65addressindex_to_p2pk65bytes: RawVec::forced_import( - &db, - "p2pk65bytes", - version + VERSION + Version::ZERO, - )?, - p2pkhaddressindex_to_p2pkhbytes: RawVec::forced_import( - &db, - "p2pkhbytes", - version + VERSION + Version::ZERO, - )?, - p2shaddressindex_to_p2shbytes: RawVec::forced_import( - &db, - "p2shbytes", - version + VERSION + Version::ZERO, - )?, - p2traddressindex_to_p2trbytes: RawVec::forced_import( - &db, - "p2trbytes", - version + VERSION + Version::ZERO, - )?, - p2wpkhaddressindex_to_p2wpkhbytes: RawVec::forced_import( - &db, - "p2wpkhbytes", - version + VERSION + Version::ZERO, - )?, - p2wshaddressindex_to_p2wshbytes: RawVec::forced_import( - &db, - "p2wshbytes", - version + VERSION + Version::ZERO, - )?, - txindex_to_base_size: CompressedVec::forced_import( - &db, - "base_size", - version + VERSION + Version::ZERO, + version, )?, + height_to_timestamp: CompressedVec::forced_import(&db, "timestamp", version)?, + height_to_total_size: CompressedVec::forced_import(&db, "total_size", version)?, + height_to_weight: CompressedVec::forced_import(&db, "weight", version)?, + inputindex_to_outputindex: RawVec::forced_import(&db, "outputindex", version)?, + opreturnindex_to_txindex: CompressedVec::forced_import(&db, "txindex", version)?, + outputindex_to_outputtype: RawVec::forced_import(&db, "outputtype", version)?, + outputindex_to_typeindex: RawVec::forced_import(&db, "typeindex", version)?, + outputindex_to_value: RawVec::forced_import(&db, "value", version)?, + p2aaddressindex_to_p2abytes: RawVec::forced_import(&db, "p2abytes", version)?, + p2msoutputindex_to_txindex: CompressedVec::forced_import(&db, "txindex", version)?, + p2pk33addressindex_to_p2pk33bytes: RawVec::forced_import(&db, "p2pk33bytes", version)?, + p2pk65addressindex_to_p2pk65bytes: RawVec::forced_import(&db, "p2pk65bytes", version)?, + p2pkhaddressindex_to_p2pkhbytes: RawVec::forced_import(&db, "p2pkhbytes", version)?, + p2shaddressindex_to_p2shbytes: RawVec::forced_import(&db, "p2shbytes", version)?, + p2traddressindex_to_p2trbytes: RawVec::forced_import(&db, "p2trbytes", version)?, + p2wpkhaddressindex_to_p2wpkhbytes: RawVec::forced_import(&db, "p2wpkhbytes", version)?, + p2wshaddressindex_to_p2wshbytes: RawVec::forced_import(&db, "p2wshbytes", version)?, + txindex_to_base_size: CompressedVec::forced_import(&db, "base_size", version)?, txindex_to_first_inputindex: CompressedVec::forced_import( &db, "first_inputindex", - version + VERSION + Version::ZERO, + version, )?, txindex_to_first_outputindex: CompressedVec::forced_import( &db, "first_outputindex", - version + VERSION + Version::ZERO, + version, )?, txindex_to_is_explicitly_rbf: CompressedVec::forced_import( &db, "is_explicitly_rbf", - version + VERSION + Version::ZERO, - )?, - txindex_to_rawlocktime: CompressedVec::forced_import( - &db, - "rawlocktime", - version + VERSION + Version::ZERO, - )?, - txindex_to_total_size: CompressedVec::forced_import( - &db, - "total_size", - version + VERSION + Version::ZERO, - )?, - txindex_to_txid: RawVec::forced_import(&db, "txid", version + VERSION + Version::ZERO)?, - txindex_to_txversion: CompressedVec::forced_import( - &db, - "txversion", - version + VERSION + Version::ZERO, - )?, - unknownoutputindex_to_txindex: CompressedVec::forced_import( - &db, - "txindex", - version + VERSION + Version::ZERO, + version, )?, + txindex_to_rawlocktime: CompressedVec::forced_import(&db, "rawlocktime", version)?, + txindex_to_total_size: CompressedVec::forced_import(&db, "total_size", version)?, + txindex_to_txid: RawVec::forced_import(&db, "txid", version)?, + txindex_to_txversion: CompressedVec::forced_import(&db, "txversion", version)?, + unknownoutputindex_to_txindex: CompressedVec::forced_import(&db, "txindex", version)?, db, }; diff --git a/crates/brk_interface/src/format.rs b/crates/brk_interface/src/format.rs index 8d5b7d642..2d8d45cfb 100644 --- a/crates/brk_interface/src/format.rs +++ b/crates/brk_interface/src/format.rs @@ -4,6 +4,7 @@ use serde::Deserialize; #[allow(clippy::upper_case_acronyms)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, JsonSchema)] +#[serde(rename_all = "lowercase")] pub enum Format { #[serde(alias = "json")] JSON, diff --git a/crates/brk_interface/src/ids.rs b/crates/brk_interface/src/ids.rs index 41627e66c..a751fb366 100644 --- a/crates/brk_interface/src/ids.rs +++ b/crates/brk_interface/src/ids.rs @@ -29,20 +29,22 @@ impl<'de> Deserialize<'de> for MaybeIds { { match serde_json::Value::deserialize(deserializer)? { serde_json::Value::String(str) => { - if str.len() > MAX_STRING_SIZE { + if str.len() <= MAX_STRING_SIZE { Ok(MaybeIds(sanitize_ids( str.split(",").map(|s| s.to_string()), ))) } else { + dbg!(str.len(), MAX_STRING_SIZE); Err(serde::de::Error::custom("Given parameter is too long")) } } serde_json::Value::Array(vec) => { - if vec.len() > MAX_VECS { + if vec.len() <= MAX_VECS { Ok(MaybeIds(sanitize_ids( vec.into_iter().map(|s| s.as_str().unwrap().to_string()), ))) } else { + dbg!(vec.len(), MAX_VECS); Err(serde::de::Error::custom("Given parameter is too long")) } } diff --git a/crates/brk_interface/src/index.rs b/crates/brk_interface/src/index.rs index 937bbf7d6..98a0b9c92 100644 --- a/crates/brk_interface/src/index.rs +++ b/crates/brk_interface/src/index.rs @@ -12,6 +12,7 @@ use schemars::JsonSchema; use serde::Deserialize; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +#[serde(rename_all = "lowercase")] pub enum Index { #[schemars(description = "Date/day index")] DateIndex, diff --git a/crates/brk_interface/src/output.rs b/crates/brk_interface/src/output.rs index 287e6bd23..def90a4e9 100644 --- a/crates/brk_interface/src/output.rs +++ b/crates/brk_interface/src/output.rs @@ -6,7 +6,7 @@ use tabled::Tabled as TabledTabled; use crate::Format; #[derive(Debug, Serialize)] -#[serde(untagged)] +#[serde(untagged, rename_all = "lowercase")] pub enum Output { Json(Value), CSV(String), diff --git a/crates/brk_parser/examples/main.rs b/crates/brk_parser/examples/main.rs index 4baa0eb5d..fdb11a5e3 100644 --- a/crates/brk_parser/examples/main.rs +++ b/crates/brk_parser/examples/main.rs @@ -1,7 +1,7 @@ use std::path::Path; use bitcoincore_rpc::{Auth, Client, Result}; -use brk_parser::Parser; +use brk_parser::{BlockExtended, Parser}; use brk_structs::Height; #[allow(clippy::needless_doctest_main)] @@ -21,55 +21,26 @@ fn main() -> Result<()> { let parser = Parser::new(bitcoin_dir.join("blocks"), Some(brk_dir), rpc); - let start = None; - let end = None; - parser - .parse(start, end) - .iter() - .for_each(|(height, _block, hash)| { - println!("{height}: {}", hash); - }); + // let start = None; + // let end = None; + // parser + // .parse(start, end) + // .iter() + // .for_each(|(height, _block, hash)| { + // println!("{height}: {}", hash); + // }); let block_0 = parser.get(Height::new(0)); - - println!( - "{}", - block_0 - .txdata - .first() - .unwrap() - .output - .first() - .unwrap() - .script_pubkey - ); + dbg!("{}", block_0.coinbase_tag()); let block_158251 = parser.get(Height::new(158251)); - println!( - "{}", - block_158251 - .txdata - .first() - .unwrap() - .output - .first() - .unwrap() - .script_pubkey - ); + dbg!("{}", block_158251.coinbase_tag()); + + let block_173195 = parser.get(Height::new(173195)); + dbg!("{}", block_173195.coinbase_tag()); let block_840_000 = parser.get(Height::new(840_004)); - - println!( - "{}", - block_840_000 - .txdata - .first() - .unwrap() - .output - .first() - .unwrap() - .value - ); + dbg!("{}", block_840_000.coinbase_tag()); dbg!(i.elapsed()); diff --git a/crates/brk_parser/src/block.rs b/crates/brk_parser/src/block.rs index b2c1decbf..6eb2eef2c 100644 --- a/crates/brk_parser/src/block.rs +++ b/crates/brk_parser/src/block.rs @@ -1,19 +1,20 @@ +use std::borrow::Cow; + use bitcoin::Block; pub trait BlockExtended { - fn coinbase_tag(&self) -> String; + fn coinbase_tag(&self) -> Cow<'_, str>; } impl BlockExtended for Block { - fn coinbase_tag(&self) -> String { - let Some(input) = self.txdata.first().and_then(|tx| tx.input.first()) else { - return String::new(); - }; - let bytes = input.script_sig.as_bytes(); - String::from_utf8_lossy(bytes) - .chars() - .filter(|&c| c != '\u{FFFD}' && (c >= ' ' || c == '\n' || c == '\r' || c == '\t')) - .take(1_024) - .collect() + fn coinbase_tag(&self) -> Cow<'_, str> { + String::from_utf8_lossy( + self.txdata + .first() + .and_then(|tx| tx.input.first()) + .unwrap() + .script_sig + .as_bytes(), + ) } } diff --git a/crates/brk_structs/Cargo.toml b/crates/brk_structs/Cargo.toml index dbf63283e..83492fb54 100644 --- a/crates/brk_structs/Cargo.toml +++ b/crates/brk_structs/Cargo.toml @@ -10,6 +10,8 @@ rust-version.workspace = true build = "build.rs" [dependencies] +allocative = { workspace = true } +allocative_derive = { workspace = true } bitcoin = { workspace = true } bitcoincore-rpc = { workspace = true } brk_error = {workspace = true} diff --git a/crates/brk_structs/src/structs/addressbytes.rs b/crates/brk_structs/src/structs/addressbytes.rs index 88c160d1c..b9a3b88f6 100644 --- a/crates/brk_structs/src/structs/addressbytes.rs +++ b/crates/brk_structs/src/structs/addressbytes.rs @@ -25,6 +25,25 @@ pub enum AddressBytes { P2A(P2ABytes), } +impl fmt::Display for AddressBytes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + AddressBytes::P2PK65(bytes) => bytes.to_string(), + AddressBytes::P2PK33(bytes) => bytes.to_string(), + AddressBytes::P2PKH(bytes) => bytes.to_string(), + AddressBytes::P2SH(bytes) => bytes.to_string(), + AddressBytes::P2WPKH(bytes) => bytes.to_string(), + AddressBytes::P2WSH(bytes) => bytes.to_string(), + AddressBytes::P2TR(bytes) => bytes.to_string(), + AddressBytes::P2A(bytes) => bytes.to_string(), + } + ) + } +} + impl AddressBytes { pub fn as_slice(&self) -> &[u8] { match self { diff --git a/crates/brk_structs/src/structs/anyaddressindex.rs b/crates/brk_structs/src/structs/anyaddressindex.rs index bd05e345a..dd3b3f668 100644 --- a/crates/brk_structs/src/structs/anyaddressindex.rs +++ b/crates/brk_structs/src/structs/anyaddressindex.rs @@ -44,6 +44,7 @@ impl Serialize for AnyAddressIndex { } #[derive(Debug, Serialize)] +#[serde(rename_all = "lowercase")] pub enum AnyAddressDataIndexEnum { Loaded(LoadedAddressIndex), Empty(EmptyAddressIndex), diff --git a/crates/brk_structs/src/structs/bitcoin.rs b/crates/brk_structs/src/structs/bitcoin.rs index 8b02f50c1..e0961e750 100644 --- a/crates/brk_structs/src/structs/bitcoin.rs +++ b/crates/brk_structs/src/structs/bitcoin.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Div, Mul}, }; +use allocative::Allocative; use serde::Serialize; use vecdb::{CheckedSub, StoredCompressed}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; @@ -20,6 +21,7 @@ use super::{Sats, StoredF64}; KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct Bitcoin(f64); diff --git a/crates/brk_structs/src/structs/dateindex.rs b/crates/brk_structs/src/structs/dateindex.rs index cf236fcdc..333947a29 100644 --- a/crates/brk_structs/src/structs/dateindex.rs +++ b/crates/brk_structs/src/structs/dateindex.rs @@ -3,10 +3,11 @@ use std::{ ops::{Add, Rem}, }; +use allocative::Allocative; use brk_error::Error; -use vecdb::{CheckedSub, FromCoarserIndex, Printable, StoredCompressed}; use jiff::Span; use serde::Serialize; +use vecdb::{CheckedSub, FromCoarserIndex, Printable, StoredCompressed}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; use crate::{DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, WeekIndex, YearIndex}; @@ -28,6 +29,7 @@ use super::Date; KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct DateIndex(u16); diff --git a/crates/brk_structs/src/structs/decadeindex.rs b/crates/brk_structs/src/structs/decadeindex.rs index bc377ecef..990aeb3c8 100644 --- a/crates/brk_structs/src/structs/decadeindex.rs +++ b/crates/brk_structs/src/structs/decadeindex.rs @@ -3,8 +3,9 @@ use std::{ ops::{Add, AddAssign, Div}, }; -use vecdb::{CheckedSub, Printable, StoredCompressed}; +use allocative::Allocative; use serde::{Deserialize, Serialize}; +use vecdb::{CheckedSub, Printable, StoredCompressed}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; use super::{Date, DateIndex, YearIndex}; @@ -25,6 +26,7 @@ use super::{Date, DateIndex, YearIndex}; IntoBytes, KnownLayout, StoredCompressed, + Allocative, )] pub struct DecadeIndex(u16); diff --git a/crates/brk_structs/src/structs/difficultyepoch.rs b/crates/brk_structs/src/structs/difficultyepoch.rs index e10e06f5e..dd2f4f7d4 100644 --- a/crates/brk_structs/src/structs/difficultyepoch.rs +++ b/crates/brk_structs/src/structs/difficultyepoch.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Div}, }; +use allocative::Allocative; use serde::{Deserialize, Serialize}; use vecdb::{CheckedSub, Printable, StoredCompressed}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; @@ -25,6 +26,7 @@ use super::Height; IntoBytes, KnownLayout, StoredCompressed, + Allocative, )] pub struct DifficultyEpoch(u16); diff --git a/crates/brk_structs/src/structs/dollars.rs b/crates/brk_structs/src/structs/dollars.rs index 95de0cad4..8d788cca7 100644 --- a/crates/brk_structs/src/structs/dollars.rs +++ b/crates/brk_structs/src/structs/dollars.rs @@ -4,6 +4,7 @@ use std::{ ops::{Add, AddAssign, Div, Mul}, }; +use allocative::Allocative; use derive_deref::Deref; use serde::{Deserialize, Serialize}; use vecdb::{CheckedSub, StoredCompressed}; @@ -24,6 +25,7 @@ use super::{Bitcoin, Cents, Close, High, Sats, StoredF32, StoredF64}; Serialize, Deserialize, StoredCompressed, + Allocative, )] pub struct Dollars(f64); diff --git a/crates/brk_structs/src/structs/feerate.rs b/crates/brk_structs/src/structs/feerate.rs index 5e1705462..8f7500025 100644 --- a/crates/brk_structs/src/structs/feerate.rs +++ b/crates/brk_structs/src/structs/feerate.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Div}, }; +use allocative::Allocative; use serde::Serialize; use vecdb::StoredCompressed; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; @@ -10,7 +11,16 @@ use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; use super::{Sats, StoredU64}; #[derive( - Debug, Clone, Copy, Serialize, FromBytes, Immutable, IntoBytes, KnownLayout, StoredCompressed, + Debug, + Clone, + Copy, + Serialize, + FromBytes, + Immutable, + IntoBytes, + KnownLayout, + StoredCompressed, + Allocative, )] pub struct FeeRate(f64); diff --git a/crates/brk_structs/src/structs/halvingepoch.rs b/crates/brk_structs/src/structs/halvingepoch.rs index 39ae6aaae..8ef0a7623 100644 --- a/crates/brk_structs/src/structs/halvingepoch.rs +++ b/crates/brk_structs/src/structs/halvingepoch.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Div}, }; +use allocative::Allocative; use serde::{Deserialize, Serialize}; use vecdb::{CheckedSub, Printable, StoredCompressed}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; @@ -25,6 +26,7 @@ use super::Height; IntoBytes, KnownLayout, StoredCompressed, + Allocative, )] pub struct HalvingEpoch(u16); diff --git a/crates/brk_structs/src/structs/height.rs b/crates/brk_structs/src/structs/height.rs index fb9749361..d90e1d146 100644 --- a/crates/brk_structs/src/structs/height.rs +++ b/crates/brk_structs/src/structs/height.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Rem}, }; +use allocative::Allocative; use bitcoincore_rpc::{Client, RpcApi}; use byteview::ByteView; use derive_deref::Deref; @@ -32,6 +33,7 @@ use super::StoredU64; IntoBytes, KnownLayout, StoredCompressed, + Allocative, )] pub struct Height(u32); diff --git a/crates/brk_structs/src/structs/inputindex.rs b/crates/brk_structs/src/structs/inputindex.rs index c08acefa9..31e67102a 100644 --- a/crates/brk_structs/src/structs/inputindex.rs +++ b/crates/brk_structs/src/structs/inputindex.rs @@ -1,5 +1,6 @@ use std::ops::{Add, AddAssign}; +use allocative::Allocative; use derive_deref::{Deref, DerefMut}; use serde::Serialize; use vecdb::{CheckedSub, Printable, StoredCompressed}; @@ -24,6 +25,7 @@ use super::Vin; KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct InputIndex(u64); diff --git a/crates/brk_structs/src/structs/monthindex.rs b/crates/brk_structs/src/structs/monthindex.rs index 7c071abf7..607c67864 100644 --- a/crates/brk_structs/src/structs/monthindex.rs +++ b/crates/brk_structs/src/structs/monthindex.rs @@ -3,8 +3,9 @@ use std::{ ops::{Add, AddAssign, Div}, }; -use vecdb::{CheckedSub, Printable, StoredCompressed}; +use allocative::Allocative; use serde::{Deserialize, Serialize}; +use vecdb::{CheckedSub, Printable, StoredCompressed}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; use super::{Date, DateIndex, YearIndex}; @@ -25,6 +26,7 @@ use super::{Date, DateIndex, YearIndex}; IntoBytes, KnownLayout, StoredCompressed, + Allocative, )] pub struct MonthIndex(u16); diff --git a/crates/brk_structs/src/structs/ohlc.rs b/crates/brk_structs/src/structs/ohlc.rs index 15b846674..901a6f040 100644 --- a/crates/brk_structs/src/structs/ohlc.rs +++ b/crates/brk_structs/src/structs/ohlc.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Div}, }; +use allocative::Allocative; use derive_deref::{Deref, DerefMut}; use serde::{Serialize, Serializer, ser::SerializeTuple}; use vecdb::StoredCompressed; @@ -473,6 +474,7 @@ where DerefMut, Serialize, StoredCompressed, + Allocative, )] #[repr(transparent)] pub struct Close(T); diff --git a/crates/brk_structs/src/structs/outputindex.rs b/crates/brk_structs/src/structs/outputindex.rs index a7f9eedaa..e083e47cd 100644 --- a/crates/brk_structs/src/structs/outputindex.rs +++ b/crates/brk_structs/src/structs/outputindex.rs @@ -1,5 +1,6 @@ use std::ops::{Add, AddAssign}; +use allocative::Allocative; use derive_deref::{Deref, DerefMut}; use serde::Serialize; use vecdb::{CheckedSub, Printable, StoredCompressed}; @@ -26,6 +27,7 @@ use super::Vout; KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct OutputIndex(u64); diff --git a/crates/brk_structs/src/structs/outputtype.rs b/crates/brk_structs/src/structs/outputtype.rs index 3a1814b9d..4699e0eb0 100644 --- a/crates/brk_structs/src/structs/outputtype.rs +++ b/crates/brk_structs/src/structs/outputtype.rs @@ -16,6 +16,7 @@ use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; KnownLayout, Serialize, )] +#[serde(rename_all = "lowercase")] #[repr(u8)] pub enum OutputType { P2PK65, diff --git a/crates/brk_structs/src/structs/quarterindex.rs b/crates/brk_structs/src/structs/quarterindex.rs index e4c438bb6..fd94d275f 100644 --- a/crates/brk_structs/src/structs/quarterindex.rs +++ b/crates/brk_structs/src/structs/quarterindex.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Div}, }; +use allocative::Allocative; use serde::{Deserialize, Serialize}; use vecdb::{CheckedSub, Printable, StoredCompressed}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; @@ -25,6 +26,7 @@ use super::MonthIndex; IntoBytes, KnownLayout, StoredCompressed, + Allocative, )] pub struct QuarterIndex(u16); diff --git a/crates/brk_structs/src/structs/sats.rs b/crates/brk_structs/src/structs/sats.rs index b9859a2e0..02a58cf46 100644 --- a/crates/brk_structs/src/structs/sats.rs +++ b/crates/brk_structs/src/structs/sats.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Div, Mul, SubAssign}, }; +use allocative::Allocative; use bitcoin::Amount; use derive_deref::Deref; use serde::{Deserialize, Serialize}; @@ -30,6 +31,7 @@ use super::{Bitcoin, Cents, Dollars, Height}; Serialize, Deserialize, StoredCompressed, + Allocative, )] pub struct Sats(u64); diff --git a/crates/brk_structs/src/structs/semesterindex.rs b/crates/brk_structs/src/structs/semesterindex.rs index 4c17beeee..d4f9d18f2 100644 --- a/crates/brk_structs/src/structs/semesterindex.rs +++ b/crates/brk_structs/src/structs/semesterindex.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Div}, }; +use allocative::Allocative; use serde::{Deserialize, Serialize}; use vecdb::{CheckedSub, Printable, StoredCompressed}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; @@ -25,6 +26,7 @@ use super::MonthIndex; IntoBytes, KnownLayout, StoredCompressed, + Allocative, )] pub struct SemesterIndex(u16); diff --git a/crates/brk_structs/src/structs/stored_bool.rs b/crates/brk_structs/src/structs/stored_bool.rs index 0086fd78c..6c57376ea 100644 --- a/crates/brk_structs/src/structs/stored_bool.rs +++ b/crates/brk_structs/src/structs/stored_bool.rs @@ -1,3 +1,4 @@ +use allocative::Allocative; use derive_deref::Deref; use serde::Serialize; use vecdb::{Printable, StoredCompressed}; @@ -19,6 +20,7 @@ use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct StoredBool(u16); diff --git a/crates/brk_structs/src/structs/stored_f32.rs b/crates/brk_structs/src/structs/stored_f32.rs index 50e901840..f6be756e1 100644 --- a/crates/brk_structs/src/structs/stored_f32.rs +++ b/crates/brk_structs/src/structs/stored_f32.rs @@ -6,11 +6,14 @@ use std::{ ops::{Add, AddAssign, Div, Mul, Sub}, }; +use allocative::Allocative; use derive_deref::Deref; use serde::Serialize; use vecdb::{CheckedSub, Printable, StoredCompressed}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; +use crate::{Close, StoredU32}; + use super::{Dollars, StoredF64}; #[derive( @@ -25,6 +28,7 @@ use super::{Dollars, StoredF64}; KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct StoredF32(f32); @@ -65,12 +69,24 @@ impl From for StoredF32 { } } +impl From for StoredF32 { + fn from(value: StoredU32) -> Self { + Self(f32::from(value)) + } +} + impl CheckedSub for StoredF32 { fn checked_sub(self, rhs: Self) -> Option { Some(Self(self.0 - rhs.0)) } } +impl CheckedSub for StoredF32 { + fn checked_sub(self, rhs: usize) -> Option { + Some(Self(self.0 - rhs as f32)) + } +} + impl Div for StoredF32 { type Output = Self; fn div(self, rhs: usize) -> Self::Output { @@ -78,6 +94,13 @@ impl Div for StoredF32 { } } +impl Div for StoredF32 { + type Output = Self; + fn div(self, rhs: StoredU32) -> Self::Output { + Self(self.0 / f32::from(rhs)) + } +} + impl Add for StoredF32 { type Output = Self; fn add(self, rhs: Self) -> Self::Output { @@ -103,6 +126,12 @@ impl From for StoredF32 { } } +impl From> for StoredF32 { + fn from(value: Close) -> Self { + Self::from(*value) + } +} + impl Div for StoredF32 { type Output = Self; fn div(self, rhs: Dollars) -> Self::Output { diff --git a/crates/brk_structs/src/structs/stored_f64.rs b/crates/brk_structs/src/structs/stored_f64.rs index 1c6d98825..b4c5ec2ea 100644 --- a/crates/brk_structs/src/structs/stored_f64.rs +++ b/crates/brk_structs/src/structs/stored_f64.rs @@ -4,6 +4,7 @@ use std::{ ops::{Add, AddAssign, Div, Mul}, }; +use allocative::Allocative; use derive_deref::Deref; use serde::Serialize; use vecdb::{CheckedSub, Printable, StoredCompressed}; @@ -23,6 +24,7 @@ use crate::{Bitcoin, Dollars}; KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct StoredF64(f64); diff --git a/crates/brk_structs/src/structs/stored_string.rs b/crates/brk_structs/src/structs/stored_string.rs index ca55470af..45282733b 100644 --- a/crates/brk_structs/src/structs/stored_string.rs +++ b/crates/brk_structs/src/structs/stored_string.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use byteview::ByteView; use derive_deref::Deref; use serde::Serialize; @@ -26,6 +28,12 @@ impl From for StoredString { } } +impl From> for StoredString { + fn from(value: Cow<'_, str>) -> Self { + Self(value.to_string()) + } +} + impl From for StoredString { fn from(value: ByteView) -> Self { let bytes = &*value; diff --git a/crates/brk_structs/src/structs/stored_u16.rs b/crates/brk_structs/src/structs/stored_u16.rs index 3290395da..41d7c709a 100644 --- a/crates/brk_structs/src/structs/stored_u16.rs +++ b/crates/brk_structs/src/structs/stored_u16.rs @@ -1,5 +1,6 @@ use std::ops::{Add, AddAssign, Div}; +use allocative::Allocative; use derive_deref::Deref; use serde::Serialize; use vecdb::{CheckedSub, Printable, StoredCompressed}; @@ -27,11 +28,13 @@ use super::{ KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct StoredU16(u16); impl StoredU16 { pub const ZERO: Self = Self(0); + pub const ONE: Self = Self(1); pub fn new(v: u16) -> Self { Self(v) diff --git a/crates/brk_structs/src/structs/stored_u32.rs b/crates/brk_structs/src/structs/stored_u32.rs index ada3e7654..575a76449 100644 --- a/crates/brk_structs/src/structs/stored_u32.rs +++ b/crates/brk_structs/src/structs/stored_u32.rs @@ -1,5 +1,6 @@ -use std::ops::{Add, AddAssign, Div}; +use std::ops::{Add, AddAssign, Div, Mul}; +use allocative::Allocative; use derive_deref::Deref; use serde::Serialize; use vecdb::{CheckedSub, Printable, StoredCompressed}; @@ -27,15 +28,21 @@ use super::{ KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct StoredU32(u32); impl StoredU32 { pub const ZERO: Self = Self(0); + pub const ONE: Self = Self(1); pub fn new(counter: u32) -> Self { Self(counter) } + + pub fn is_zero(&self) -> bool { + self.0 == 0 + } } impl From for StoredU32 { @@ -44,6 +51,12 @@ impl From for StoredU32 { } } +impl From for f32 { + fn from(value: StoredU32) -> Self { + value.0 as f32 + } +} + impl From for StoredU32 { fn from(value: usize) -> Self { if value > u32::MAX as usize { @@ -59,6 +72,15 @@ impl CheckedSub for StoredU32 { } } +impl CheckedSub for StoredU32 { + fn checked_sub(self, rhs: usize) -> Option { + if rhs > u32::MAX as usize { + panic!() + } + self.0.checked_sub(rhs as u32).map(Self) + } +} + impl Div for StoredU32 { type Output = Self; fn div(self, rhs: usize) -> Self::Output { @@ -79,6 +101,17 @@ impl AddAssign for StoredU32 { } } +impl Mul for StoredU32 { + type Output = Self; + fn mul(self, rhs: usize) -> Self::Output { + let res = self.0 as usize * rhs; + if res > u32::MAX as usize { + panic!() + } + Self::from(res) + } +} + impl From for StoredU32 { fn from(value: f64) -> Self { if value < 0.0 || value > u32::MAX as f64 { diff --git a/crates/brk_structs/src/structs/stored_u64.rs b/crates/brk_structs/src/structs/stored_u64.rs index 7b66e4406..8972dade8 100644 --- a/crates/brk_structs/src/structs/stored_u64.rs +++ b/crates/brk_structs/src/structs/stored_u64.rs @@ -1,5 +1,6 @@ use std::ops::{Add, AddAssign, Div}; +use allocative::Allocative; use derive_deref::Deref; use serde::Serialize; use vecdb::{CheckedSub, Printable, StoredCompressed}; @@ -28,6 +29,7 @@ use super::{ KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct StoredU64(u64); diff --git a/crates/brk_structs/src/structs/timestamp.rs b/crates/brk_structs/src/structs/timestamp.rs index b10ffe765..dc1c41829 100644 --- a/crates/brk_structs/src/structs/timestamp.rs +++ b/crates/brk_structs/src/structs/timestamp.rs @@ -1,5 +1,6 @@ use std::ops::{Add, AddAssign, Div}; +use allocative::Allocative; use derive_deref::Deref; use jiff::{civil::date, tz::TimeZone}; use serde::Serialize; @@ -23,6 +24,7 @@ use super::Date; KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct Timestamp(u32); diff --git a/crates/brk_structs/src/structs/txindex.rs b/crates/brk_structs/src/structs/txindex.rs index 014f76c6e..25df3f5cd 100644 --- a/crates/brk_structs/src/structs/txindex.rs +++ b/crates/brk_structs/src/structs/txindex.rs @@ -1,5 +1,6 @@ use std::ops::{Add, AddAssign}; +use allocative::Allocative; use byteview::ByteView; use derive_deref::{Deref, DerefMut}; use serde::Serialize; @@ -27,6 +28,7 @@ use super::StoredU32; KnownLayout, Serialize, StoredCompressed, + Allocative, )] pub struct TxIndex(u32); diff --git a/crates/brk_structs/src/structs/weekindex.rs b/crates/brk_structs/src/structs/weekindex.rs index 6eb6a9659..aaafc5c41 100644 --- a/crates/brk_structs/src/structs/weekindex.rs +++ b/crates/brk_structs/src/structs/weekindex.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Div}, }; +use allocative::Allocative; use serde::{Deserialize, Serialize}; use vecdb::{CheckedSub, Printable, StoredCompressed}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; @@ -25,6 +26,7 @@ use super::{Date, DateIndex}; IntoBytes, KnownLayout, StoredCompressed, + Allocative, )] pub struct WeekIndex(u16); diff --git a/crates/brk_structs/src/structs/weight.rs b/crates/brk_structs/src/structs/weight.rs index 10cf78eab..fc1943dbc 100644 --- a/crates/brk_structs/src/structs/weight.rs +++ b/crates/brk_structs/src/structs/weight.rs @@ -1,5 +1,6 @@ use std::ops::{Add, AddAssign, Div}; +use allocative::Allocative; use derive_deref::Deref; use serde::Serialize; use vecdb::StoredCompressed; @@ -20,6 +21,7 @@ use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; FromBytes, Serialize, StoredCompressed, + Allocative, )] pub struct Weight(u64); diff --git a/crates/brk_structs/src/structs/yearindex.rs b/crates/brk_structs/src/structs/yearindex.rs index 9b65e9af3..22d66d13a 100644 --- a/crates/brk_structs/src/structs/yearindex.rs +++ b/crates/brk_structs/src/structs/yearindex.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Div}, }; +use allocative::Allocative; use serde::{Deserialize, Serialize}; use vecdb::{CheckedSub, Printable, StoredCompressed}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; @@ -25,6 +26,7 @@ use super::{Date, DateIndex, MonthIndex}; IntoBytes, KnownLayout, StoredCompressed, + Allocative, )] pub struct YearIndex(u16); diff --git a/websites/bitview/index.html b/websites/bitview/index.html index 4bbdb0721..db9641fea 100644 --- a/websites/bitview/index.html +++ b/websites/bitview/index.html @@ -1266,7 +1266,7 @@ } td { - text-transform: lowercase; + text-transform: none; } a { diff --git a/websites/bitview/scripts/main.js b/websites/bitview/scripts/main.js index 9652ed813..38e08e3a5 100644 --- a/websites/bitview/scripts/main.js +++ b/websites/bitview/scripts/main.js @@ -6,7 +6,8 @@ * @import * as _ from "../packages/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.d.ts" * @import { SerializedChartableIndex } from "./chart"; * @import { Signal, Signals, Accessor } from "../packages/solidjs-signals/wrapper"; - * @import { DateIndex, DecadeIndex, DifficultyEpoch, Index, HalvingEpoch, Height, MonthIndex, P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2MSOutputIndex, P2AAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex, TxIndex, InputIndex, OutputIndex, VecId, WeekIndex, SemesterIndex, YearIndex, VecIdToIndexes, QuarterIndex, EmptyOutputIndex, OpReturnIndex, UnknownOutputIndex, EmptyAddressIndex, LoadedAddressIndex } from "./vecid-to-indexes" + * @import { DateIndex, DecadeIndex, DifficultyEpoch, Index, HalvingEpoch, Height, MonthIndex, P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2MSOutputIndex, P2AAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex, TxIndex, InputIndex, OutputIndex, VecId, WeekIndex, SemesterIndex, YearIndex, VecIdToIndexes, QuarterIndex, EmptyOutputIndex, OpReturnIndex, UnknownOutputIndex, EmptyAddressIndex, LoadedAddressIndex } from "./bridge/vecs" + * @import { Pools, Pool} from "./bridge/pools" */ /** @@ -51,6 +52,7 @@ * "constant" | * "vB" | * "block" | + * "id" | * "address data" | * "sd" | * "Epoch" | @@ -842,6 +844,7 @@ function createUtils() { (id.endsWith("count") || id.includes("_count_") || id.startsWith("block_count") || + id.includes("blocks_mined") || (id.includes("tx_v") && !id.includes("vsize"))) ) { setUnit("Count"); @@ -852,6 +855,9 @@ function createUtils() { ) { setUnit("H/s"); } + if ((!unit || thoroughUnitCheck) && id === "pool") { + setUnit("id"); + } if ((!unit || thoroughUnitCheck) && id.includes("fee_rate")) { setUnit("sat/vB"); } @@ -2005,7 +2011,8 @@ function initWebSockets(signals, utils) { function main() { const optionsPromise = import("./options.js"); - const vecidToIndexesPromise = import("./vecid-to-indexes.js"); + const vecsPromise = import("./bridge/vecs.js"); + const poolsPromise = import("./bridge/pools.js"); const packages = initPackages(); const env = initEnv(); const utils = createUtils(); @@ -2117,639 +2124,648 @@ function main() { } createKeyDownEventListener(); - packages.signals().then((signals) => - vecidToIndexesPromise.then(({ createVecIdToIndexes, VERSION }) => - optionsPromise.then(async ({ initOptions }) => - signals.createRoot(() => { - const owner = signals.getOwner(); + Promise.all([ + packages.signals(), + vecsPromise, + poolsPromise, + optionsPromise, + ]).then( + ([ + signals, + { createVecIdToIndexes, VERSION }, + { createPools }, + { initOptions }, + ]) => + signals.createRoot(() => { + const owner = signals.getOwner(); - console.log(`VERSION = ${VERSION}`); + console.log(`VERSION = ${VERSION}`); - const vecIdToIndexes = createVecIdToIndexes(); + const vecIdToIndexes = createVecIdToIndexes(); - if (env.localhost) { - Object.keys(vecIdToIndexes).forEach((id) => { - utils.vecidToUnit(/** @type {VecId} */ (id)); - }); - } + if (env.localhost) { + Object.keys(vecIdToIndexes).forEach((id) => { + utils.vecidToUnit(/** @type {VecId} */ (id)); + }); + } - function initDark() { - const preferredColorSchemeMatchMedia = window.matchMedia( - "(prefers-color-scheme: dark)", - ); - const dark = signals.createSignal( - preferredColorSchemeMatchMedia.matches, - ); - preferredColorSchemeMatchMedia.addEventListener( - "change", - ({ matches }) => { - dark.set(matches); + function initDark() { + const preferredColorSchemeMatchMedia = window.matchMedia( + "(prefers-color-scheme: dark)", + ); + const dark = signals.createSignal( + preferredColorSchemeMatchMedia.matches, + ); + preferredColorSchemeMatchMedia.addEventListener( + "change", + ({ matches }) => { + dark.set(matches); + }, + ); + return dark; + } + const dark = initDark(); + + const qrcode = signals.createSignal( + /** @type {string | null} */ (null), + ); + + function createLastHeightResource() { + const lastHeight = signals.createSignal(0); + function fetchLastHeight() { + utils.api.fetchLast( + (h) => { + lastHeight.set(h); }, + /** @satisfies {Height} */ (5), + "height", ); - return dark; } - const dark = initDark(); + fetchLastHeight(); + setInterval(fetchLastHeight, 10_000); + return lastHeight; + } + const lastHeight = createLastHeightResource(); - const qrcode = signals.createSignal( - /** @type {string | null} */ (null), - ); + const webSockets = initWebSockets(signals, utils); - function createLastHeightResource() { - const lastHeight = signals.createSignal(0); - function fetchLastHeight() { - utils.api.fetchLast( - (h) => { - lastHeight.set(h); - }, - /** @satisfies {Height} */ (5), - "height", - ); - } - fetchLastHeight(); - setInterval(fetchLastHeight, 10_000); - return lastHeight; - } - const lastHeight = createLastHeightResource(); + const vecsResources = createVecsResources( + signals, + utils, + env, + vecIdToIndexes, + ); - const webSockets = initWebSockets(signals, utils); + const pools = createPools(); - const vecsResources = createVecsResources( - signals, - utils, - env, - vecIdToIndexes, - ); + const colors = createColors(dark, elements); - const colors = createColors(dark, elements); + const options = initOptions({ + colors, + env, + signals, + utils, + qrcode, + vecIdToIndexes, + pools, + }); - const options = initOptions({ - colors, - env, - signals, - utils, - qrcode, - vecIdToIndexes, - }); + window.addEventListener("popstate", (event) => { + const path = window.document.location.pathname + .split("/") + .filter((v) => v); + let folder = options.tree; - window.addEventListener("popstate", (event) => { - const path = window.document.location.pathname - .split("/") - .filter((v) => v); - let folder = options.tree; - - while (path.length) { - const id = path.shift(); - const res = folder.find((v) => id === utils.stringToId(v.name)); - if (!res) throw "Option not found"; - if (path.length >= 1) { - if (!("tree" in res)) { - throw "Unreachable"; - } - folder = res.tree; - } else { - if ("tree" in res) { - throw "Unreachable"; - } - options.selected.set(res); + while (path.length) { + const id = path.shift(); + const res = folder.find((v) => id === utils.stringToId(v.name)); + if (!res) throw "Option not found"; + if (path.length >= 1) { + if (!("tree" in res)) { + throw "Unreachable"; } + folder = res.tree; + } else { + if ("tree" in res) { + throw "Unreachable"; + } + options.selected.set(res); } - }); + } + }); - function initSelected() { - let firstRun = true; - function initSelectedFrame() { - if (!firstRun) throw Error("Unreachable"); - firstRun = false; + function initSelected() { + let firstRun = true; + function initSelectedFrame() { + if (!firstRun) throw Error("Unreachable"); + firstRun = false; - const owner = signals.getOwner(); + const owner = signals.getOwner(); - const chartOption = signals.createSignal( - /** @type {ChartOption | null} */ (null), - ); - const simOption = signals.createSignal( - /** @type {SimulationOption | null} */ (null), - ); + const chartOption = signals.createSignal( + /** @type {ChartOption | null} */ (null), + ); + const simOption = signals.createSignal( + /** @type {SimulationOption | null} */ (null), + ); - let previousElement = /** @type {HTMLElement | undefined} */ ( - undefined - ); - let firstTimeLoadingChart = true; - let firstTimeLoadingTable = true; - let firstTimeLoadingSimulation = true; - let firstTimeLoadingExplorer = true; + let previousElement = /** @type {HTMLElement | undefined} */ ( + undefined + ); + let firstTimeLoadingChart = true; + let firstTimeLoadingTable = true; + let firstTimeLoadingSimulation = true; + let firstTimeLoadingExplorer = true; - signals.createEffect(options.selected, (option) => { - /** @type {HTMLElement} */ - let element; + signals.createEffect(options.selected, (option) => { + /** @type {HTMLElement} */ + let element; - switch (option.kind) { - case "explorer": { - element = elements.explorer; + switch (option.kind) { + case "explorer": { + element = elements.explorer; - if (firstTimeLoadingExplorer) { - const lightweightCharts = packages.lightweightCharts(); - import("./explorer.js").then(({ init }) => - lightweightCharts.then((lightweightCharts) => - signals.runWithOwner(owner, () => - init({ - colors, - elements, - lightweightCharts, - option: /** @type {Accessor} */ ( - chartOption - ), - signals, - utils, - webSockets, - vecsResources, - vecIdToIndexes, - }), - ), - ), - ); - } - firstTimeLoadingExplorer = false; - - break; - } - case "chart": { - element = elements.charts; - - chartOption.set(option); - - if (firstTimeLoadingChart) { - const lightweightCharts = packages.lightweightCharts(); - import("./chart.js").then(({ init: initChartsElement }) => - lightweightCharts.then((lightweightCharts) => - signals.runWithOwner(owner, () => - initChartsElement({ - colors, - elements, - lightweightCharts, - option: /** @type {Accessor} */ ( - chartOption - ), - env, - signals, - utils, - webSockets, - vecsResources, - vecIdToIndexes, - packages, - }), - ), - ), - ); - } - firstTimeLoadingChart = false; - - break; - } - case "table": { - element = elements.table; - - if (firstTimeLoadingTable) { - import("./table.js").then(({ init }) => + if (firstTimeLoadingExplorer) { + const lightweightCharts = packages.lightweightCharts(); + import("./explorer.js").then(({ init }) => + lightweightCharts.then((lightweightCharts) => signals.runWithOwner(owner, () => init({ + colors, elements, + lightweightCharts, + option: /** @type {Accessor} */ ( + chartOption + ), signals, utils, + webSockets, vecsResources, - option, vecIdToIndexes, }), ), - ); - } - firstTimeLoadingTable = false; - - break; + ), + ); } - case "simulation": { - element = elements.simulation; + firstTimeLoadingExplorer = false; - simOption.set(option); + break; + } + case "chart": { + element = elements.charts; - if (firstTimeLoadingSimulation) { - const lightweightCharts = packages.lightweightCharts(); - import("./simulation.js").then(({ init }) => - lightweightCharts.then((lightweightCharts) => - signals.runWithOwner(owner, () => - init({ - colors, - elements, - lightweightCharts, - signals, - utils, - vecsResources, - }), - ), + chartOption.set(option); + + if (firstTimeLoadingChart) { + const lightweightCharts = packages.lightweightCharts(); + import("./chart.js").then(({ init: initChartsElement }) => + lightweightCharts.then((lightweightCharts) => + signals.runWithOwner(owner, () => + initChartsElement({ + colors, + elements, + lightweightCharts, + option: /** @type {Accessor} */ ( + chartOption + ), + env, + signals, + utils, + webSockets, + vecsResources, + vecIdToIndexes, + packages, + }), ), - ); - } - firstTimeLoadingSimulation = false; - - break; + ), + ); } - case "url": { - return; + firstTimeLoadingChart = false; + + break; + } + case "table": { + element = elements.table; + + if (firstTimeLoadingTable) { + import("./table.js").then(({ init }) => + signals.runWithOwner(owner, () => + init({ + elements, + signals, + utils, + vecsResources, + option, + vecIdToIndexes, + }), + ), + ); } + firstTimeLoadingTable = false; + + break; } + case "simulation": { + element = elements.simulation; - if (element !== previousElement) { - if (previousElement) previousElement.hidden = true; - element.hidden = false; + simOption.set(option); + + if (firstTimeLoadingSimulation) { + const lightweightCharts = packages.lightweightCharts(); + import("./simulation.js").then(({ init }) => + lightweightCharts.then((lightweightCharts) => + signals.runWithOwner(owner, () => + init({ + colors, + elements, + lightweightCharts, + signals, + utils, + vecsResources, + }), + ), + ), + ); + } + firstTimeLoadingSimulation = false; + + break; } - - if (!previousElement) { - utils.url.replaceHistory({ pathname: option.path }); + case "url": { + return; } + } - previousElement = element; - }); - } + if (element !== previousElement) { + if (previousElement) previousElement.hidden = true; + element.hidden = false; + } - function createMobileSwitchEffect() { - let firstRun = true; - signals.createEffect(options.selected, () => { - if (!firstRun && !utils.dom.isHidden(elements.asideLabel)) { - elements.asideLabel.click(); - } - firstRun = false; - }); - } - createMobileSwitchEffect(); + if (!previousElement) { + utils.url.replaceHistory({ pathname: option.path }); + } - utils.dom.onFirstIntersection(elements.aside, () => - signals.runWithOwner(owner, initSelectedFrame), - ); + previousElement = element; + }); } - initSelected(); - utils.dom.onFirstIntersection(elements.nav, async () => { - options.parent.set(elements.nav); + function createMobileSwitchEffect() { + let firstRun = true; + signals.createEffect(options.selected, () => { + if (!firstRun && !utils.dom.isHidden(elements.asideLabel)) { + elements.asideLabel.click(); + } + firstRun = false; + }); + } + createMobileSwitchEffect(); - const option = options.selected(); - if (!option) throw "Selected should be set by now"; - const path = [...option.path]; + utils.dom.onFirstIntersection(elements.aside, () => + signals.runWithOwner(owner, initSelectedFrame), + ); + } + initSelected(); - /** @type {HTMLUListElement | null} */ - let ul = /** @type {any} */ (null); - async function getFirstChild() { - try { - ul = /** @type {HTMLUListElement} */ ( - elements.nav.firstElementChild - ); - await utils.next(); - if (!ul) { - await getFirstChild(); - } - } catch (_) { - await utils.next(); + utils.dom.onFirstIntersection(elements.nav, async () => { + options.parent.set(elements.nav); + + const option = options.selected(); + if (!option) throw "Selected should be set by now"; + const path = [...option.path]; + + /** @type {HTMLUListElement | null} */ + let ul = /** @type {any} */ (null); + async function getFirstChild() { + try { + ul = /** @type {HTMLUListElement} */ ( + elements.nav.firstElementChild + ); + await utils.next(); + if (!ul) { await getFirstChild(); } + } catch (_) { + await utils.next(); + await getFirstChild(); } - await getFirstChild(); - if (!ul) throw Error("Unreachable"); + } + await getFirstChild(); + if (!ul) throw Error("Unreachable"); - let i = 0; - while (path.length > 1) { - const name = path.shift(); - if (!name) throw "Unreachable"; - /** @type {HTMLDetailsElement[]} */ - let detailsList = []; - while (!detailsList.length) { - detailsList = Array.from( - ul.querySelectorAll(":scope > li > details"), - ); - if (!detailsList.length) { - await utils.next(); - } - } - const details = detailsList.find((s) => s.dataset.name == name); - if (!details) return; - details.open = true; - ul = null; - while (!ul) { - const uls = /** @type {HTMLUListElement[]} */ ( - Array.from(details.querySelectorAll(":scope > ul")) - ); - if (!uls.length) { - await utils.next(); - } else if (uls.length > 1) { - throw "Shouldn't be possible"; - } else { - ul = /** @type {HTMLUListElement} */ (uls.pop()); - } - } - } - /** @type {HTMLAnchorElement[]} */ - let anchors = []; - while (!anchors.length) { - anchors = Array.from(ul.querySelectorAll(":scope > li > a")); - if (!anchors.length) { + let i = 0; + while (path.length > 1) { + const name = path.shift(); + if (!name) throw "Unreachable"; + /** @type {HTMLDetailsElement[]} */ + let detailsList = []; + while (!detailsList.length) { + detailsList = Array.from( + ul.querySelectorAll(":scope > li > details"), + ); + if (!detailsList.length) { await utils.next(); } } - anchors - .find( - (a) => - a.getAttribute("href") == window.document.location.pathname, - ) - ?.scrollIntoView({ - behavior: "instant", - block: "center", - }); - }); + const details = detailsList.find((s) => s.dataset.name == name); + if (!details) return; + details.open = true; + ul = null; + while (!ul) { + const uls = /** @type {HTMLUListElement[]} */ ( + Array.from(details.querySelectorAll(":scope > ul")) + ); + if (!uls.length) { + await utils.next(); + } else if (uls.length > 1) { + throw "Shouldn't be possible"; + } else { + ul = /** @type {HTMLUListElement} */ (uls.pop()); + } + } + } + /** @type {HTMLAnchorElement[]} */ + let anchors = []; + while (!anchors.length) { + anchors = Array.from(ul.querySelectorAll(":scope > li > a")); + if (!anchors.length) { + await utils.next(); + } + } + anchors + .find( + (a) => + a.getAttribute("href") == window.document.location.pathname, + ) + ?.scrollIntoView({ + behavior: "instant", + block: "center", + }); + }); - utils.dom.onFirstIntersection(elements.search, () => { - console.log("search: init"); + utils.dom.onFirstIntersection(elements.search, () => { + console.log("search: init"); - const haystack = options.list.map((option) => option.title); + const haystack = options.list.map((option) => option.title); - const RESULTS_PER_PAGE = 100; + const RESULTS_PER_PAGE = 100; - packages.ufuzzy().then((ufuzzy) => { - /** - * @param {uFuzzy.SearchResult} searchResult - * @param {number} pageIndex - */ - function computeResultPage(searchResult, pageIndex) { - /** @type {{ option: Option, title: string }[]} */ - let list = []; + packages.ufuzzy().then((ufuzzy) => { + /** + * @param {uFuzzy.SearchResult} searchResult + * @param {number} pageIndex + */ + function computeResultPage(searchResult, pageIndex) { + /** @type {{ option: Option, title: string }[]} */ + let list = []; - let [indexes, info, order] = searchResult || [null, null, null]; + let [indexes, info, order] = searchResult || [null, null, null]; - const minIndex = pageIndex * RESULTS_PER_PAGE; + const minIndex = pageIndex * RESULTS_PER_PAGE; - if (indexes?.length) { - const maxIndex = Math.min( - (order || indexes).length - 1, - minIndex + RESULTS_PER_PAGE - 1, - ); + if (indexes?.length) { + const maxIndex = Math.min( + (order || indexes).length - 1, + minIndex + RESULTS_PER_PAGE - 1, + ); - list = Array(maxIndex - minIndex + 1); + list = Array(maxIndex - minIndex + 1); - for (let i = minIndex; i <= maxIndex; i++) { - let index = indexes[i]; + for (let i = minIndex; i <= maxIndex; i++) { + let index = indexes[i]; - const title = haystack[index]; + const title = haystack[index]; - list[i % 100] = { - option: options.list[index], - title, - }; - } + list[i % 100] = { + option: options.list[index], + title, + }; } - - return list; } - /** @type {uFuzzy.Options} */ - const config = { - intraIns: Infinity, - intraChars: `[a-z\d' ]`, - }; + return list; + } - const fuzzyMultiInsert = /** @type {uFuzzy} */ ( - ufuzzy({ - intraIns: 1, - }) - ); - const fuzzyMultiInsertFuzzier = /** @type {uFuzzy} */ ( - ufuzzy(config) - ); - const fuzzySingleError = /** @type {uFuzzy} */ ( - ufuzzy({ - intraMode: 1, - ...config, - }) - ); - const fuzzySingleErrorFuzzier = /** @type {uFuzzy} */ ( - ufuzzy({ - intraMode: 1, - ...config, - }) - ); + /** @type {uFuzzy.Options} */ + const config = { + intraIns: Infinity, + intraChars: `[a-z\d' ]`, + }; - /** @type {VoidFunction | undefined} */ - let dispose; + const fuzzyMultiInsert = /** @type {uFuzzy} */ ( + ufuzzy({ + intraIns: 1, + }) + ); + const fuzzyMultiInsertFuzzier = /** @type {uFuzzy} */ ( + ufuzzy(config) + ); + const fuzzySingleError = /** @type {uFuzzy} */ ( + ufuzzy({ + intraMode: 1, + ...config, + }) + ); + const fuzzySingleErrorFuzzier = /** @type {uFuzzy} */ ( + ufuzzy({ + intraMode: 1, + ...config, + }) + ); - function inputEvent() { - signals.createRoot((_dispose) => { - const needle = /** @type {string} */ ( - elements.searchInput.value + /** @type {VoidFunction | undefined} */ + let dispose; + + function inputEvent() { + signals.createRoot((_dispose) => { + const needle = /** @type {string} */ ( + elements.searchInput.value + ); + + dispose?.(); + + dispose = _dispose; + + elements.searchResults.scrollTo({ + top: 0, + }); + + if (!needle) { + elements.searchResults.innerHTML = ""; + return; + } + + const outOfOrder = 5; + const infoThresh = 5_000; + + let result = fuzzyMultiInsert?.search( + haystack, + needle, + undefined, + infoThresh, + ); + + if (!result?.[0]?.length || !result?.[1]) { + result = fuzzyMultiInsert?.search( + haystack, + needle, + outOfOrder, + infoThresh, ); + } - dispose?.(); + if (!result?.[0]?.length || !result?.[1]) { + result = fuzzySingleError?.search( + haystack, + needle, + outOfOrder, + infoThresh, + ); + } - dispose = _dispose; + if (!result?.[0]?.length || !result?.[1]) { + result = fuzzySingleErrorFuzzier?.search( + haystack, + needle, + outOfOrder, + infoThresh, + ); + } - elements.searchResults.scrollTo({ - top: 0, - }); - - if (!needle) { - elements.searchResults.innerHTML = ""; - return; - } - - const outOfOrder = 5; - const infoThresh = 5_000; - - let result = fuzzyMultiInsert?.search( + if (!result?.[0]?.length || !result?.[1]) { + result = fuzzyMultiInsertFuzzier?.search( haystack, needle, undefined, infoThresh, ); + } - if (!result?.[0]?.length || !result?.[1]) { - result = fuzzyMultiInsert?.search( - haystack, - needle, - outOfOrder, - infoThresh, - ); - } + if (!result?.[0]?.length || !result?.[1]) { + result = fuzzyMultiInsertFuzzier?.search( + haystack, + needle, + outOfOrder, + infoThresh, + ); + } - if (!result?.[0]?.length || !result?.[1]) { - result = fuzzySingleError?.search( - haystack, - needle, - outOfOrder, - infoThresh, - ); - } + elements.searchResults.innerHTML = ""; - if (!result?.[0]?.length || !result?.[1]) { - result = fuzzySingleErrorFuzzier?.search( - haystack, - needle, - outOfOrder, - infoThresh, - ); - } + const list = computeResultPage(result, 0); - if (!result?.[0]?.length || !result?.[1]) { - result = fuzzyMultiInsertFuzzier?.search( - haystack, - needle, - undefined, - infoThresh, - ); - } + list.forEach(({ option, title }) => { + const li = window.document.createElement("li"); + elements.searchResults.appendChild(li); - if (!result?.[0]?.length || !result?.[1]) { - result = fuzzyMultiInsertFuzzier?.search( - haystack, - needle, - outOfOrder, - infoThresh, - ); - } - - elements.searchResults.innerHTML = ""; - - const list = computeResultPage(result, 0); - - list.forEach(({ option, title }) => { - const li = window.document.createElement("li"); - elements.searchResults.appendChild(li); - - const element = options.createOptionElement({ - option, - frame: "search", - name: title, - qrcode, - }); - - if (element) { - li.append(element); - } + const element = options.createOptionElement({ + option, + frame: "search", + name: title, + qrcode, }); + + if (element) { + li.append(element); + } }); - } + }); + } - if (elements.searchInput.value) { - inputEvent(); - } + if (elements.searchInput.value) { + inputEvent(); + } - elements.searchInput.addEventListener("input", inputEvent); - }); + elements.searchInput.addEventListener("input", inputEvent); + }); + }); + + function initShare() { + const shareDiv = utils.dom.getElementById("share-div"); + const shareContentDiv = utils.dom.getElementById("share-content-div"); + + shareDiv.addEventListener("click", () => { + qrcode.set(null); }); - function initShare() { - const shareDiv = utils.dom.getElementById("share-div"); - const shareContentDiv = - utils.dom.getElementById("share-content-div"); + shareContentDiv.addEventListener("click", (event) => { + event.stopPropagation(); + event.preventDefault(); + }); - shareDiv.addEventListener("click", () => { - qrcode.set(null); - }); + packages.leanQr().then(({ generate }) => + signals.runWithOwner(owner, () => { + const imgQrcode = /** @type {HTMLImageElement} */ ( + utils.dom.getElementById("share-img") + ); - shareContentDiv.addEventListener("click", (event) => { - event.stopPropagation(); - event.preventDefault(); - }); + const anchor = /** @type {HTMLAnchorElement} */ ( + utils.dom.getElementById("share-anchor") + ); - packages.leanQr().then(({ generate }) => - signals.runWithOwner(owner, () => { - const imgQrcode = /** @type {HTMLImageElement} */ ( - utils.dom.getElementById("share-img") - ); - - const anchor = /** @type {HTMLAnchorElement} */ ( - utils.dom.getElementById("share-anchor") - ); - - signals.createEffect(qrcode, (qrcode) => { - if (!qrcode) { - shareDiv.hidden = true; - return; - } - - const href = qrcode; - anchor.href = href; - anchor.innerText = - (href.startsWith("http") - ? href.split("//").at(-1) - : href.split(":").at(-1)) || ""; - - imgQrcode.src = - generate(/** @type {any} */ (href))?.toDataURL({ - // @ts-ignore - padX: 0, - padY: 0, - }) || ""; - - shareDiv.hidden = false; - }); - }), - ); - } - initShare(); - - function initDesktopResizeBar() { - const resizeBar = utils.dom.getElementById("resize-bar"); - let resize = false; - let startingWidth = 0; - let startingClientX = 0; - - const barWidthLocalStorageKey = "bar-width"; - - /** - * @param {number | null} width - */ - function setBarWidth(width) { - // TODO: Check if should be a signal ?? - try { - if (typeof width === "number") { - elements.main.style.width = `${width}px`; - utils.storage.write(barWidthLocalStorageKey, String(width)); - } else { - elements.main.style.width = elements.style.getPropertyValue( - "--default-main-width", - ); - utils.storage.remove(barWidthLocalStorageKey); + signals.createEffect(qrcode, (qrcode) => { + if (!qrcode) { + shareDiv.hidden = true; + return; } - } catch (_) {} - } - /** - * @param {MouseEvent} event - */ - function mouseMoveEvent(event) { - if (resize) { - setBarWidth(startingWidth + (event.clientX - startingClientX)); + const href = qrcode; + anchor.href = href; + anchor.innerText = + (href.startsWith("http") + ? href.split("//").at(-1) + : href.split(":").at(-1)) || ""; + + imgQrcode.src = + generate(/** @type {any} */ (href))?.toDataURL({ + // @ts-ignore + padX: 0, + padY: 0, + }) || ""; + + shareDiv.hidden = false; + }); + }), + ); + } + initShare(); + + function initDesktopResizeBar() { + const resizeBar = utils.dom.getElementById("resize-bar"); + let resize = false; + let startingWidth = 0; + let startingClientX = 0; + + const barWidthLocalStorageKey = "bar-width"; + + /** + * @param {number | null} width + */ + function setBarWidth(width) { + // TODO: Check if should be a signal ?? + try { + if (typeof width === "number") { + elements.main.style.width = `${width}px`; + utils.storage.write(barWidthLocalStorageKey, String(width)); + } else { + elements.main.style.width = elements.style.getPropertyValue( + "--default-main-width", + ); + utils.storage.remove(barWidthLocalStorageKey); } - } - - resizeBar.addEventListener("mousedown", (event) => { - startingClientX = event.clientX; - startingWidth = elements.main.clientWidth; - resize = true; - window.document.documentElement.dataset.resize = ""; - window.addEventListener("mousemove", mouseMoveEvent); - }); - - resizeBar.addEventListener("dblclick", () => { - setBarWidth(null); - }); - - const setResizeFalse = () => { - resize = false; - delete window.document.documentElement.dataset.resize; - window.removeEventListener("mousemove", mouseMoveEvent); - }; - window.addEventListener("mouseup", setResizeFalse); - window.addEventListener("mouseleave", setResizeFalse); + } catch (_) {} } - initDesktopResizeBar(); - }), - ), - ), + + /** + * @param {MouseEvent} event + */ + function mouseMoveEvent(event) { + if (resize) { + setBarWidth(startingWidth + (event.clientX - startingClientX)); + } + } + + resizeBar.addEventListener("mousedown", (event) => { + startingClientX = event.clientX; + startingWidth = elements.main.clientWidth; + resize = true; + window.document.documentElement.dataset.resize = ""; + window.addEventListener("mousemove", mouseMoveEvent); + }); + + resizeBar.addEventListener("dblclick", () => { + setBarWidth(null); + }); + + const setResizeFalse = () => { + resize = false; + delete window.document.documentElement.dataset.resize; + window.removeEventListener("mousemove", mouseMoveEvent); + }; + window.addEventListener("mouseup", setResizeFalse); + window.addEventListener("mouseleave", setResizeFalse); + } + initDesktopResizeBar(); + }), ); } main(); diff --git a/websites/bitview/scripts/options.js b/websites/bitview/scripts/options.js index 0733e589b..c09ca5c3f 100644 --- a/websites/bitview/scripts/options.js +++ b/websites/bitview/scripts/options.js @@ -124,9 +124,10 @@ * @param {Env} args.env * @param {Colors} args.colors * @param {VecIdToIndexes} args.vecIdToIndexes + * @param {Pools} args.pools * @returns {PartialOptionsTree} */ -function createPartialOptions({ env, colors, vecIdToIndexes }) { +function createPartialOptions({ env, colors, vecIdToIndexes, pools }) { /** * @template {string} S * @typedef {Extract} StartsWith @@ -3343,6 +3344,24 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) { lineStyle: 4, }, }), + createBaseSeries({ + key: "1w_block_count", + name: "1w sum", + color: colors.red, + defaultActive: false, + }), + createBaseSeries({ + key: "1m_block_count", + name: "1m sum", + color: colors.pink, + defaultActive: false, + }), + createBaseSeries({ + key: "1y_block_count", + name: "1y sum", + color: colors.purple, + defaultActive: false, + }), ], }, { @@ -3521,87 +3540,116 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) { ], }, { - name: "Coinbase", - title: "Coinbase", - bottom: [ - ...createBaseAverageSumCumulativeMinMaxPercentilesSeries({ - key: "coinbase", + name: "Rewards", + tree: [ + { name: "Coinbase", - }), - ...createBaseAverageSumCumulativeMinMaxPercentilesSeries({ - key: "coinbase_in_btc", - name: "Coinbase", - }), - ...createBaseAverageSumCumulativeMinMaxPercentilesSeries({ - key: "coinbase_in_usd", - name: "Coinbase", - }), - ], - }, - { - name: "Subsidy", - title: "Subsidy", - bottom: [ - ...createBaseAverageSumCumulativeMinMaxPercentilesSeries({ - key: "subsidy", + title: "Coinbase", + bottom: [ + ...createBaseAverageSumCumulativeMinMaxPercentilesSeries( + { + key: "coinbase", + name: "Coinbase", + }, + ), + ...createBaseAverageSumCumulativeMinMaxPercentilesSeries( + { + key: "coinbase_in_btc", + name: "Coinbase", + }, + ), + ...createBaseAverageSumCumulativeMinMaxPercentilesSeries( + { + key: "coinbase_in_usd", + name: "Coinbase", + }, + ), + ], + }, + { name: "Subsidy", - }), - createBaseSeries({ - key: "subsidy_usd_1y_sma", - name: "1y sma", - }), - ...createBaseAverageSumCumulativeMinMaxPercentilesSeries({ - key: "subsidy_in_btc", - name: "Subsidy", - }), - ...createBaseAverageSumCumulativeMinMaxPercentilesSeries({ - key: "subsidy_in_usd", - name: "Subsidy", - }), - ], - }, - { - name: "Fee", - title: "Transaction Fee", - bottom: [ - ...createAverageSumCumulativeMinMaxPercentilesSeries("fee"), - ...createAverageSumCumulativeMinMaxPercentilesSeries( - "fee_in_btc", - ), - ...createAverageSumCumulativeMinMaxPercentilesSeries( - "fee_in_usd", - ), - ], - }, - { - name: "Dominance", - title: "Reward Dominance", - bottom: [ - createBaseSeries({ - key: "fee_dominance", + title: "Subsidy", + bottom: [ + ...createBaseAverageSumCumulativeMinMaxPercentilesSeries( + { + key: "subsidy", + name: "Subsidy", + }, + ), + createBaseSeries({ + key: "subsidy_usd_1y_sma", + name: "1y sma", + }), + ...createBaseAverageSumCumulativeMinMaxPercentilesSeries( + { + key: "subsidy_in_btc", + name: "Subsidy", + }, + ), + ...createBaseAverageSumCumulativeMinMaxPercentilesSeries( + { + key: "subsidy_in_usd", + name: "Subsidy", + }, + ), + ], + }, + { name: "Fee", - color: colors.amber, - }), - createBaseSeries({ - key: "subsidy_dominance", - name: "Subsidy", - color: colors.red, - }), - ], - }, - { - name: "Unclaimed Rewards", - title: "Unclaimed Rewards", - bottom: [ - ...createSumCumulativeSeries({ - concat: "unclaimed_rewards", - }), - ...createSumCumulativeSeries({ - concat: "unclaimed_rewards_in_btc", - }), - ...createSumCumulativeSeries({ - concat: "unclaimed_rewards_in_usd", - }), + title: "Transaction Fee", + bottom: [ + ...createAverageSumCumulativeMinMaxPercentilesSeries( + "fee", + ), + ...createAverageSumCumulativeMinMaxPercentilesSeries( + "fee_in_btc", + ), + ...createAverageSumCumulativeMinMaxPercentilesSeries( + "fee_in_usd", + ), + ], + }, + { + name: "Dominance", + title: "Reward Dominance", + bottom: [ + createBaseSeries({ + key: "fee_dominance", + name: "Fee", + color: colors.amber, + }), + createBaseSeries({ + key: "subsidy_dominance", + name: "Subsidy", + color: colors.red, + }), + ], + }, + { + name: "Unclaimed", + title: "Unclaimed Rewards", + bottom: [ + ...createSumCumulativeSeries({ + concat: "unclaimed_rewards", + }), + ...createSumCumulativeSeries({ + concat: "unclaimed_rewards_in_btc", + }), + ...createSumCumulativeSeries({ + concat: "unclaimed_rewards_in_usd", + }), + ], + }, + { + name: "Puell multiple", + title: "Puell multiple", + bottom: [ + createBaseSeries({ + key: "puell_multiple", + name: "Multiple", + }), + ], + }, ], }, { @@ -3614,16 +3662,6 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) { }), ], }, - { - name: "Puell multiple", - title: "Puell multiple", - bottom: [ - createBaseSeries({ - key: "puell_multiple", - name: "Multiple", - }), - ], - }, { name: "Difficulty", title: "Difficulty", @@ -3695,6 +3733,165 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) { }), ], }, + { + name: "Pools", + tree: Object.entries(pools).map(([_key, name]) => { + const key = /** @type {Pool} */ (_key); + return { + name, + tree: [ + // indexes_to_dominance: ComputedVecsFromDateIndex, + // indexes_to_1d_dominance: ComputedVecsFromDateIndex, + // indexes_to_1w_dominance: ComputedVecsFromDateIndex, + // indexes_to_1m_dominance: ComputedVecsFromDateIndex, + // indexes_to_1y_dominance: ComputedVecsFromDateIndex, + // indexes_to_days_since_block: ComputedVecsFromDateIndex, + { + name: "Dominance", + title: `Dominance of ${name}`, + bottom: [ + createBaseSeries({ + key: `${key}_1d_dominance`, + name: "1d", + color: colors.rose, + defaultActive: false, + }), + createBaseSeries({ + key: `${key}_1w_dominance`, + name: "1w", + color: colors.red, + defaultActive: false, + }), + createBaseSeries({ + key: `${key}_1m_dominance`, + name: "1m", + }), + createBaseSeries({ + key: `${key}_1y_dominance`, + name: "1y", + color: colors.lime, + defaultActive: false, + }), + createBaseSeries({ + key: `${key}_dominance`, + name: "all time", + color: colors.teal, + defaultActive: false, + }), + ], + }, + { + name: "Blocks mined", + title: `Blocks mined by ${name}`, + bottom: [ + createBaseSeries({ + key: `${key}_blocks_mined`, + name: "Sum", + }), + createBaseSeries({ + key: `${key}_blocks_mined_cumulative`, + name: "Cumulative", + color: colors.blue, + }), + createBaseSeries({ + key: `${key}_1w_blocks_mined`, + name: "1w Sum", + color: colors.red, + defaultActive: false, + }), + createBaseSeries({ + key: `${key}_1m_blocks_mined`, + name: "1m Sum", + color: colors.pink, + defaultActive: false, + }), + createBaseSeries({ + key: `${key}_1y_blocks_mined`, + name: "1y Sum", + color: colors.purple, + defaultActive: false, + }), + ], + }, + { + name: "Rewards", + tree: [ + { + name: "coinbase", + title: `coinbase collected by ${name}`, + bottom: [ + ...createSumCumulativeSeries({ + concat: `${key}_coinbase`, + common: "coinbase", + // cumulativeColor: colors. + }), + ...createSumCumulativeSeries({ + concat: `${key}_coinbase_in_btc`, + common: "coinbase", + // cumulativeColor: colors. + }), + ...createSumCumulativeSeries({ + concat: `${key}_coinbase_in_usd`, + common: "coinbase", + }), + ], + }, + { + name: "subsidy", + title: `subsidy collected by ${name}`, + bottom: [ + ...createSumCumulativeSeries({ + concat: `${key}_subsidy`, + common: "subsidy", + // cumulativeColor: colors. + }), + ...createSumCumulativeSeries({ + concat: `${key}_subsidy_in_btc`, + common: "subsidy", + // cumulativeColor: colors. + }), + ...createSumCumulativeSeries({ + concat: `${key}_subsidy_in_usd`, + common: "subsidy", + }), + ], + }, + { + name: "fees", + title: `fees collected by ${name}`, + bottom: [ + ...createSumCumulativeSeries({ + concat: `${key}_fee`, + common: "fee", + // cumulativeColor: colors. + }), + ...createSumCumulativeSeries({ + concat: `${key}_fee_in_btc`, + common: "fee", + // cumulativeColor: colors. + }), + ...createSumCumulativeSeries({ + concat: `${key}_fee_in_usd`, + common: "fee", + }), + ], + }, + ], + }, + { + name: "Days since block", + title: `Days since ${name} mined a block`, + bottom: [ + createBaseSeries({ + key: `${key}_days_since_block`, + name: "Raw", + }), + ], + }, + ], + }; + }), + }, ], }, { @@ -4184,6 +4381,7 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) { * @param {Env} args.env * @param {Utilities} args.utils * @param {VecIdToIndexes} args.vecIdToIndexes + * @param {Pools} args.pools * @param {Signal} args.qrcode */ export function initOptions({ @@ -4193,6 +4391,7 @@ export function initOptions({ utils, qrcode, vecIdToIndexes, + pools, }) { const LS_SELECTED_KEY = `selected_path`; @@ -4208,7 +4407,12 @@ export function initOptions({ /** @type {Signal