commit 51e78f29e735f8a5bc46f40a41327c3733ca349b Author: Uncle Stinky Date: Mon Nov 18 16:58:38 2024 +0300 initial commit, building blocks for the later usage Signed-off-by: Uncle Stinky diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5549a69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +debug/ +target/ + +**/*.rs.bk +*.pdb diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..80bc746 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2599 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" + +[[package]] +name = "cc" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin 0.9.8", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "ghost-telemetry-common" +version = "0.1.0" +dependencies = [ + "anyhow", + "arrayvec", + "base64 0.21.7", + "bimap", + "bincode", + "bytes", + "flume", + "fnv", + "futures", + "hex", + "http", + "hyper", + "log", + "num-traits", + "pin-project-lite", + "primitive-types", + "rustc-hash", + "serde", + "serde_json", + "sha-1 0.10.1", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "webpki-roots", +] + +[[package]] +name = "ghost-telemetry-core" +version = "0.1.0" +dependencies = [ + "anyhow", + "bimap", + "bincode", + "bytes", + "flume", + "futures", + "ghost-telemetry-common", + "hex", + "http", + "hyper", + "jemallocator", + "log", + "maxminddb", + "num_cpus", + "once_cell", + "parking_lot", + "primitive-types", + "rayon", + "reqwest", + "rustc-hash", + "serde", + "serde_json", + "shellwords", + "simple_logger", + "smallvec", + "soketto", + "structopt", + "thiserror", + "tokio", + "tokio-util", +] + +[[package]] +name = "ghost-telemetry-shard" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "flume", + "futures", + "ghost-telemetry-common", + "hex", + "http", + "hyper", + "jemallocator", + "log", + "num_cpus", + "primitive-types", + "serde", + "serde_json", + "simple_logger", + "soketto", + "structopt", + "thiserror", + "tokio", + "tokio-util", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "ipnetwork" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4088d739b183546b239688ddbc79891831df421773df95e236daf7867866d355" +dependencies = [ + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jemalloc-sys" +version = "0.5.4+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "jemallocator" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0de374a9f8e63150e6f5e8a60cc14c668226d7a347d8aee1a45766e3c4dd3bc" +dependencies = [ + "jemalloc-sys", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "maxminddb" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe2ba61113f9f7a9f0e87c519682d39c43a6f3f79c2cc42c3ba3dda83b1fa334" +dependencies = [ + "ipnetwork", + "log", + "memchr", + "serde", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustix" +version = "0.38.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +dependencies = [ + "log", + "ring 0.16.20", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "shellwords" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e515aa4699a88148ed5ef96413ceef0048ce95b43fbc955a33bde0a70fcae6" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simple_logger" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64 0.13.1", + "bytes", + "futures", + "httparse", + "log", + "rand", + "sha-1 0.9.8", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.87", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..690fa56 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] +resolver = "2" +members = [ + "common", + "telemetry-core", + "telemetry-shard", +] + +[profile.dev] +opt-level = 3 + +[profile.release] +lto = true +panic = "abort" diff --git a/common/Cargo.toml b/common/Cargo.toml new file mode 100644 index 0000000..b7a2a79 --- /dev/null +++ b/common/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "ghost-telemetry-common" +version = "0.1.0" +authors = ["Uncle Stinky uncle.stinky@ghostchain.io"] +edition = "2021" + +[dependencies] +anyhow = "1.0.42" +arrayvec = { version = "0.7.1", features = ["serde"] } +base64 = { version = "0.21", default-features = false, features = ["alloc"] } +bimap = "0.6.1" +bytes = "1.0.1" +flume = "0.10.8" +fnv = "1.0.7" +futures = "0.3.15" +hex = "0.4.3" +http = "0.2.4" +hyper = { version = "0.14.11", features = ["full"] } +log = "0.4.14" +num-traits = "0.2" +pin-project-lite = "0.2.7" +primitive-types = { version = "0.12.1", features = ["serde"] } +rustc-hash = "1.1.0" +serde = { version = "1.0.126", features = ["derive"] } +serde_json = { version = "1.0.64", features = ["raw_value"] } +sha-1 = { version = "0.10.1", default-features = false } +soketto = "0.7.1" +thiserror = "1.0.24" +tokio = { version = "1.10.1", features = ["full"] } +tokio-util = { version = "0.7.4", features = ["compat"] } +tokio-rustls = "0.23.4" +webpki-roots = "0.22.4" + +[dev-dependencies] +bincode = "1.3.3" diff --git a/common/src/assign_id.rs b/common/src/assign_id.rs new file mode 100644 index 0000000..36a16f5 --- /dev/null +++ b/common/src/assign_id.rs @@ -0,0 +1,73 @@ +use bimap::BiMap; +use std::hash::Hash; + +/// A struct that allows you to assign an Id to an arbitrary set of +/// details (so long as they are Eq+Hash+Clone), and then access +/// the assigned Id given those details or access the details given +/// the Id. +/// +/// The Id can be any type that's convertible to/from a `usize`. Using +/// a custom type is recommended for increased type safety. +#[derive(Debug)] +pub struct AssignId { + current_id: usize, + mapping: BiMap, + _id_type: std::marker::PhantomData, +} + +impl AssignId +where + Details: Eq + Hash, + Id: From + Copy, + usize: From, +{ + pub fn new() -> Self { + Self { + current_id: 0, + mapping: BiMap::new(), + _id_type: std::marker::PhantomData, + } + } + + pub fn assign_id(&mut self, details: Details) -> Id { + let this_id = self.current_id; + // It's very unlikely we'll ever overflow the ID limit, but in case we do, + // a wrapping_add will almost certainly be fine: + self.current_id = self.current_id.wrapping_add(1); + self.mapping.insert(this_id, details); + this_id.into() + } + + pub fn get_details(&mut self, id: Id) -> Option<&Details> { + self.mapping.get_by_left(&id.into()) + } + + pub fn get_id(&mut self, details: &Details) -> Option { + self.mapping.get_by_right(details).map(|&id| id.into()) + } + + pub fn remove_by_id(&mut self, id: Id) -> Option
{ + self.mapping + .remove_by_left(&id.into()) + .map(|(_, details)| details) + } + + pub fn remove_by_details(&mut self, details: &Details) -> Option { + self.mapping + .remove_by_right(&details) + .map(|(id, _)| id.into()) + } + + pub fn clear(&mut self) { + // Leave the `current_id` as-is. Why? To avoid reusing IDs and risking + // race conditions where old messages can accidentally screw with new nodes + // that have been assigned the same ID. + self.mapping = BiMap::new(); + } + + pub fn iter(&self) -> impl Iterator { + self.mapping + .iter() + .map(|(&id, details)| (id.into(), details)) + } +} diff --git a/common/src/byte_size.rs b/common/src/byte_size.rs new file mode 100644 index 0000000..d374167 --- /dev/null +++ b/common/src/byte_size.rs @@ -0,0 +1,92 @@ +use anyhow::{anyhow, Error}; + +#[derive(Copy, Clone, Debug)] +pub struct ByteSize(usize); + +impl ByteSize { + pub fn new(bytes: usize) -> ByteSize { + ByteSize(bytes) + } + /// Return the number of bytes stored within. + pub fn num_bytes(self) -> usize { + self.0 + } +} + +impl From for usize { + fn from(b: ByteSize) -> Self { + b.0 + } +} + +impl std::str::FromStr for ByteSize { + type Err = Error; + + fn from_str(s: &str) -> Result { + let s = s.trim(); + match s.find(|c| !char::is_ascii_digit(&c)) { + // No non-numeric chars; assume bytes then + None => Ok(ByteSize(s.parse().expect("all ascii digits"))), + // First non-numeric char + Some(idx) => { + let n = s[..idx].parse().expect("all ascii digits"); + let suffix = s[idx..].trim(); + let n = match suffix { + "B" | "b" => n, + "kB" | "K" | "k" => n * 1000, + "MB" | "M" | "m" => n * 1000 * 1000, + "GB" | "G" | "g" => n * 1000 * 1000 * 1000, + "KiB" | "Ki" => n * 1024, + "MiB" | "Mi" => n * 1024 * 1024, + "GiB" | "Gi" => n * 1024 * 1024 * 1024, + _ => { + return Err(anyhow!( + "\ + Cannot parse into bytes; suffix is '{}', but expecting one of \ + B,b, kB,K,k, MB,M,m, GB,G,g, KiB,Ki, MiB,Mi, GiB,Gi", + suffix + )) + } + }; + Ok(ByteSize(n)) + } + } + } +} + +#[cfg(test)] +mod test { + use crate::byte_size::ByteSize; + + #[test] + fn can_parse_valid_strings() { + let cases = vec![ + ("100", 100), + ("100B", 100), + ("100b", 100), + ("20kB", 20 * 1000), + ("20 kB", 20 * 1000), + ("20K", 20 * 1000), + (" 20k", 20 * 1000), + ("1MB", 1 * 1000 * 1000), + ("1M", 1 * 1000 * 1000), + ("1m", 1 * 1000 * 1000), + ("1 m", 1 * 1000 * 1000), + ("1GB", 1 * 1000 * 1000 * 1000), + ("1G", 1 * 1000 * 1000 * 1000), + ("1g", 1 * 1000 * 1000 * 1000), + ("1KiB", 1 * 1024), + ("1Ki", 1 * 1024), + ("1MiB", 1 * 1024 * 1024), + ("1Mi", 1 * 1024 * 1024), + ("1GiB", 1 * 1024 * 1024 * 1024), + ("1Gi", 1 * 1024 * 1024 * 1024), + (" 1 Gi ", 1 * 1024 * 1024 * 1024), + ]; + + for (s, expected) in cases { + let b: ByteSize = s.parse().unwrap(); + assert_eq!(b.num_bytes(), expected); + } + } +} diff --git a/common/src/dense_map.rs b/common/src/dense_map.rs new file mode 100644 index 0000000..11a51da --- /dev/null +++ b/common/src/dense_map.rs @@ -0,0 +1,149 @@ +/// This stores items in contiguous memory, making a note of free +/// slots when items are removed again so that they can be reused. +/// +/// This is particularly efficient when items are often added and +/// seldom removed. +/// +/// Items are keyed by an Id, which can be any type you wish, but +/// must be convertible to/from a `usize`. This promotes using a +/// custom Id type to talk about items in the map. +pub struct DenseMap { + /// List of retired indexes that can be re-used + retired: Vec, + /// All items + items: Vec>, + /// Our ID type + _id_type: std::marker::PhantomData, +} + +impl DenseMap +where + Id: From + Copy, + usize: From, +{ + pub fn new() -> Self { + DenseMap { + retired: Vec::new(), + items: Vec::new(), + _id_type: std::marker::PhantomData, + } + } + + pub fn add(&mut self, item: T) -> Id { + self.add_with(|_| item) + } + + pub fn as_slice(&self) -> &[Option] { + &self.items + } + + pub fn add_with(&mut self, f: F) -> Id + where + F: FnOnce(Id) -> T, + { + match self.retired.pop() { + Some(id) => { + let id_out = id.into(); + self.items[id] = Some(f(id_out)); + id_out + } + None => { + let id = self.items.len().into(); + self.items.push(Some(f(id))); + id + } + } + } + + pub fn get(&self, id: Id) -> Option<&T> { + let id: usize = id.into(); + self.items.get(id).and_then(|item| item.as_ref()) + } + + pub fn get_mut(&mut self, id: Id) -> Option<&mut T> { + let id: usize = id.into(); + self.items.get_mut(id).and_then(|item| item.as_mut()) + } + + pub fn remove(&mut self, id: Id) -> Option { + let id: usize = id.into(); + let old = self.items.get_mut(id).and_then(|item| item.take()); + + if old.is_some() { + // something was actually removed, so lets add the id to + // the list of retired ids! + self.retired.push(id); + } + + old + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.items + .iter() + .enumerate() + .filter_map(|(id, item)| Some((id.into(), item.as_ref()?))) + } + + pub fn iter_mut(&mut self) -> impl Iterator + '_ { + self.items + .iter_mut() + .enumerate() + .filter_map(|(id, item)| Some((id.into(), item.as_mut()?))) + } + + pub fn into_iter(self) -> impl Iterator { + self.items + .into_iter() + .enumerate() + .filter_map(|(id, item)| Some((id.into(), item?))) + } + + pub fn len(&self) -> usize { + self.items.len() - self.retired.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Return the next Id that will be assigned. + pub fn next_id(&self) -> usize { + match self.retired.last() { + Some(id) => *id, + None => self.items.len(), + } + } +} + +#[cfg(test)] +mod test { + + use super::*; + + #[test] + fn len_doesnt_panic_if_lots_of_ids_are_retired() { + let mut map = DenseMap::::new(); + + let id1 = map.add(1); + let id2 = map.add(2); + let id3 = map.add(3); + + assert_eq!(map.len(), 3); + + map.remove(id1); + map.remove(id2); + + assert_eq!(map.len(), 1); + + map.remove(id3); + + assert_eq!(map.len(), 0); + + map.remove(id1); + map.remove(id1); + map.remove(id1); + + assert_eq!(map.len(), 0); + } +} diff --git a/common/src/either_sink.rs b/common/src/either_sink.rs new file mode 100644 index 0000000..e823fb6 --- /dev/null +++ b/common/src/either_sink.rs @@ -0,0 +1,66 @@ +use futures::sink::Sink; +use pin_project_lite::pin_project; + +pin_project! { + #[project = EitherSinkInner] + pub enum EitherSink { + A { #[pin] inner: A }, + B { #[pin] inner: B } + } +} + +/// A simple enum that delegates implementation to one of +/// the two possible sinks contained within. +impl EitherSink { + pub fn a(val: A) -> Self { + EitherSink::A { inner: val } + } + pub fn b(val: B) -> Self { + EitherSink::B { inner: val } + } +} + +impl Sink for EitherSink +where + A: Sink, + B: Sink, +{ + type Error = Error; + + fn poll_ready( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match self.project() { + EitherSinkInner::A { inner } => inner.poll_ready(cx), + EitherSinkInner::B { inner } => inner.poll_ready(cx), + } + } + + fn start_send(self: std::pin::Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + match self.project() { + EitherSinkInner::A { inner } => inner.start_send(item), + EitherSinkInner::B { inner } => inner.start_send(item), + } + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match self.project() { + EitherSinkInner::A { inner } => inner.poll_flush(cx), + EitherSinkInner::B { inner } => inner.poll_flush(cx), + } + } + + fn poll_close( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match self.project() { + EitherSinkInner::A { inner } => inner.poll_close(cx), + EitherSinkInner::B { inner } => inner.poll_close(cx), + } + } +} diff --git a/common/src/http_utils.rs b/common/src/http_utils.rs new file mode 100644 index 0000000..2cee521 --- /dev/null +++ b/common/src/http_utils.rs @@ -0,0 +1,159 @@ +use futures::io::{BufReader, BufWriter}; +use hyper::server::conn::AddrStream; +use hyper::{Body, Request, Response, Server}; +use std::future::Future; +use std::net::SocketAddr; +use tokio_util::compat::{Compat, TokioAsyncReadCompatExt}; + +/// A convenience function to start up a Hyper server and handle requests. +pub async fn start_server(addr: SocketAddr, handler: H) -> Result<(), anyhow::Error> +where + H: Clone + Send + Sync + 'static + FnMut(SocketAddr, Request) -> F, + F: Send + 'static + Future, anyhow::Error>>, +{ + let service = hyper::service::make_service_fn(move |addr: &AddrStream| { + let mut handler = handler.clone(); + let addr = addr.remote_addr(); + async move { Ok::<_, hyper::Error>(hyper::service::service_fn(move |r| handler(addr, r))) } + }); + let server = Server::bind(&addr).serve(service); + + log::info!("listening on http://{}", server.local_addr()); + server.await?; + + Ok(()) +} + +type WsStream = BufReader>>; +pub type WsSender = soketto::connection::Sender; +pub type WsReceiver = soketto::connection::Receiver; + +/// A convenience function to upgrade a Hyper request into a Soketto Websocket. +pub fn upgrade_to_websocket(req: Request, on_upgrade: H) -> hyper::Response +where + H: 'static + Send + FnOnce(WsSender, WsReceiver) -> F, + F: Send + Future, +{ + if !is_upgrade_request(&req) { + return basic_response(400, "Expecting WebSocket upgrade headers"); + } + + let key = match req.headers().get("Sec-WebSocket-Key") { + Some(key) => key, + None => { + return basic_response( + 400, + "Upgrade to websocket connection failed; Sec-WebSocket-Key header not provided", + ) + } + }; + + if req + .headers() + .get("Sec-WebSocket-Version") + .map(|v| v.as_bytes()) + != Some(b"13") + { + return basic_response( + 400, + "Sec-WebSocket-Version header should have a value of 13", + ); + } + + // Just a little ceremony to return the correct response key: + let mut accept_key_buf = [0; 32]; + let accept_key = generate_websocket_accept_key(key.as_bytes(), &mut accept_key_buf); + + // Tell the client that we accept the upgrade-to-WS request: + let response = Response::builder() + .status(hyper::StatusCode::SWITCHING_PROTOCOLS) + .header(hyper::header::CONNECTION, "upgrade") + .header(hyper::header::UPGRADE, "websocket") + .header("Sec-WebSocket-Accept", accept_key) + .body(Body::empty()) + .expect("bug: failed to build response"); + + // Spawn our handler to work with the WS connection: + tokio::spawn(async move { + // Get our underlying TCP stream: + let stream = match hyper::upgrade::on(req).await { + Ok(stream) => stream, + Err(e) => { + log::error!("Error upgrading connection to websocket: {}", e); + return; + } + }; + + // Start a Soketto server with it: + let server = + soketto::handshake::Server::new(BufReader::new(BufWriter::new(stream.compat()))); + + // Get hold of a way to send and receive messages: + let (sender, receiver) = server.into_builder().finish(); + + // Pass these to our when-upgraded handler: + on_upgrade(sender, receiver).await; + }); + + response +} + +/// A helper to return a basic HTTP response with a code and text body. +fn basic_response(code: u16, msg: impl AsRef) -> Response { + Response::builder() + .status(code) + .body(Body::from(msg.as_ref().to_owned())) + .expect("bug: failed to build response body") +} + +/// Defined in RFC 6455. this is how we convert the Sec-WebSocket-Key in a request into a +/// Sec-WebSocket-Accept that we return in the response. +fn generate_websocket_accept_key<'a>(key: &[u8], buf: &'a mut [u8; 32]) -> &'a [u8] { + // Defined in RFC 6455, we append this to the key to generate the response: + const KEY: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + use sha1::{Digest, Sha1}; + let mut digest = Sha1::new(); + digest.update(key); + digest.update(KEY); + let d = digest.finalize(); + + use base64::{engine::general_purpose, Engine as _}; + let n = general_purpose::STANDARD + .encode_slice(&d, buf) + .expect("Sha1 must fit into [u8; 32]"); + &buf[..n] +} + +/// Check if a request is a websocket upgrade request. +fn is_upgrade_request(request: &hyper::Request) -> bool { + header_contains_value(request.headers(), hyper::header::CONNECTION, b"upgrade") + && header_contains_value(request.headers(), hyper::header::UPGRADE, b"websocket") +} + +/// Check if there is a header of the given name containing the wanted value. +fn header_contains_value( + headers: &hyper::HeaderMap, + header: hyper::header::HeaderName, + value: &[u8], +) -> bool { + pub fn trim(x: &[u8]) -> &[u8] { + let from = match x.iter().position(|x| !x.is_ascii_whitespace()) { + Some(i) => i, + None => return &[], + }; + let to = x.iter().rposition(|x| !x.is_ascii_whitespace()).unwrap(); + &x[from..=to] + } + + for header in headers.get_all(header) { + if header + .as_bytes() + .split(|&c| c == b',') + .any(|x| trim(x).eq_ignore_ascii_case(value)) + { + return true; + } + } + false +} diff --git a/common/src/id_type.rs b/common/src/id_type.rs new file mode 100644 index 0000000..3f208ab --- /dev/null +++ b/common/src/id_type.rs @@ -0,0 +1,74 @@ +/// Define a type that can be used as an ID, be converted from/to the inner type, +/// and serialized/deserialized transparently into the inner type. +#[macro_export] +macro_rules! id_type { + ($( #[$attrs:meta] )* $vis:vis struct $ty:ident ( $inner:ident ) $(;)? ) => { + #[derive(Debug,Clone,Copy,PartialEq,Eq,Hash)] + $( #[$attrs] )* + $vis struct $ty($inner); + + impl $ty { + #[allow(dead_code)] + pub fn new(inner: $inner) -> Self { + Self(inner) + } + } + + impl From<$inner> for $ty { + fn from(inner: $inner) -> Self { + Self(inner) + } + } + + impl From<$ty> for $inner { + fn from(ty: $ty) -> Self { + ty.0 + } + } + } +} + +#[cfg(test)] +mod test { + // Mostly we're just checking that everything compiles OK + // when the macro is used as expected.. + + // A basic definition is possible: + id_type! { + struct Foo(usize) + } + + // We can add a ';' on the end: + id_type! { + struct Bar(usize); + } + + // Visibility qualifiers are allowed: + id_type! { + pub struct Wibble(u64) + } + + // Doc strings are possible + id_type! { + /// We can have doc strings, too + pub(crate) struct Wobble(u16) + } + + // In fact, any attributes can be added (common + // derives are added already): + id_type! { + /// We can have doc strings, too + #[derive(serde::Serialize)] + #[serde(transparent)] + pub(crate) struct Lark(u16) + } + + #[test] + fn create_and_use_new_id_type() { + let _ = Foo::new(123); + let id = Foo::from(123); + let id_num: usize = id.into(); + + assert_eq!(id_num, 123); + } +} diff --git a/common/src/internal_messages.rs b/common/src/internal_messages.rs new file mode 100644 index 0000000..8a0ecd8 --- /dev/null +++ b/common/src/internal_messages.rs @@ -0,0 +1,51 @@ +//! Internal messages passed between the shard and telemetry core. + +use std::net::IpAddr; + +use crate::id_type; +use crate::node_message::Payload; +use crate::node_types::{BlockHash, NodeDetails}; +use serde::{Deserialize, Serialize}; + +id_type! { + /// The shard-local ID of a given node, where a single connection + /// might send data on behalf of more than one chain. + #[derive(serde::Serialize, serde::Deserialize)] + pub struct ShardNodeId(usize); +} + +/// Message sent from a telemetry shard to the telemetry core +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum FromShardAggregator { + /// Get information about a new node, including it's IP + /// address and chain genesis hash. + AddNode { + ip: IpAddr, + node: NodeDetails, + local_id: ShardNodeId, + genesis_hash: BlockHash, + }, + /// A message payload with updated details for a node + UpdateNode { + local_id: ShardNodeId, + payload: Payload, + }, + /// Inform the telemetry core that a node has been removed + RemoveNode { local_id: ShardNodeId }, +} + +/// Message sent form the telemetry core to a telemetry shard +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum FromTelemetryCore { + Mute { + local_id: ShardNodeId, + reason: MuteReason, + }, +} + +/// Why is the thing being muted? +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum MuteReason { + Overquota, + ChainNotAllowed, +} diff --git a/common/src/lib.rs b/common/src/lib.rs new file mode 100644 index 0000000..541a0da --- /dev/null +++ b/common/src/lib.rs @@ -0,0 +1,27 @@ +pub mod byte_size; +pub mod http_utils; +pub mod id_type; +pub mod internal_messages; +pub mod node_message; +pub mod node_types; +pub mod ready_chunks_all; +pub mod rolling_total; +pub mod time; +pub mod ws_client; + +mod assign_id; +mod dense_map; +mod either_sink; +mod mean_list; +mod most_seen; +mod multi_map_unique; +mod num_stats; + +// Export a bunch of common bits at the top level for ease of import: +pub use assign_id::AssignId; +pub use dense_map::DenseMap; +pub use either_sink::EitherSink; +pub use mean_list::MeanList; +pub use most_seen::MostSeen; +pub use multi_map_unique::MultiMapUnique; +pub use num_stats::NumStats; diff --git a/common/src/mean_list.rs b/common/src/mean_list.rs new file mode 100644 index 0000000..ce91aaa --- /dev/null +++ b/common/src/mean_list.rs @@ -0,0 +1,79 @@ +use num_traits::{Float, Zero}; +use std::ops::AddAssign; + +pub struct MeanList +where + T: Float + AddAssign + Zero + From, +{ + period_sum: T, + period_count: u8, + mean_index: u8, + means: [T; 20], + ticks_per_mean: u8, +} + +impl Default for MeanList +where + T: Float + AddAssign + Zero + From, +{ + fn default() -> MeanList { + MeanList { + period_sum: T::zero(), + period_count: 0, + mean_index: 0, + means: [T::zero(); 20], + ticks_per_mean: 1, + } + } +} + +impl MeanList +where + T: Float + AddAssign + Zero + From, +{ + pub fn slice(&self) -> &[T] { + &self.means[..usize::from(self.mean_index)] + } + + pub fn push(&mut self, val: T) -> bool { + if self.mean_index == 20 && self.ticks_per_mean < 32 { + self.squash_means(); + } + + self.period_sum += val; + self.period_count += 1; + + if self.period_count == self.ticks_per_mean { + self.push_mean(); + true + } else { + false + } + } + + fn push_mean(&mut self) { + let mean = self.period_sum / std::convert::From::from(self.period_count); + + if self.mean_index == 20 && self.ticks_per_mean == 32 { + self.means.rotate_left(1); + self.means[19] = mean; + } else { + self.means[usize::from(self.mean_index)] = mean; + self.mean_index += 1; + } + + self.period_sum = T::zero(); + self.period_count = 0; + } + + fn squash_means(&mut self) { + self.ticks_per_mean *= 2; + self.mean_index = 10; + + for i in 0..10 { + let i2 = i * 2; + + self.means[i] = (self.means[i2] + self.means[i2 + 1]) / std::convert::From::from(2) + } + } +} diff --git a/common/src/most_seen.rs b/common/src/most_seen.rs new file mode 100644 index 0000000..f42df65 --- /dev/null +++ b/common/src/most_seen.rs @@ -0,0 +1,236 @@ +use std::collections::HashMap; +use std::hash::Hash; + +/// Add items to this, and it will keep track of what the item +/// seen the most is. +#[derive(Debug)] +pub struct MostSeen { + current_best: T, + current_count: usize, + others: HashMap, +} + +impl Default for MostSeen { + fn default() -> Self { + // This sets the "most seen item" to the default value for the type, + // and notes that nobody has actually seen it yet (current_count is 0). + Self { + current_best: T::default(), + current_count: 0, + others: HashMap::new(), + } + } +} + +impl MostSeen { + pub fn new(item: T) -> Self { + // This starts us off with an item that we've seen. This item is set as + // the "most seen item" and the current_count is set to 1, as we've seen it + // once by virtue of providing it here. + Self { + current_best: item, + current_count: 1, + others: HashMap::new(), + } + } + pub fn best(&self) -> &T { + &self.current_best + } + pub fn best_count(&self) -> usize { + self.current_count + } +} + +impl MostSeen { + pub fn insert(&mut self, item: &T) -> ChangeResult { + if &self.current_best == item { + // Item already the best one; bump count. + self.current_count += 1; + return ChangeResult::NoChange; + } + + // Item not the best; increment count in map + let item_count = self.others.entry(item.clone()).or_default(); + *item_count += 1; + + // Is item now the best? + if *item_count > self.current_count { + let (mut item, mut count) = self.others.remove_entry(item).expect("item added above"); + + // Swap the current best for the new best: + std::mem::swap(&mut item, &mut self.current_best); + std::mem::swap(&mut count, &mut self.current_count); + + // Insert the old best back into the map: + self.others.insert(item, count); + + ChangeResult::NewMostSeenItem + } else { + ChangeResult::NoChange + } + } + pub fn remove(&mut self, item: &T) -> ChangeResult { + if &self.current_best == item { + // Item already the best one; reduce count (don't allow to drop below 0) + self.current_count = self.current_count.saturating_sub(1); + + // Is there a new best? + let other_best = self.others.iter().max_by_key(|f| f.1); + + let (other_item, &other_count) = match other_best { + Some(item) => item, + None => return ChangeResult::NoChange, + }; + + if other_count > self.current_count { + // Clone item to unborrow self.others so that we can remove + // the item from it. We could pre-emptively remove and reinsert + // instead, but most of the time there is no change, so I'm + // aiming to keep that path cheaper. + let other_item = other_item.clone(); + let (mut other_item, mut other_count) = self + .others + .remove_entry(&other_item) + .expect("item returned above, so def exists"); + + // Swap the current best for the new best: + std::mem::swap(&mut other_item, &mut self.current_best); + std::mem::swap(&mut other_count, &mut self.current_count); + + // Insert the old best back into the map: + self.others.insert(other_item, other_count); + + return ChangeResult::NewMostSeenItem; + } else { + return ChangeResult::NoChange; + } + } + + // Item is in the map; not the best anyway. decrement count. + if let Some(count) = self.others.get_mut(item) { + *count += 1; + } + ChangeResult::NoChange + } +} + +/// Record the result of adding/removing an entry +#[derive(Clone, Copy)] +pub enum ChangeResult { + /// The best item has remained the same. + NoChange, + /// There is a new best item now. + NewMostSeenItem, +} + +impl ChangeResult { + pub fn has_changed(self) -> bool { + match self { + ChangeResult::NewMostSeenItem => true, + ChangeResult::NoChange => false, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn default_renames_instantly() { + let mut a: MostSeen<&str> = MostSeen::default(); + let res = a.insert(&"Hello"); + assert_eq!(*a.best(), "Hello"); + assert!(res.has_changed()); + } + + #[test] + fn new_renames_on_second_change() { + let mut a: MostSeen<&str> = MostSeen::new("First"); + a.insert(&"Second"); + assert_eq!(*a.best(), "First"); + a.insert(&"Second"); + assert_eq!(*a.best(), "Second"); + } + + #[test] + fn removing_doesnt_underflow() { + let mut a: MostSeen<&str> = MostSeen::new("First"); + a.remove(&"First"); + a.remove(&"First"); + a.remove(&"Second"); + a.remove(&"Third"); + } + + #[test] + fn keeps_track_of_best_count() { + let mut a: MostSeen<&str> = MostSeen::default(); + a.insert(&"First"); + assert_eq!(a.best_count(), 1); + + a.insert(&"First"); + assert_eq!(a.best_count(), 2); + + a.insert(&"First"); + assert_eq!(a.best_count(), 3); + + a.remove(&"First"); + assert_eq!(a.best_count(), 2); + + a.remove(&"First"); + assert_eq!(a.best_count(), 1); + + a.remove(&"First"); + assert_eq!(a.best_count(), 0); + + a.remove(&"First"); + assert_eq!(a.best_count(), 0); + } + + #[test] + fn it_tracks_best_on_insert() { + let mut a: MostSeen<&str> = MostSeen::default(); + + a.insert(&"First"); + assert_eq!(*a.best(), "First", "1"); + + a.insert(&"Second"); + assert_eq!(*a.best(), "First", "2"); + + a.insert(&"Second"); + assert_eq!(*a.best(), "Second", "3"); + + a.insert(&"First"); + assert_eq!(*a.best(), "Second", "4"); + + a.insert(&"First"); + assert_eq!(*a.best(), "First", "5"); + } + + #[test] + fn it_tracks_best() { + let mut a: MostSeen<&str> = MostSeen::default(); + a.insert(&"First"); + a.insert(&"Second"); + a.insert(&"Third"); // 1 + + a.insert(&"Second"); + a.insert(&"Second"); // 3 + a.insert(&"First"); // 2 + + assert_eq!(*a.best(), "Second"); + assert_eq!(a.best_count(), 3); + + let res = a.remove(&"Second"); + + assert!(!res.has_changed()); + assert_eq!(a.best_count(), 2); + assert_eq!(*a.best(), "Second"); // Tied with "First" + + let res = a.remove(&"Second"); + + assert!(res.has_changed()); + assert_eq!(a.best_count(), 2); + assert_eq!(*a.best(), "First"); // First is now ahead + } +} diff --git a/common/src/multi_map_unique.rs b/common/src/multi_map_unique.rs new file mode 100644 index 0000000..b2d5cfe --- /dev/null +++ b/common/src/multi_map_unique.rs @@ -0,0 +1,151 @@ +use std::collections::{HashMap, HashSet}; +use std::hash::Hash; + +/// A map where each key can contain multiple values. We enforce that a value +/// only ever belongs to one key at a time (the latest key it was inserted +/// against). +pub struct MultiMapUnique { + value_to_key: HashMap, + key_to_values: HashMap>, +} + +impl MultiMapUnique { + /// Construct a new MultiMap + pub fn new() -> Self { + Self { + value_to_key: HashMap::new(), + key_to_values: HashMap::new(), + } + } + + /// Return the set of values associated with a key. + pub fn get_values(&self, key: &K) -> Option<&HashSet> + where + K: Eq + Hash, + { + self.key_to_values.get(key) + } + + /// Remove a value from the MultiMap, returning the key it was found + /// under, if it was found at all. + /// + /// ``` + /// let mut m = ghost_telemetry_common::MultiMapUnique::new(); + /// + /// m.insert("a", 1); + /// m.insert("a", 2); + /// + /// m.insert("b", 3); + /// m.insert("b", 4); + /// + /// assert_eq!(m.num_keys(), 2); + /// assert_eq!(m.num_values(), 4); + /// + /// m.remove_value(&1); + /// + /// assert_eq!(m.num_keys(), 2); + /// assert_eq!(m.num_values(), 3); + /// + /// m.remove_value(&2); + /// + /// assert_eq!(m.num_keys(), 1); + /// assert_eq!(m.num_values(), 2); + /// ``` + pub fn remove_value(&mut self, value: &V) -> Option + where + V: Eq + Hash, + K: Eq + Hash, + { + if let Some(key) = self.value_to_key.remove(value) { + if let Some(m) = self.key_to_values.get_mut(&key) { + m.remove(value); + if m.is_empty() { + self.key_to_values.remove(&key); + } + } + return Some(key); + } + None + } + + /// Insert a key+value pair into the multimap. Multiple different + /// values can exist for a single key, but only one of each value can + /// exist in the MultiMap. + /// + /// If a previous value did exist, the old key it was inserted against + /// is returned. + /// + /// ``` + /// let mut m = ghost_telemetry_common::MultiMapUnique::new(); + /// + /// let old_key = m.insert("a", 1); + /// assert_eq!(old_key, None); + /// + /// let old_key = m.insert("b", 1); + /// assert_eq!(old_key, Some("a")); + /// + /// let old_key = m.insert("c", 1); + /// assert_eq!(old_key, Some("b")); + /// + /// assert_eq!(m.num_keys(), 1); + /// assert_eq!(m.num_values(), 1); + /// + /// // The value `1` must be unique in the map, so it only exists + /// // in the last location it was inserted: + /// assert!(m.get_values(&"a").is_none()); + /// assert!(m.get_values(&"b").is_none()); + /// assert_eq!(m.get_values(&"c").unwrap().iter().collect::>(), vec![&1]); + /// ``` + pub fn insert(&mut self, key: K, value: V) -> Option + where + V: Clone + Eq + Hash, + K: Clone + Eq + Hash, + { + // Ensure that the value doesn't exist elsewhere already; + // values must be unique and only belong to one key: + let old_key = self.remove_value(&value); + + self.value_to_key.insert(value.clone(), key.clone()); + self.key_to_values.entry(key).or_default().insert(value); + + old_key + } + + /// Number of values stored in the map + pub fn num_values(&self) -> usize { + self.value_to_key.len() + } + + /// Number of keys stored in the map + pub fn num_keys(&self) -> usize { + self.key_to_values.len() + } +} + +#[cfg(test)] +mod test { + + use super::*; + + #[test] + fn multiple_values_allowed_per_key() { + let mut m = MultiMapUnique::new(); + + m.insert("a", 1); + m.insert("a", 2); + + m.insert("b", 3); + m.insert("b", 4); + + assert_eq!(m.num_keys(), 2); + assert_eq!(m.num_values(), 4); + + let a_vals = m.get_values(&"a").expect("a vals"); + assert!(a_vals.contains(&1)); + assert!(a_vals.contains(&2)); + + let b_vals = m.get_values(&"b").expect("b vals"); + assert!(b_vals.contains(&3)); + assert!(b_vals.contains(&4)); + } +} diff --git a/common/src/node_message.rs b/common/src/node_message.rs new file mode 100644 index 0000000..732fe9c --- /dev/null +++ b/common/src/node_message.rs @@ -0,0 +1,208 @@ +//! This is the internal representation of telemetry messages sent from nodes. +//! There is a separate JSON representation of these types, because internally we want to be +//! able to serialize these messages to bincode, and various serde attributes aren't compatible +//! with this, hence this separate internal representation. + +use crate::node_types::{Block, BlockHash, BlockNumber, NodeDetails}; +use serde::{Deserialize, Serialize}; + +pub type NodeMessageId = u64; + +#[derive(Serialize, Deserialize, Debug)] +pub enum NodeMessage { + V1 { payload: Payload }, + V2 { id: NodeMessageId, payload: Payload }, +} + +impl NodeMessage { + /// Returns the ID associated with the node message, or 0 + /// if the message has no ID. + pub fn id(&self) -> NodeMessageId { + match self { + NodeMessage::V1 { .. } => 0, + NodeMessage::V2 { id, .. } => *id, + } + } + /// Return the payload associated with the message. + pub fn into_payload(self) -> Payload { + match self { + NodeMessage::V1 { payload, .. } | NodeMessage::V2 { payload, .. } => payload, + } + } +} + +impl From for Payload { + fn from(msg: NodeMessage) -> Payload { + msg.into_payload() + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum Payload { + SystemConnected(SystemConnected), + SystemInterval(SystemInterval), + BlockImport(Block), + NotifyFinalized(Finalized), + AfgAuthoritySet(AfgAuthoritySet), + HwBench(NodeHwBench), +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemConnected { + pub genesis_hash: BlockHash, + pub node: NodeDetails, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemInterval { + pub peers: Option, + pub txcount: Option, + pub bandwidth_upload: Option, + pub bandwidth_download: Option, + pub finalized_height: Option, + pub finalized_hash: Option, + pub block: Option, + pub used_state_cache_size: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Finalized { + pub hash: BlockHash, + pub height: Box, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AfgAuthoritySet { + pub authority_id: Box, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct NodeHwBench { + pub cpu_hashrate_score: u64, + pub memory_memcpy_score: u64, + pub disk_sequential_write_score: Option, + pub disk_random_write_score: Option, +} + +impl Payload { + pub fn best_block(&self) -> Option<&Block> { + match self { + Payload::BlockImport(block) => Some(block), + Payload::SystemInterval(SystemInterval { block, .. }) => block.as_ref(), + _ => None, + } + } + + pub fn finalized_block(&self) -> Option { + match self { + Payload::SystemInterval(ref interval) => Some(Block { + hash: interval.finalized_hash?, + height: interval.finalized_height?, + }), + Payload::NotifyFinalized(ref finalized) => Some(Block { + hash: finalized.hash, + height: finalized.height.parse().ok()?, + }), + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use arrayvec::ArrayString; + use bincode::Options; + + // Without adding a derive macro and marker trait (and enforcing their use), we don't really + // know whether things can (de)serialize to bincode or not at runtime without failing unless + // we test the different types we want to (de)serialize ourselves. We just need to test each + // type, not each variant. + fn bincode_can_serialize_and_deserialize<'de, T>(item: T) + where + T: Serialize + serde::de::DeserializeOwned, + { + let bytes = bincode::serialize(&item).expect("Serialization should work"); + let _: T = bincode::deserialize(&bytes).expect("Deserialization should work"); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_system_connected() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::SystemConnected(SystemConnected { + genesis_hash: BlockHash::zero(), + node: NodeDetails { + chain: "foo".into(), + name: "foo".into(), + implementation: "foo".into(), + version: "foo".into(), + target_arch: Some("x86_64".into()), + target_os: Some("linux".into()), + target_env: Some("env".into()), + validator: None, + network_id: ArrayString::new(), + startup_time: None, + sysinfo: None, + ip: Some("127.0.0.1".into()), + }, + }), + }); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_system_interval() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::SystemInterval(SystemInterval { + peers: None, + txcount: None, + bandwidth_upload: None, + bandwidth_download: None, + finalized_height: None, + finalized_hash: None, + block: None, + used_state_cache_size: None, + }), + }); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_block_import() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::BlockImport(Block { + hash: BlockHash([0; 32]), + height: 0, + }), + }); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_notify_finalized() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::NotifyFinalized(Finalized { + hash: BlockHash::zero(), + height: "foo".into(), + }), + }); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_afg_authority_set() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::AfgAuthoritySet(AfgAuthoritySet { + authority_id: "foo".into(), + }), + }); + } + + #[test] + fn bincode_block_zero() { + let raw = Block::zero(); + + let bytes = bincode::options().serialize(&raw).unwrap(); + + let deserialized: Block = bincode::options().deserialize(&bytes).unwrap(); + + assert_eq!(raw.hash, deserialized.hash); + assert_eq!(raw.height, deserialized.height); + } +} diff --git a/common/src/node_types.rs b/common/src/node_types.rs new file mode 100644 index 0000000..191b888 --- /dev/null +++ b/common/src/node_types.rs @@ -0,0 +1,245 @@ +//! These types are partly used in [`crate::node_message`], but also stored and used +//! more generally through the application. + +use arrayvec::ArrayString; +use serde::ser::{SerializeTuple, Serializer}; +use serde::{Deserialize, Serialize}; + +use crate::{time, MeanList}; + +pub type BlockNumber = u64; +pub type Timestamp = u64; +pub use primitive_types::H256 as BlockHash; +pub type NetworkId = ArrayString<64>; + +/// Basic node details. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct NodeDetails { + pub chain: Box, + pub name: Box, + pub implementation: Box, + pub version: Box, + pub validator: Option>, + pub network_id: NetworkId, + pub startup_time: Option>, + pub target_os: Option>, + pub target_arch: Option>, + pub target_env: Option>, + pub sysinfo: Option, + pub ip: Option>, +} + +/// Hardware and software information for the node. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct NodeSysInfo { + /// The exact CPU model. + pub cpu: Option>, + /// The total amount of memory, in bytes. + pub memory: Option, + /// The number of physical CPU cores. + pub core_count: Option, + /// The Linux kernel version. + pub linux_kernel: Option>, + /// The exact Linux distribution used. + pub linux_distro: Option>, + /// Whether the node's running under a virtual machine. + pub is_virtual_machine: Option, +} + +/// Hardware benchmark results for the node. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct NodeHwBench { + /// The CPU speed, as measured in how many MB/s it can hash using the BLAKE2b-256 hash. + pub cpu_hashrate_score: u64, + /// Memory bandwidth in MB/s, calculated by measuring the throughput of `memcpy`. + pub memory_memcpy_score: u64, + /// Sequential disk write speed in MB/s. + pub disk_sequential_write_score: Option, + /// Random disk write speed in MB/s. + pub disk_random_write_score: Option, +} + +/// A couple of node statistics. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct NodeStats { + pub peers: u64, + pub txcount: u64, +} + +// # A note about serialization/deserialization of types in this file: +// +// Some of the types here are sent to UI feeds. In an effort to keep the +// amount of bytes sent to a minimum, we have written custom serializers +// for those types. +// +// For testing purposes, it's useful to be able to deserialize from some +// of these types so that we can test message feed things, so custom +// deserializers exist to undo the work of the custom serializers. +impl Serialize for NodeStats { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(2)?; + tup.serialize_element(&self.peers)?; + tup.serialize_element(&self.txcount)?; + tup.end() + } +} + +impl<'de> Deserialize<'de> for NodeStats { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let (peers, txcount) = <(u64, u64)>::deserialize(deserializer)?; + Ok(NodeStats { peers, txcount }) + } +} + +/// Node IO details. +#[derive(Default)] +pub struct NodeIO { + pub used_state_cache_size: MeanList, +} + +impl Serialize for NodeIO { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(1)?; + // This is "one-way": we can't deserialize again from this to a MeanList: + tup.serialize_element(self.used_state_cache_size.slice())?; + tup.end() + } +} + +/// Concise block details +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] +pub struct Block { + pub hash: BlockHash, + pub height: BlockNumber, +} + +impl Block { + pub fn zero() -> Self { + Block { + hash: BlockHash::from([0; 32]), + height: 0, + } + } +} + +/// Node hardware details. +#[derive(Default)] +pub struct NodeHardware { + /// Upload uses means + pub upload: MeanList, + /// Download uses means + pub download: MeanList, + /// Stampchange uses means + pub chart_stamps: MeanList, +} + +impl Serialize for NodeHardware { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(3)?; + // These are "one-way": we can't deserialize again from them to MeanLists: + tup.serialize_element(self.upload.slice())?; + tup.serialize_element(self.download.slice())?; + tup.serialize_element(self.chart_stamps.slice())?; + tup.end() + } +} + +/// Node location details +#[derive(Debug, Clone, PartialEq)] +pub struct NodeLocation { + pub latitude: f32, + pub longitude: f32, + pub city: Box, +} + +impl Serialize for NodeLocation { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(3)?; + tup.serialize_element(&self.latitude)?; + tup.serialize_element(&self.longitude)?; + tup.serialize_element(&&*self.city)?; + tup.end() + } +} + +impl<'de> Deserialize<'de> for NodeLocation { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let (latitude, longitude, city) = <(f32, f32, Box)>::deserialize(deserializer)?; + Ok(NodeLocation { + latitude, + longitude, + city, + }) + } +} + +/// Verbose block details +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct BlockDetails { + pub block: Block, + pub block_time: u64, + pub block_timestamp: u64, + pub propagation_time: Option, +} + +impl Default for BlockDetails { + fn default() -> Self { + BlockDetails { + block: Block::zero(), + block_timestamp: time::now(), + block_time: 0, + propagation_time: None, + } + } +} + +impl Serialize for BlockDetails { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(5)?; + tup.serialize_element(&self.block.height)?; + tup.serialize_element(&self.block.hash)?; + tup.serialize_element(&self.block_time)?; + tup.serialize_element(&self.block_timestamp)?; + tup.serialize_element(&self.propagation_time)?; + tup.end() + } +} + +impl<'de> Deserialize<'de> for BlockDetails { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let tup = <(u64, BlockHash, u64, u64, Option)>::deserialize(deserializer)?; + Ok(BlockDetails { + block: Block { + height: tup.0, + hash: tup.1, + }, + block_time: tup.2, + block_timestamp: tup.3, + propagation_time: tup.4, + }) + } +} diff --git a/common/src/num_stats.rs b/common/src/num_stats.rs new file mode 100644 index 0000000..8bec802 --- /dev/null +++ b/common/src/num_stats.rs @@ -0,0 +1,104 @@ +use num_traits::{Bounded, NumOps, Zero}; +use std::convert::TryFrom; +use std::iter::Sum; + +/// Keep track of last N numbers pushed onto internal stack. +/// Provides means to get an average of said numbers. +pub struct NumStats { + stack: Box<[T]>, + index: usize, + sum: T, +} + +impl> NumStats { + pub fn new(size: usize) -> Self { + NumStats { + stack: vec![T::zero(); size].into_boxed_slice(), + index: 0, + sum: T::zero(), + } + } + + pub fn push(&mut self, val: T) { + let slot = &mut self.stack[self.index % self.stack.len()]; + + self.sum = (self.sum + val) - *slot; + + *slot = val; + + self.index += 1; + } + + pub fn average(&self) -> T { + let cap = std::cmp::min(self.index, self.stack.len()); + + if cap == 0 { + return T::zero(); + } + + let cap = T::try_from(cap).unwrap_or_else(|_| T::max_value()); + + self.sum / cap + } + + pub fn reset(&mut self) { + self.index = 0; + self.sum = T::zero(); + + for val in self.stack.iter_mut() { + *val = T::zero(); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn calculates_correct_average() { + let mut stats: NumStats = NumStats::new(10); + + stats.push(3); + stats.push(7); + + assert_eq!(stats.average(), 5); + } + + #[test] + fn calculates_correct_average_over_bounds() { + let mut stats: NumStats = NumStats::new(10); + + stats.push(100); + + for _ in 0..9 { + stats.push(0); + } + + assert_eq!(stats.average(), 10); + + stats.push(0); + + assert_eq!(stats.average(), 0); + } + + #[test] + fn resets_properly() { + let mut stats: NumStats = NumStats::new(10); + + for _ in 0..10 { + stats.push(100); + } + + assert_eq!(stats.average(), 100); + + stats.reset(); + + assert_eq!(stats.average(), 0); + + stats.push(7); + stats.push(3); + + assert_eq!(stats.average(), 5); + } +} diff --git a/common/src/ready_chunks_all.rs b/common/src/ready_chunks_all.rs new file mode 100644 index 0000000..bb44e85 --- /dev/null +++ b/common/src/ready_chunks_all.rs @@ -0,0 +1,105 @@ +//! [`futures::StreamExt::ready_chunks()`] internally stores a vec with a certain capacity, and will buffer up +//! up to that many items that are ready from the underlying stream before returning either when we run out of +//! Poll::Ready items, or we hit the capacity. +//! +//! This variation has no fixed capacity, and will buffer everything it can up at each point to return. This is +//! better when the amount of items varies a bunch (and we don't want to allocate a fixed capacity every time), +//! and can help ensure that we process as many items as possible each time (rather than only up to capacity items). +//! +//! Code is adapted from the futures implementation +//! (see [ready_chunks.rs](https://docs.rs/futures-util/0.3.15/src/futures_util/stream/stream/ready_chunks.rs.html)). + +use core::mem; +use core::pin::Pin; +use futures::stream::Fuse; +use futures::stream::{FusedStream, Stream}; +use futures::task::{Context, Poll}; +use futures::StreamExt; +use pin_project_lite::pin_project; + +pin_project! { + /// Buffer up all Ready items in the underlying stream each time + /// we attempt to retrieve items from it, and return a Vec of those + /// items. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct ReadyChunksAll { + #[pin] + stream: Fuse, + items: Vec, + } +} + +impl ReadyChunksAll +where + St: Stream, +{ + pub fn new(stream: St) -> Self { + Self { + stream: stream.fuse(), + items: Vec::new(), + } + } +} + +impl Stream for ReadyChunksAll { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + loop { + match this.stream.as_mut().poll_next(cx) { + // Flush all collected data if underlying stream doesn't contain + // more ready values + Poll::Pending => { + return if this.items.is_empty() { + Poll::Pending + } else { + Poll::Ready(Some(mem::take(this.items))) + } + } + + // Push the ready item into the buffer + Poll::Ready(Some(item)) => { + this.items.push(item); + } + + // Since the underlying stream ran out of values, return what we + // have buffered, if we have anything. + Poll::Ready(None) => { + let last = if this.items.is_empty() { + None + } else { + let full_buf = mem::take(this.items); + Some(full_buf) + }; + + return Poll::Ready(last); + } + } + } + } + + fn size_hint(&self) -> (usize, Option) { + // Look at the underlying stream's size_hint. If we've + // buffered some items, we'll return at least that Vec, + // giving us a lower bound 1 greater than the underlying. + // The upper bound is, worst case, our vec + each individual + // item in the underlying stream. + let chunk_len = if self.items.is_empty() { 0 } else { 1 }; + let (lower, upper) = self.stream.size_hint(); + let lower = lower.saturating_add(chunk_len); + let upper = match upper { + Some(x) => x.checked_add(chunk_len), + None => None, + }; + (lower, upper) + } +} + +impl FusedStream for ReadyChunksAll { + fn is_terminated(&self) -> bool { + self.stream.is_terminated() && self.items.is_empty() + } +} diff --git a/common/src/rolling_total.rs b/common/src/rolling_total.rs new file mode 100644 index 0000000..ccba3df --- /dev/null +++ b/common/src/rolling_total.rs @@ -0,0 +1,342 @@ +use num_traits::{SaturatingAdd, SaturatingSub, Zero}; +use std::collections::VecDeque; +use std::time::{Duration, Instant}; + +/// Build an object responsible for keeping track of a rolling total. +/// It does this in constant time and using memory proportional to the +/// granularity * window size multiple that we set. +pub struct RollingTotalBuilder { + window_size_multiple: usize, + granularity: Duration, + time_source: Time, +} + +impl RollingTotalBuilder { + /// Build a [`RollingTotal`] struct. By default, + /// the window_size is 10s, the granularity is 1s, + /// and system time is used. + pub fn new() -> RollingTotalBuilder { + Self { + window_size_multiple: 10, + granularity: Duration::from_secs(1), + time_source: SystemTimeSource, + } + } + + /// Set the source of time we'll use. By default, we use system time. + pub fn time_source(self, val: Time) -> RollingTotalBuilder