diff --git a/.dockerignore b/.dockerignore index 0fa9ce9d18..fec7622b6c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,3 +6,11 @@ docker-compose.yml cli/bin cli/tests !cli/bin/snark-worker + +# Heartbeats processor +tools/heartbeats-processor/.env +tools/heartbeats-processor/data/ +tools/heartbeats-processor/credentials/ +tools/heartbeats-processor/*.db +# Ensure .sqlx files are included +!tools/heartbeats-processor/.sqlx/ diff --git a/Cargo.lock b/Cargo.lock index 0c6720644c..ac4edf0fb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,7 +130,7 @@ dependencies = [ "num", "serde", "serde_json", - "thiserror", + "thiserror 1.0.60", "toml", "wasm-bindgen", "wasm-bindgen-test", @@ -329,7 +329,7 @@ name = "ark-serialize-derive" version = "0.3.0" source = "git+https://github.com/openmina/algebra?rev=aea157a#aea157a8e81ddc9f16bae5123b5dda169e3842c9" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", ] @@ -385,7 +385,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.60", "time", ] @@ -401,7 +401,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.60", "time", ] @@ -411,7 +411,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", "synstructure 0.12.6", @@ -423,9 +423,9 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", "synstructure 0.13.1", ] @@ -435,7 +435,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", ] @@ -446,9 +446,22 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", +] + +[[package]] +name = "async-compression" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", ] [[package]] @@ -480,15 +493,37 @@ dependencies = [ "event-listener 2.5.3", ] +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.35", + "syn 2.0.96", +] + [[package]] name = "async-trait" version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -557,9 +592,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "459b77b7e855f875fd15f101064825cd79eb83185a961d66e6298560126facfb" dependencies = [ "derive_utils", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -596,7 +631,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -623,6 +658,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "futures-core", + "getrandom", + "instant", + "pin-project-lite", + "rand", + "tokio", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -681,7 +730,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b6598a2f5d564fb7855dc6b06fd1c38cff5a72bd8b863a4d021938497b440a" dependencies = [ "serde", - "thiserror", + "thiserror 1.0.60", ] [[package]] @@ -699,7 +748,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.8.0", "cexpr", "clang-sys", "itertools 0.12.0", @@ -707,12 +756,12 @@ dependencies = [ "lazycell", "log", "prettyplease", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", - "syn 2.0.58", + "syn 2.0.96", "which", ] @@ -749,9 +798,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" dependencies = [ "serde", ] @@ -881,9 +930,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" dependencies = [ "serde", ] @@ -935,6 +984,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chacha20" version = "0.8.2" @@ -992,8 +1047,10 @@ checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-targets 0.52.6", ] @@ -1072,9 +1129,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -1202,6 +1259,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -1212,11 +1278,21 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core2" @@ -1236,9 +1312,9 @@ dependencies = [ "cc", "cpp_common", "lazy_static", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "regex", - "syn 2.0.58", + "syn 2.0.96", "unicode-xid 0.2.4", ] @@ -1249,8 +1325,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25fcfea2ee05889597d35e986c2ad0169694320ae5cc8f6d2640a4bb8a884560" dependencies = [ "lazy_static", - "proc-macro2 1.0.79", - "syn 2.0.58", + "proc-macro2 1.0.93", + "syn 2.0.96", ] [[package]] @@ -1443,9 +1519,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -1486,7 +1562,7 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "strsim 0.10.0", "syn 1.0.109", @@ -1500,7 +1576,7 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "strsim 0.10.0", "syn 1.0.109", @@ -1514,10 +1590,10 @@ checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "strsim 0.10.0", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -1550,7 +1626,7 @@ checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be" dependencies = [ "darling_core 0.20.6", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -1659,7 +1735,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", ] @@ -1680,7 +1756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" dependencies = [ "darling 0.14.4", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", ] @@ -1701,8 +1777,8 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case", - "proc-macro2 1.0.79", + "convert_case 0.4.0", + "proc-macro2 1.0.93", "quote 1.0.35", "rustc_version 0.4.0", "syn 1.0.109", @@ -1714,9 +1790,9 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65f152f4b8559c4da5d574bafc7af85454d706b4c5fe8b530d508cacbb6807ea" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -1785,9 +1861,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -1806,9 +1882,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f87ab5c4c35c1d2fc7928e96d1cee3786a9bc9571ebaf4bf8f4207e6271165fa" dependencies = [ "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dotenvy" version = "0.15.7" @@ -1925,9 +2007,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -1937,9 +2019,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" dependencies = [ "once_cell", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -2061,6 +2143,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" +[[package]] +name = "firestore" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbba2c9a18b47ce16358ffe33bb442256d0cc9f166883aba856746382a8bd3e" +dependencies = [ + "async-trait", + "backoff", + "chrono", + "futures", + "gcloud-sdk", + "hex", + "hyper 1.5.0", + "rand", + "rsb_derive", + "rvstruct", + "serde", + "struct-path", + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -2112,9 +2217,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -2236,9 +2341,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -2337,7 +2442,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30ce01e8bbb3e7e0758dcf907fe799f5998a54368963f766ae94b84624ba60c8" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", ] @@ -2351,6 +2456,34 @@ dependencies = [ "byteorder", ] +[[package]] +name = "gcloud-sdk" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a771db7ee43ad84638d0e6131cb514e402af6a5a96051f4425d66dc0ee527d8" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "futures", + "hyper 1.5.0", + "jsonwebtoken", + "once_cell", + "prost 0.13.4", + "prost-types 0.13.4", + "reqwest 0.12.12", + "secret-vault-value", + "serde", + "serde_json", + "tokio", + "tonic", + "tower 0.5.2", + "tower-layer", + "tower-util", + "tracing", + "url", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2473,7 +2606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ebc8013b4426d5b81a4364c419a95ed0b404af2b82e2457de52d9348f0e474" dependencies = [ "combine", - "thiserror", + "thiserror 1.0.60", ] [[package]] @@ -2498,7 +2631,7 @@ dependencies = [ "graphql-parser", "heck 0.4.1", "lazy_static", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "serde", "serde_json", @@ -2512,7 +2645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83febfa838f898cfa73dfaa7a8eb69ff3409021ac06ee94cfb3d622f6eeb1a97" dependencies = [ "graphql_client_codegen", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "syn 1.0.109", ] @@ -2665,6 +2798,25 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heartbeats-processor" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64 0.22.0", + "chrono", + "clap 4.5.20", + "dotenv", + "firestore", + "gcloud-sdk", + "mina-p2p-messages", + "openmina-core", + "serde", + "serde_json", + "sqlx", + "tokio", +] + [[package]] name = "heck" version = "0.3.3" @@ -2881,21 +3033,35 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.4" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6884a48c6826ec44f524c7456b163cebe9e55a18d7b5e307cb4f100371cc767" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http 1.1.0", "hyper 1.5.0", "hyper-util", - "rustls 0.23.20", + "rustls 0.23.21", + "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper 1.5.0", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -2927,9 +3093,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -2940,7 +3106,6 @@ dependencies = [ "pin-project-lite", "socket2 0.5.5", "tokio", - "tower", "tower-service", "tracing", ] @@ -2968,6 +3133,124 @@ dependencies = [ "cc", ] +[[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 1.13.2", + "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 1.0.93", + "quote 1.0.35", + "syn 2.0.96", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -2984,6 +3267,27 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec 1.13.2", + "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 = "if-addrs" version = "0.7.0" @@ -3001,7 +3305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb892e5777fe09e16f3d44de7802f4daa7267ecbe8c466f19d94e25bb0c303e" dependencies = [ "async-io", - "core-foundation", + "core-foundation 0.9.3", "fnv", "futures", "if-addrs", @@ -3085,7 +3389,7 @@ dependencies = [ "rand", "rtcp", "rtp", - "thiserror", + "thiserror 1.0.60", "tokio", "waitgroup", "webrtc-srtp", @@ -3190,7 +3494,22 @@ dependencies = [ "pest_derive", "regex", "serde_json", - "thiserror", + "thiserror 1.0.60", +] + +[[package]] +name = "jsonwebtoken" +version = "9.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +dependencies = [ + "base64 0.21.7", + "js-sys", + "pem 3.0.4", + "ring 0.17.8", + "serde", + "serde_json", + "simple_asn1", ] [[package]] @@ -3216,9 +3535,9 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "760dbe46660494d469023d661e8d268f413b2cb68c999975dcc237407096a693" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", "url", ] @@ -3232,7 +3551,7 @@ dependencies = [ "juniper", "serde", "serde_json", - "thiserror", + "thiserror 1.0.60", "tokio", "warp", ] @@ -3288,7 +3607,7 @@ dependencies = [ "serde_with 1.14.0", "strum 0.24.1", "strum_macros 0.24.3", - "thiserror", + "thiserror 1.0.60", "turshi", ] @@ -3380,9 +3699,9 @@ dependencies = [ "libp2p-upnp", "libp2p-yamux", "multiaddr", - "pin-project", + "pin-project 1.1.5", "rw-stream-sink", - "thiserror", + "thiserror 1.0.60", ] [[package]] @@ -3424,13 +3743,13 @@ dependencies = [ "multistream-select", "once_cell", "parking_lot 0.12.1", - "pin-project", + "pin-project 1.1.5", "quick-protobuf", "rand", "rw-stream-sink", "serde", "smallvec 1.13.2", - "thiserror", + "thiserror 1.0.60", "unsigned-varint 0.7.2", "void", ] @@ -3500,7 +3819,7 @@ dependencies = [ "quick-protobuf", "quick-protobuf-codec", "smallvec 1.13.2", - "thiserror", + "thiserror 1.0.60", "void", ] @@ -3519,7 +3838,7 @@ dependencies = [ "rand", "serde", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.60", "zeroize", ] @@ -3546,7 +3865,7 @@ dependencies = [ "serde", "sha2 0.10.8", "smallvec 1.13.2", - "thiserror", + "thiserror 1.0.60", "uint", "unsigned-varint 0.7.2", "void", @@ -3608,7 +3927,7 @@ dependencies = [ "sha2 0.10.8", "snow", "static_assertions", - "thiserror", + "thiserror 1.0.60", "x25519-dalek", "zeroize", ] @@ -3620,7 +3939,7 @@ source = "git+https://github.com/openmina/rust-libp2p?rev=5c44c7d9#5c44c7d953f50 dependencies = [ "futures", "log", - "pin-project", + "pin-project 1.1.5", "rand", "salsa20", "sha3", @@ -3640,12 +3959,12 @@ dependencies = [ "libp2p-tls", "log", "parking_lot 0.12.1", - "quinn", + "quinn 0.10.2", "rand", "ring 0.16.20", - "rustls 0.23.20", + "rustls 0.23.21", "socket2 0.5.5", - "thiserror", + "thiserror 1.0.60", "tokio", ] @@ -3687,9 +4006,9 @@ source = "git+https://github.com/openmina/rust-libp2p?rev=5c44c7d9#5c44c7d953f50 dependencies = [ "heck 0.4.1", "proc-macro-warning", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -3721,7 +4040,7 @@ dependencies = [ "ring 0.16.20", "rustls 0.21.12", "rustls-webpki 0.101.7", - "thiserror", + "thiserror 1.0.60", "x509-parser 0.15.1", "yasna", ] @@ -3749,7 +4068,7 @@ dependencies = [ "futures", "libp2p-core", "log", - "thiserror", + "thiserror 1.0.60", "yamux", ] @@ -3796,9 +4115,9 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adf157a4dc5a29b7b464aa8fe7edeff30076e07e13646a1c3874f58477dc99f8" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -3813,6 +4132,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "local-ip-address" version = "0.6.1" @@ -3821,7 +4146,7 @@ checksum = "136ef34e18462b17bf39a7826f8f3bbc223341f8e83822beb8b77db9a3d49696" dependencies = [ "libc", "neli", - "thiserror", + "thiserror 1.0.60", "windows-sys 0.48.0", ] @@ -3992,7 +4317,7 @@ dependencies = [ "sha2 0.10.8", "strum 0.26.2", "strum_macros 0.26.4", - "thiserror", + "thiserror 1.0.60", "time", "toml", "wasm-bindgen", @@ -4033,7 +4358,7 @@ dependencies = [ "o1-utils", "rand", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.60", ] [[package]] @@ -4098,7 +4423,7 @@ dependencies = [ "sha2 0.10.8", "strum 0.26.2", "strum_macros 0.26.4", - "thiserror", + "thiserror 1.0.60", "tuple-map", "uuid", "wasm-bindgen", @@ -4231,7 +4556,7 @@ checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate", "proc-macro-error", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", "synstructure 0.12.6", @@ -4251,7 +4576,7 @@ dependencies = [ "bytes", "futures", "log", - "pin-project", + "pin-project 1.1.5", "smallvec 1.13.2", "unsigned-varint 0.7.2", ] @@ -4269,7 +4594,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.9.2", "security-framework-sys", "tempfile", ] @@ -4293,7 +4618,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c168194d373b1e134786274020dae7fc5513d565ea2ebb9bc9ff17ffb69106d4" dependencies = [ "either", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "serde", "syn 1.0.109", @@ -4334,7 +4659,7 @@ dependencies = [ "anyhow", "byteorder", "paste", - "thiserror", + "thiserror 1.0.60", ] [[package]] @@ -4348,7 +4673,7 @@ dependencies = [ "log", "netlink-packet-core", "netlink-sys", - "thiserror", + "thiserror 1.0.60", "tokio", ] @@ -4395,7 +4720,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.8.0", "cfg-if", "libc", ] @@ -4436,7 +4761,7 @@ dependencies = [ "static_assertions", "strum 0.26.2", "strum_macros 0.26.4", - "thiserror", + "thiserror 1.0.60", "time", "tokio", "vergen", @@ -4545,7 +4870,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", ] @@ -4626,7 +4951,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", ] @@ -4638,9 +4963,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -4672,7 +4997,7 @@ dependencies = [ "serde", "serde_with 1.14.0", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.60", ] [[package]] @@ -4798,7 +5123,7 @@ dependencies = [ "serde", "serde_json", "structopt", - "thiserror", + "thiserror 1.0.60", "tokio", ] @@ -4832,7 +5157,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "slab", - "thiserror", + "thiserror 1.0.60", "time", "tokio", "tracing", @@ -4873,10 +5198,10 @@ version = "0.13.0" dependencies = [ "anyhow", "openmina-core", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "rust-format", - "syn 2.0.58", + "syn 2.0.96", "tracing", ] @@ -4895,7 +5220,7 @@ dependencies = [ "rand", "serde", "serde_json", - "thiserror", + "thiserror 1.0.60", ] [[package]] @@ -4918,12 +5243,12 @@ dependencies = [ "rand", "rayon", "redux", - "reqwest 0.12.9", + "reqwest 0.12.12", "rsa", "serde", "serde_json", "sha3", - "thiserror", + "thiserror 1.0.60", "tokio", "tracing", "tracing-appender", @@ -4978,7 +5303,7 @@ dependencies = [ "sha3", "strum 0.26.2", "strum_macros 0.26.4", - "thiserror", + "thiserror 1.0.60", "tokio", "tracing", "tracing-subscriber", @@ -5023,7 +5348,7 @@ dependencies = [ "strum 0.26.2", "strum_macros 0.26.4", "temp-dir", - "thiserror", + "thiserror 1.0.60", "time", "tokio", "tower-http", @@ -5054,7 +5379,7 @@ dependencies = [ "redux", "serde", "serde_json", - "thiserror", + "thiserror 1.0.60", "vrf", "wasm-bindgen", "wasm-bindgen-futures", @@ -5077,7 +5402,7 @@ dependencies = [ "serde_json", "sled", "sqlx", - "thiserror", + "thiserror 1.0.60", "time", "tokio", "vrf", @@ -5090,7 +5415,7 @@ version = "0.10.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.8.0", "cfg-if", "foreign-types", "libc", @@ -5105,9 +5430,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -5170,7 +5495,7 @@ dependencies = [ "base64 0.22.0", "binprot", "binprot_derive", - "bitflags 2.4.1", + "bitflags 2.8.0", "blake2", "bs58 0.4.0", "bytes", @@ -5202,7 +5527,7 @@ dependencies = [ "openmina-fuzzer", "openmina-macros", "p2p-testing", - "prost", + "prost 0.12.4", "prost-build", "quick-protobuf", "rand", @@ -5216,7 +5541,7 @@ dependencies = [ "smallvec 1.13.2", "strum 0.26.2", "strum_macros 0.26.4", - "thiserror", + "thiserror 1.0.60", "tokio", "unsigned-varint 0.8.0", "url", @@ -5246,7 +5571,7 @@ dependencies = [ "rand", "redux", "serde_json", - "thiserror", + "thiserror 1.0.60", "tokio", "tracing", "tracing-log", @@ -5366,9 +5691,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" @@ -5377,7 +5702,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.60", "ucd-trie", ] @@ -5399,9 +5724,9 @@ checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -5453,9 +5778,9 @@ checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ "phf_generator", "phf_shared", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -5467,13 +5792,33 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" +dependencies = [ + "pin-project-internal 0.4.30", +] + [[package]] name = "pin-project" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ - "pin-project-internal", + "pin-project-internal 1.1.5", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] @@ -5482,9 +5827,9 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -5571,7 +5916,7 @@ dependencies = [ "serde", "serde_with 1.14.0", "smallvec 2.0.0-alpha.9", - "thiserror", + "thiserror 1.0.60", ] [[package]] @@ -5666,8 +6011,8 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ - "proc-macro2 1.0.79", - "syn 2.0.58", + "proc-macro2 1.0.93", + "syn 2.0.96", ] [[package]] @@ -5696,7 +6041,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", "version_check", @@ -5708,7 +6053,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "version_check", ] @@ -5719,9 +6064,9 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b698b0b09d40e9b7c1a47b132d66a8b54bcd20583d9b6d06e4535e383b4405c" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -5735,9 +6080,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -5760,9 +6105,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -5772,7 +6117,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.12.5", +] + +[[package]] +name = "prost" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +dependencies = [ + "bytes", + "prost-derive 0.13.4", ] [[package]] @@ -5789,10 +6144,10 @@ dependencies = [ "once_cell", "petgraph", "prettyplease", - "prost", - "prost-types", + "prost 0.12.4", + "prost-types 0.12.4", "regex", - "syn 2.0.58", + "syn 2.0.96", "tempfile", ] @@ -5804,18 +6159,40 @@ checksum = "9554e3ab233f0a932403704f1a1d08c30d5ccd931adfdfa1e8b5a19b52c1d55a" dependencies = [ "anyhow", "itertools 0.12.0", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] -name = "prost-types" -version = "0.12.4" +name = "prost-derive" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" dependencies = [ - "prost", + "anyhow", + "itertools 0.12.0", + "proc-macro2 1.0.93", + "quote 1.0.35", + "syn 2.0.96", +] + +[[package]] +name = "prost-types" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +dependencies = [ + "prost 0.12.4", +] + +[[package]] +name = "prost-types" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" +dependencies = [ + "prost 0.13.4", ] [[package]] @@ -5841,7 +6218,7 @@ dependencies = [ "asynchronous-codec", "bytes", "quick-protobuf", - "thiserror", + "thiserror 1.0.60", "unsigned-varint 0.7.2", ] @@ -5854,11 +6231,29 @@ dependencies = [ "bytes", "futures-io", "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", + "quinn-proto 0.10.5", + "quinn-udp 0.4.1", + "rustc-hash 1.1.0", "rustls 0.21.12", - "thiserror", + "thiserror 1.0.60", + "tokio", + "tracing", +] + +[[package]] +name = "quinn" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto 0.11.9", + "quinn-udp 0.5.9", + "rustc-hash 2.1.0", + "rustls 0.23.21", + "socket2 0.5.5", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -5872,12 +6267,32 @@ dependencies = [ "bytes", "rand", "ring 0.16.20", - "rustc-hash", + "rustc-hash 1.1.0", "rustls 0.21.12", "slab", - "thiserror", + "thiserror 1.0.60", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +dependencies = [ + "bytes", + "getrandom", + "rand", + "ring 0.17.8", + "rustc-hash 2.1.0", + "rustls 0.23.21", + "rustls-pki-types", + "slab", + "thiserror 2.0.11", "tinyvec", "tracing", + "web-time", ] [[package]] @@ -5893,6 +6308,20 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "quinn-udp" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.5.5", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "0.6.13" @@ -5908,7 +6337,7 @@ version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", ] [[package]] @@ -6047,7 +6476,7 @@ checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", "redox_syscall 0.2.16", - "thiserror", + "thiserror 1.0.60", ] [[package]] @@ -6157,10 +6586,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ + "async-compression", "base64 0.22.0", "bytes", "encoding_rs", @@ -6179,11 +6609,16 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "quinn 0.11.6", + "rustls 0.23.21", + "rustls-native-certs", "rustls-pemfile 2.2.0", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", @@ -6191,10 +6626,14 @@ dependencies = [ "system-configuration 0.6.1", "tokio", "tokio-native-tls", + "tokio-rustls", + "tokio-util", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "windows-registry", ] @@ -6297,6 +6736,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rsb_derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2c53e42fccdc5f1172e099785fe78f89bc0c1e657d0c2ef591efbfac427e9a4" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.35", + "syn 1.0.109", +] + [[package]] name = "rsexp" version = "0.2.3" @@ -6309,7 +6759,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0726130f64c2f613129d9870fa803071173f317f92bfe924d2d21252c4b3403" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", ] @@ -6333,7 +6783,7 @@ version = "0.11.0" source = "git+https://github.com/openmina/webrtc.git?rev=e8705db39af1b198b324a5db6ff57fb213ba75e9#e8705db39af1b198b324a5db6ff57fb213ba75e9" dependencies = [ "bytes", - "thiserror", + "thiserror 1.0.60", "webrtc-util", ] @@ -6348,7 +6798,7 @@ dependencies = [ "netlink-packet-route", "netlink-proto", "nix 0.24.3", - "thiserror", + "thiserror 1.0.60", "tokio", ] @@ -6361,7 +6811,7 @@ dependencies = [ "portable-atomic", "rand", "serde", - "thiserror", + "thiserror 1.0.60", "webrtc-util", ] @@ -6371,7 +6821,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60e7c00b6c3bf5e38a880eec01d7e829d12ca682079f8238a464def3c4b31627" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", ] [[package]] @@ -6386,6 +6836,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + [[package]] name = "rustc_version" version = "0.3.3" @@ -6433,7 +6889,7 @@ version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys 0.4.14", @@ -6454,10 +6910,11 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ + "log", "once_cell", "ring 0.17.8", "rustls-pki-types", @@ -6466,6 +6923,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.2.0", +] + [[package]] name = "rustls-pemfile" version = "1.0.3" @@ -6486,9 +6955,12 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -6526,13 +6998,33 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "rvs_derive" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1fa12378eb54f3d4f2db8dcdbe33af610b7e7d001961c1055858282ecef2a5" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.35", + "syn 1.0.109", +] + +[[package]] +name = "rvstruct" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5107860ec34506b64cf3680458074eac5c2c564f7ccc140918bbcd1714fd8d5d" +dependencies = [ + "rvs_derive", +] + [[package]] name = "rw-stream-sink" version = "0.4.0" source = "git+https://github.com/openmina/rust-libp2p?rev=5c44c7d9#5c44c7d953f50134a00ded1f9924fa2edd5e09b9" dependencies = [ "futures", - "pin-project", + "pin-project 1.1.5", "static_assertions", ] @@ -6603,7 +7095,7 @@ source = "git+https://github.com/openmina/webrtc.git?rev=e8705db39af1b198b324a5d dependencies = [ "rand", "substring", - "thiserror", + "thiserror 1.0.60", "url", ] @@ -6621,6 +7113,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secret-vault-value" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f8cfb86d2019f64a4cfb49e499f401f406fbec946c1ffeea9d0504284347de" +dependencies = [ + "prost 0.12.4", + "prost-types 0.12.4", + "serde", + "serde_json", + "zeroize", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -6628,7 +7133,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.3", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -6636,9 +7154,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -6692,9 +7210,9 @@ version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -6766,7 +7284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling 0.13.4", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", ] @@ -6778,9 +7296,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" dependencies = [ "darling 0.20.6", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -6900,9 +7418,21 @@ dependencies = [ [[package]] name = "similar" -version = "2.6.0" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "simple_asn1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.11", + "time", +] [[package]] name = "siphasher" @@ -7000,7 +7530,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "strum_macros 0.26.4", - "thiserror", + "thiserror 1.0.60", "wasm-bindgen-test", ] @@ -7098,6 +7628,7 @@ dependencies = [ "atoi", "byteorder", "bytes", + "chrono", "crc", "crossbeam-queue", "either", @@ -7113,6 +7644,7 @@ dependencies = [ "indexmap 2.0.2", "log", "memchr", + "native-tls", "once_cell", "paste", "percent-encoding", @@ -7121,7 +7653,7 @@ dependencies = [ "sha2 0.10.8", "smallvec 1.13.2", "sqlformat", - "thiserror", + "thiserror 1.0.60", "tokio", "tokio-stream", "tracing", @@ -7134,11 +7666,11 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "sqlx-core", "sqlx-macros-core", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -7152,7 +7684,7 @@ dependencies = [ "heck 0.5.0", "hex", "once_cell", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "serde", "serde_json", @@ -7161,7 +7693,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.58", + "syn 2.0.96", "tempfile", "tokio", "url", @@ -7175,9 +7707,10 @@ checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" dependencies = [ "atoi", "base64 0.22.0", - "bitflags 2.4.1", + "bitflags 2.8.0", "byteorder", "bytes", + "chrono", "crc", "digest 0.10.7", "dotenvy", @@ -7204,7 +7737,7 @@ dependencies = [ "smallvec 1.13.2", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.60", "tracing", "whoami", ] @@ -7217,8 +7750,9 @@ checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" dependencies = [ "atoi", "base64 0.22.0", - "bitflags 2.4.1", + "bitflags 2.8.0", "byteorder", + "chrono", "crc", "dotenvy", "etcetera", @@ -7242,7 +7776,7 @@ dependencies = [ "smallvec 1.13.2", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.60", "tracing", "whoami", ] @@ -7254,6 +7788,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" dependencies = [ "atoi", + "chrono", "flume", "futures-channel", "futures-core", @@ -7311,6 +7846,15 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +[[package]] +name = "struct-path" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899edf28cf7320503eda593b4bbce1bc5e9533501a11d45537e2c5be90128fc7" +dependencies = [ + "convert_case 0.6.0", +] + [[package]] name = "structopt" version = "0.3.26" @@ -7330,7 +7874,7 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", ] @@ -7354,7 +7898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "rustversion", "syn 1.0.109", @@ -7367,10 +7911,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "rustversion", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -7385,7 +7929,7 @@ dependencies = [ "rand", "ring 0.17.8", "subtle", - "thiserror", + "thiserror 1.0.60", "tokio", "url", "webrtc-util", @@ -7423,18 +7967,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.58" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "unicode-ident", ] @@ -7472,7 +8016,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", "syn 1.0.109", "unicode-xid 0.2.4", @@ -7484,9 +8028,9 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -7496,7 +8040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.3", "system-configuration-sys 0.5.0", ] @@ -7506,8 +8050,8 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.4.1", - "core-foundation", + "bitflags 2.8.0", + "core-foundation 0.9.3", "system-configuration-sys 0.6.0", ] @@ -7591,7 +8135,16 @@ version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.60", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -7600,9 +8153,20 @@ version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.35", + "syn 2.0.96", ] [[package]] @@ -7668,6 +8232,16 @@ dependencies = [ "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 = "tinyvec" version = "1.8.1" @@ -7708,9 +8282,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -7729,15 +8303,15 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.23.20", + "rustls 0.23.21", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -7796,6 +8370,39 @@ dependencies = [ "winnow", ] +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.22.0", + "bytes", + "h2 0.4.7", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.0", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project 1.1.5", + "prost 0.13.4", + "rustls-native-certs", + "rustls-pemfile 2.2.0", + "socket2 0.5.5", + "tokio", + "tokio-rustls", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.4.13" @@ -7804,21 +8411,40 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "pin-project", + "indexmap 1.9.3", + "pin-project 1.1.5", "pin-project-lite", + "rand", + "slab", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.1", + "tokio", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-http" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.8.0", "bytes", "futures-util", "http 1.1.0", @@ -7845,9 +8471,21 @@ checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tower-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674" +dependencies = [ + "futures-core", + "futures-util", + "pin-project 0.4.30", + "tower-service", +] [[package]] name = "tracing" @@ -7868,7 +8506,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.60", "time", "tracing-subscriber", ] @@ -7879,9 +8517,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -7996,13 +8634,13 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna", + "idna 0.4.0", "ipnet", "once_cell", "rand", "smallvec 1.13.2", "socket2 0.5.5", - "thiserror", + "thiserror 1.0.60", "tinyvec", "tokio", "tracing", @@ -8024,7 +8662,7 @@ dependencies = [ "rand", "resolv-conf", "smallvec 1.13.2", - "thiserror", + "thiserror 1.0.60", "tokio", "tracing", "trust-dns-proto", @@ -8050,7 +8688,7 @@ dependencies = [ "log", "rand", "sha1", - "thiserror", + "thiserror 1.0.60", "url", "utf-8", ] @@ -8075,7 +8713,7 @@ dependencies = [ "rand", "ring 0.17.8", "stun", - "thiserror", + "thiserror 1.0.60", "tokio", "tokio-util", "webrtc-util", @@ -8244,12 +8882,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna", + "idna 1.0.3", "percent-encoding", ] @@ -8259,6 +8897,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[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 = "utf8parse" version = "0.2.1" @@ -8341,7 +8991,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.60", ] [[package]] @@ -8385,7 +9035,7 @@ dependencies = [ "mime_guess", "multer", "percent-encoding", - "pin-project", + "pin-project 1.1.5", "rustls-pemfile 1.0.3", "scoped-tls", "serde", @@ -8430,9 +9080,9 @@ checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", "wasm-bindgen-shared", ] @@ -8465,9 +9115,9 @@ version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8498,10 +9148,23 @@ version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", ] +[[package]] +name = "wasm-streams" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasm-timer" version = "0.2.5" @@ -8538,6 +9201,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webrtc" version = "0.11.0" @@ -8558,14 +9231,14 @@ dependencies = [ "ring 0.17.8", "rtcp", "rtp", - "rustls 0.23.20", + "rustls 0.23.21", "sdp", "serde", "serde_json", "sha2 0.10.8", "smol_str", "stun", - "thiserror", + "thiserror 1.0.60", "time", "tokio", "turn", @@ -8589,7 +9262,7 @@ dependencies = [ "bytes", "log", "portable-atomic", - "thiserror", + "thiserror 1.0.60", "tokio", "webrtc-sctp", "webrtc-util", @@ -8618,13 +9291,13 @@ dependencies = [ "rand_core", "rcgen 0.13.1", "ring 0.17.8", - "rustls 0.23.20", + "rustls 0.23.21", "sec1", "serde", "sha1", "sha2 0.10.8", "subtle", - "thiserror", + "thiserror 1.0.60", "tokio", "webrtc-util", "x25519-dalek", @@ -8645,7 +9318,7 @@ dependencies = [ "serde", "serde_json", "stun", - "thiserror", + "thiserror 1.0.60", "tokio", "turn", "url", @@ -8662,7 +9335,7 @@ source = "git+https://github.com/openmina/webrtc.git?rev=e8705db39af1b198b324a5d dependencies = [ "log", "socket2 0.5.5", - "thiserror", + "thiserror 1.0.60", "tokio", "webrtc-util", ] @@ -8676,7 +9349,7 @@ dependencies = [ "bytes", "rand", "rtp", - "thiserror", + "thiserror 1.0.60", ] [[package]] @@ -8691,7 +9364,7 @@ dependencies = [ "log", "portable-atomic", "rand", - "thiserror", + "thiserror 1.0.60", "tokio", "webrtc-util", ] @@ -8723,7 +9396,7 @@ dependencies = [ "rtp", "sha1", "subtle", - "thiserror", + "thiserror 1.0.60", "tokio", "webrtc-util", ] @@ -8743,7 +9416,7 @@ dependencies = [ "nix 0.26.4", "portable-atomic", "rand", - "thiserror", + "thiserror 1.0.60", "tokio", "winapi 0.3.9", ] @@ -9083,6 +9756,18 @@ dependencies = [ "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" @@ -9117,7 +9802,7 @@ dependencies = [ "nom", "oid-registry 0.6.1", "rusticata-macros", - "thiserror", + "thiserror 1.0.60", "time", ] @@ -9135,7 +9820,7 @@ dependencies = [ "oid-registry 0.7.1", "ring 0.17.8", "rusticata-macros", - "thiserror", + "thiserror 1.0.60", "time", ] @@ -9164,7 +9849,7 @@ dependencies = [ "log", "nohash-hasher", "parking_lot 0.12.1", - "pin-project", + "pin-project 1.1.5", "rand", "static_assertions", ] @@ -9178,6 +9863,30 @@ dependencies = [ "time", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.35", + "syn 2.0.96", + "synstructure 0.13.1", +] + [[package]] name = "zerocopy" version = "0.7.32" @@ -9193,9 +9902,30 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.35", + "syn 2.0.96", + "synstructure 0.13.1", ] [[package]] @@ -9213,9 +9943,31 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.93", + "quote 1.0.35", + "syn 2.0.96", +] + +[[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 1.0.93", "quote 1.0.35", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index add1a35d76..889c49c481 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ members = [ "tools/salsa-simple", "tools/fuzzing", "tools/archive-breadcrumb-compare", + "tools/heartbeats-processor", "producer-dashboard", "fuzzer", diff --git a/tools/heartbeats-processor/.gitignore b/tools/heartbeats-processor/.gitignore new file mode 100644 index 0000000000..c6f9ae0c43 --- /dev/null +++ b/tools/heartbeats-processor/.gitignore @@ -0,0 +1,5 @@ + +/data +/credentials +.env +*.db \ No newline at end of file diff --git a/tools/heartbeats-processor/.sqlx/query-12261ecc56a9408bc7b95eb66dd939823a72b071adc7428ef353375982274d7f.json b/tools/heartbeats-processor/.sqlx/query-12261ecc56a9408bc7b95eb66dd939823a72b071adc7428ef353375982274d7f.json new file mode 100644 index 0000000000..fc5b537638 --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-12261ecc56a9408bc7b95eb66dd939823a72b071adc7428ef353375982274d7f.json @@ -0,0 +1,38 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT\n pk.public_key,\n ss.score,\n ss.blocks_produced,\n ss.last_updated\n FROM submitter_scores ss\n JOIN public_keys pk ON pk.id = ss.public_key_id\n ORDER BY ss.score DESC\n ", + "describe": { + "columns": [ + { + "name": "public_key", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "score", + "ordinal": 1, + "type_info": "Integer" + }, + { + "name": "blocks_produced", + "ordinal": 2, + "type_info": "Integer" + }, + { + "name": "last_updated", + "ordinal": 3, + "type_info": "Integer" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "12261ecc56a9408bc7b95eb66dd939823a72b071adc7428ef353375982274d7f" +} diff --git a/tools/heartbeats-processor/.sqlx/query-1d954275ae05319000ad2b298491ffab0747985e8af22a52ac0ed77c3cf27e64.json b/tools/heartbeats-processor/.sqlx/query-1d954275ae05319000ad2b298491ffab0747985e8af22a52ac0ed77c3cf27e64.json new file mode 100644 index 0000000000..ec9715b3df --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-1d954275ae05319000ad2b298491ffab0747985e8af22a52ac0ed77c3cf27e64.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n UPDATE time_windows\n SET disabled = ?1\n WHERE start_time >= ?2 AND end_time < ?3\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "1d954275ae05319000ad2b298491ffab0747985e8af22a52ac0ed77c3cf27e64" +} diff --git a/tools/heartbeats-processor/.sqlx/query-22a52592b753ea43f7e33af6166f9b8f74f03c087618df92e9b9062c8af30314.json b/tools/heartbeats-processor/.sqlx/query-22a52592b753ea43f7e33af6166f9b8f74f03c087618df92e9b9062c8af30314.json new file mode 100644 index 0000000000..7adb655c92 --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-22a52592b753ea43f7e33af6166f9b8f74f03c087618df92e9b9062c8af30314.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO time_windows (start_time, end_time) VALUES (?1, ?2) RETURNING id", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Integer" + } + ], + "parameters": { + "Right": 2 + }, + "nullable": [ + false + ] + }, + "hash": "22a52592b753ea43f7e33af6166f9b8f74f03c087618df92e9b9062c8af30314" +} diff --git a/tools/heartbeats-processor/.sqlx/query-25c9e074156b792e92cbfbaf5954647bfdda59b680b5a393c5324dd6d8a19683.json b/tools/heartbeats-processor/.sqlx/query-25c9e074156b792e92cbfbaf5954647bfdda59b680b5a393c5324dd6d8a19683.json new file mode 100644 index 0000000000..ea821622ea --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-25c9e074156b792e92cbfbaf5954647bfdda59b680b5a393c5324dd6d8a19683.json @@ -0,0 +1,38 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT\n pk.public_key,\n ss.score,\n ss.blocks_produced,\n datetime(ss.last_updated, 'unixepoch') as last_updated\n FROM submitter_scores ss\n JOIN public_keys pk ON pk.id = ss.public_key_id\n ORDER BY ss.score DESC, ss.blocks_produced DESC\n ", + "describe": { + "columns": [ + { + "name": "public_key", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "score", + "ordinal": 1, + "type_info": "Integer" + }, + { + "name": "blocks_produced", + "ordinal": 2, + "type_info": "Integer" + }, + { + "name": "last_updated", + "ordinal": 3, + "type_info": "Text" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false, + false, + true + ] + }, + "hash": "25c9e074156b792e92cbfbaf5954647bfdda59b680b5a393c5324dd6d8a19683" +} diff --git a/tools/heartbeats-processor/.sqlx/query-30837ba4832ee31eee8b4568a9b431d3277c344bd0e396e6ae199de5ad85f82a.json b/tools/heartbeats-processor/.sqlx/query-30837ba4832ee31eee8b4568a9b431d3277c344bd0e396e6ae199de5ad85f82a.json new file mode 100644 index 0000000000..9ae54e9c3c --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-30837ba4832ee31eee8b4568a9b431d3277c344bd0e396e6ae199de5ad85f82a.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n UPDATE time_windows\n SET disabled = TRUE\n WHERE start_time >= ? AND end_time <= ?\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "30837ba4832ee31eee8b4568a9b431d3277c344bd0e396e6ae199de5ad85f82a" +} diff --git a/tools/heartbeats-processor/.sqlx/query-6a6910daae1887a4e1a8570c8fb06d1d585460f13b56a45d49fe2fc7447829a9.json b/tools/heartbeats-processor/.sqlx/query-6a6910daae1887a4e1a8570c8fb06d1d585460f13b56a45d49fe2fc7447829a9.json new file mode 100644 index 0000000000..9bcdf4f032 --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-6a6910daae1887a4e1a8570c8fb06d1d585460f13b56a45d49fe2fc7447829a9.json @@ -0,0 +1,32 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT id, start_time, end_time\n FROM time_windows\n WHERE start_time <= ?2 AND end_time >= ?1 AND disabled = FALSE\n ORDER BY start_time ASC\n ", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Integer" + }, + { + "name": "start_time", + "ordinal": 1, + "type_info": "Integer" + }, + { + "name": "end_time", + "ordinal": 2, + "type_info": "Integer" + } + ], + "parameters": { + "Right": 2 + }, + "nullable": [ + true, + false, + false + ] + }, + "hash": "6a6910daae1887a4e1a8570c8fb06d1d585460f13b56a45d49fe2fc7447829a9" +} diff --git a/tools/heartbeats-processor/.sqlx/query-93720d0caecab27616c2826d0a188fabef65f281986d72ffffab0dd58369a673.json b/tools/heartbeats-processor/.sqlx/query-93720d0caecab27616c2826d0a188fabef65f281986d72ffffab0dd58369a673.json new file mode 100644 index 0000000000..2a250fe40d --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-93720d0caecab27616c2826d0a188fabef65f281986d72ffffab0dd58369a673.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "SELECT last_processed_time FROM processing_state WHERE id = 1", + "describe": { + "columns": [ + { + "name": "last_processed_time", + "ordinal": 0, + "type_info": "Integer" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false + ] + }, + "hash": "93720d0caecab27616c2826d0a188fabef65f281986d72ffffab0dd58369a673" +} diff --git a/tools/heartbeats-processor/.sqlx/query-98b0cf1049d82f39c4ba61515174126b1be6873040411debedab5b27ca2606b9.json b/tools/heartbeats-processor/.sqlx/query-98b0cf1049d82f39c4ba61515174126b1be6873040411debedab5b27ca2606b9.json new file mode 100644 index 0000000000..aa1bf181f6 --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-98b0cf1049d82f39c4ba61515174126b1be6873040411debedab5b27ca2606b9.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "SELECT COUNT(*) as count FROM time_windows WHERE disabled = FALSE", + "describe": { + "columns": [ + { + "name": "count", + "ordinal": 0, + "type_info": "Integer" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false + ] + }, + "hash": "98b0cf1049d82f39c4ba61515174126b1be6873040411debedab5b27ca2606b9" +} diff --git a/tools/heartbeats-processor/.sqlx/query-a29bafe3ddeef887e7e08390d03f664302deac08315a30a499a569310799055a.json b/tools/heartbeats-processor/.sqlx/query-a29bafe3ddeef887e7e08390d03f664302deac08315a30a499a569310799055a.json new file mode 100644 index 0000000000..9434b2dee2 --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-a29bafe3ddeef887e7e08390d03f664302deac08315a30a499a569310799055a.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n UPDATE time_windows \n SET disabled = TRUE \n WHERE (start_time < ?1 OR end_time > ?2) \n AND disabled = FALSE\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "a29bafe3ddeef887e7e08390d03f664302deac08315a30a499a569310799055a" +} diff --git a/tools/heartbeats-processor/.sqlx/query-a76de53b5c3443bee6539d261edd2044fc771fbf0e9e6d94d61be4e5a31d91f6.json b/tools/heartbeats-processor/.sqlx/query-a76de53b5c3443bee6539d261edd2044fc771fbf0e9e6d94d61be4e5a31d91f6.json new file mode 100644 index 0000000000..e41d04efc1 --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-a76de53b5c3443bee6539d261edd2044fc771fbf0e9e6d94d61be4e5a31d91f6.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "SELECT id FROM time_windows WHERE start_time = ?1 AND end_time = ?2", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Integer" + } + ], + "parameters": { + "Right": 2 + }, + "nullable": [ + true + ] + }, + "hash": "a76de53b5c3443bee6539d261edd2044fc771fbf0e9e6d94d61be4e5a31d91f6" +} diff --git a/tools/heartbeats-processor/.sqlx/query-bc586a064ad3094fe93bf09715e00c3638e403705c437816d56de4af3fcbdb17.json b/tools/heartbeats-processor/.sqlx/query-bc586a064ad3094fe93bf09715e00c3638e403705c437816d56de4af3fcbdb17.json new file mode 100644 index 0000000000..f8a843138a --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-bc586a064ad3094fe93bf09715e00c3638e403705c437816d56de4af3fcbdb17.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n INSERT INTO submitter_scores (public_key_id, score, blocks_produced)\n SELECT\n pk.id,\n COUNT(DISTINCT hp.window_id) as score,\n COUNT(DISTINCT pb.id) as blocks_produced\n FROM public_keys pk\n LEFT JOIN heartbeat_presence hp ON pk.id = hp.public_key_id\n LEFT JOIN time_windows tw ON hp.window_id = tw.id\n LEFT JOIN produced_blocks pb ON pk.id = pb.public_key_id\n WHERE tw.disabled = FALSE\n GROUP BY pk.id\n ON CONFLICT(public_key_id) DO UPDATE SET\n score = excluded.score,\n blocks_produced = excluded.blocks_produced,\n last_updated = strftime('%s', 'now')\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 0 + }, + "nullable": [] + }, + "hash": "bc586a064ad3094fe93bf09715e00c3638e403705c437816d56de4af3fcbdb17" +} diff --git a/tools/heartbeats-processor/.sqlx/query-d2f074d1223c3a5e1b0cb54a2c18828b5e176624ae2ae9a5fb0412e8bf3f29f0.json b/tools/heartbeats-processor/.sqlx/query-d2f074d1223c3a5e1b0cb54a2c18828b5e176624ae2ae9a5fb0412e8bf3f29f0.json new file mode 100644 index 0000000000..6d645480c0 --- /dev/null +++ b/tools/heartbeats-processor/.sqlx/query-d2f074d1223c3a5e1b0cb54a2c18828b5e176624ae2ae9a5fb0412e8bf3f29f0.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE processing_state SET last_processed_time = ? WHERE id = 1", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "d2f074d1223c3a5e1b0cb54a2c18828b5e176624ae2ae9a5fb0412e8bf3f29f0" +} diff --git a/tools/heartbeats-processor/Cargo.toml b/tools/heartbeats-processor/Cargo.toml new file mode 100644 index 0000000000..67fda5436f --- /dev/null +++ b/tools/heartbeats-processor/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "heartbeats-processor" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1.28", features = ["full", "time"] } +firestore = "0.44" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +anyhow = "1.0" +chrono = "0.4" +sqlx = { version = "0.8", features = ["runtime-tokio-native-tls", "sqlite", "chrono"] } +dotenv = "0.15" +clap = { version = "4.4", features = ["derive"] } +gcloud-sdk = { version = "0.26.0", default-features = false, features = ["google-firestore-v1"] } +base64 = "0.22" + +mina-p2p-messages = { workspace = true } +openmina-core = { path = "../../core" } \ No newline at end of file diff --git a/tools/heartbeats-processor/Dockerfile b/tools/heartbeats-processor/Dockerfile new file mode 100644 index 0000000000..f13a04b697 --- /dev/null +++ b/tools/heartbeats-processor/Dockerfile @@ -0,0 +1,24 @@ +# Build stage +FROM rust:1.84-slim-bookworm AS builder + +WORKDIR /usr/src/app +RUN apt-get update && apt-get install -y pkg-config libssl-dev && rm -rf /var/lib/apt/lists/* + +COPY . . +RUN ls -la tools/heartbeats-processor +RUN cargo build --release -p heartbeats-processor + +# Runtime stage +FROM debian:bookworm-slim + +RUN apt-get update && apt-get install -y libsqlite3-0 ca-certificates && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY --from=builder /usr/src/app/target/release/heartbeats-processor . +COPY tools/heartbeats-processor/schema.sql . + +ENV DATABASE_PATH=/app/data/heartbeats.db + +ENTRYPOINT ["./heartbeats-processor"] +CMD ["process-loop"] diff --git a/tools/heartbeats-processor/README.md b/tools/heartbeats-processor/README.md new file mode 100644 index 0000000000..3334d2a05b --- /dev/null +++ b/tools/heartbeats-processor/README.md @@ -0,0 +1,44 @@ +# Heartbeats Processor + +This application processes "heartbeat" entries from Firestore. It fetches data, groups it by time windows, and stores the results into a local SQLite database for further analysis or reporting. + +## Environment Variables + +The following environment variables control the program's behavior. + +These variables can be set in your shell environment or in a `.env` file in the project root directory. + +### Required Variables +* `DATABASE_PATH` - SQLite database path (e.g., "./data.db") +* `GOOGLE_CLOUD_PROJECT` - Google Cloud project ID +* `WINDOW_RANGE_START` - Start time for window creation in RFC3339 format +* `WINDOW_RANGE_END` - End time for window creation in RFC3339 format + +### Optional Variables +* `GOOGLE_APPLICATION_CREDENTIALS` - Path to Google Cloud credentials file +* `DISABLED_WINDOWS` - Comma-separated list of time ranges to disable in RFC3339 format (e.g., `2023-01-01T00:00:00Z/2023-01-02T00:00:00Z,2023-02-01T00:00:00Z/2023-02-02T00:00:00Z`) + +## Development With Firestore Emulator + +To develop locally using the Firestore Emulator, do the following: + +1. Set these environment variables in your shell: + + ``` + FIRESTORE_EMULATOR_HOST=127.0.0.1:8080 + GOOGLE_CLOUD_PROJECT=staging + ``` + +2. From the "frontend/firestore" directory, start the emulator by running: + + ``` + npm run serve + ``` + +3. Authenticate on your local machine with Google Cloud to allow proper credential usage: + + ``` + gcloud auth application-default login + ``` + +Once these steps are complete, the application can connect to the local emulator to simulate production-like Firestore behavior for debugging or development. diff --git a/tools/heartbeats-processor/docker-compose.yml b/tools/heartbeats-processor/docker-compose.yml new file mode 100644 index 0000000000..0648fd8283 --- /dev/null +++ b/tools/heartbeats-processor/docker-compose.yml @@ -0,0 +1,23 @@ +version: '3.8' + +services: + heartbeats-processor: + #build: . + image: openmina/heartbeat-processor:local + environment: + - GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT:-staging} + - WINDOW_RANGE_START=${WINDOW_RANGE_START:-} + - WINDOW_RANGE_END=${WINDOW_RANGE_END:-} + #- FIRESTORE_EMULATOR_HOST=${FIRESTORE_EMULATOR_HOST:-} + - DISABLED_WINDOWS=${DISABLED_WINDOWS:-} + - GOOGLE_APPLICATION_CREDENTIALS=${GOOGLE_APPLICATION_CREDENTIALS:-/credentials/service-account.json} + - DATABASE_PATH=${DATABASE_PATH:-/app/data/store.db} + volumes: + - ./data:/app/data + - ./credentials:/credentials:ro + command: ["process-loop", "--interval-seconds", "300"] + restart: unless-stopped + +volumes: + data: + driver: local diff --git a/tools/heartbeats-processor/schema.sql b/tools/heartbeats-processor/schema.sql new file mode 100644 index 0000000000..f1a99dd4e6 --- /dev/null +++ b/tools/heartbeats-processor/schema.sql @@ -0,0 +1,112 @@ +CREATE TABLE IF NOT EXISTS public_keys ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + public_key TEXT NOT NULL UNIQUE +); + +CREATE TABLE IF NOT EXISTS submitter_counts ( + public_key_id INTEGER PRIMARY KEY, + count INTEGER NOT NULL, + last_seen INTEGER NOT NULL, -- Unix timestamp + updated_at INTEGER DEFAULT (strftime('%s', 'now')), + FOREIGN KEY (public_key_id) REFERENCES public_keys(id) +); + +CREATE TABLE IF NOT EXISTS processing_state ( + id INTEGER PRIMARY KEY, + last_processed_time INTEGER NOT NULL -- Unix timestamp +); + +INSERT OR IGNORE INTO processing_state (id, last_processed_time) +VALUES (1, 0); + +CREATE TABLE IF NOT EXISTS time_windows ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + start_time INTEGER NOT NULL, -- Unix timestamp + end_time INTEGER NOT NULL, -- Unix timestamp + disabled BOOLEAN NOT NULL DEFAULT FALSE, + UNIQUE(start_time, end_time) +); + +CREATE TABLE IF NOT EXISTS heartbeat_presence ( + window_id INTEGER NOT NULL, + public_key_id INTEGER NOT NULL, + best_tip_hash TEXT NOT NULL, + best_tip_height INTEGER NOT NULL, + best_tip_global_slot INTEGER NOT NULL, + heartbeat_time INTEGER NOT NULL, + disabled BOOLEAN NOT NULL DEFAULT FALSE, + PRIMARY KEY (window_id, public_key_id), + FOREIGN KEY (window_id) REFERENCES time_windows(id), + FOREIGN KEY (public_key_id) REFERENCES public_keys(id) +); + +CREATE TABLE IF NOT EXISTS submitter_scores ( + public_key_id INTEGER PRIMARY KEY, + score INTEGER NOT NULL DEFAULT 0, + last_updated INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), + blocks_produced INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (public_key_id) REFERENCES public_keys(id) +); + +CREATE TABLE IF NOT EXISTS produced_blocks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + window_id INTEGER NOT NULL, + public_key_id INTEGER NOT NULL, + block_hash TEXT NOT NULL, + block_height INTEGER NOT NULL, + block_global_slot INTEGER NOT NULL, + block_data_blob TEXT, -- Raw block data in base64-encoded binprot format + validated BOOLEAN NOT NULL DEFAULT FALSE, + UNIQUE(public_key_id, block_hash), + FOREIGN KEY (window_id, public_key_id) REFERENCES heartbeat_presence(window_id, public_key_id), + FOREIGN KEY (window_id) REFERENCES time_windows(id), + FOREIGN KEY (public_key_id) REFERENCES public_keys(id) +); + +-- Index for time window queries +CREATE INDEX IF NOT EXISTS idx_time_windows_start_end +ON time_windows(start_time, end_time); + +-- Index for public key lookups +CREATE INDEX IF NOT EXISTS idx_public_keys_key +ON public_keys(public_key); + +-- Index for presence queries by window +CREATE INDEX IF NOT EXISTS idx_heartbeat_presence_window +ON heartbeat_presence(window_id); + +-- Index for presence queries by public key +CREATE INDEX IF NOT EXISTS idx_heartbeat_presence_pubkey +ON heartbeat_presence(public_key_id); + +-- Index for presence queries by global slot +CREATE INDEX IF NOT EXISTS idx_heartbeat_presence_global_slot +ON heartbeat_presence(best_tip_global_slot); + +-- Index for submitter counts lookup +CREATE INDEX IF NOT EXISTS idx_submitter_counts_last_seen +ON submitter_counts(last_seen); + +-- Index for submitter scores lookup +CREATE INDEX IF NOT EXISTS idx_submitter_scores_score +ON submitter_scores(score DESC); + +-- Index for produced blocks queries by window +CREATE INDEX IF NOT EXISTS idx_produced_blocks_window +ON produced_blocks(window_id); + +-- Index for produced blocks queries by public key +CREATE INDEX IF NOT EXISTS idx_produced_blocks_pubkey +ON produced_blocks(public_key_id); + +-- Index for produced blocks queries by block hash +CREATE INDEX IF NOT EXISTS idx_produced_blocks_hash +ON produced_blocks(block_hash); + +-- Combined index for window and public key lookups +CREATE INDEX IF NOT EXISTS idx_produced_blocks_window_pubkey +ON produced_blocks(window_id, public_key_id); + +-- Index for global slot queries +CREATE INDEX IF NOT EXISTS idx_produced_blocks_global_slot +ON produced_blocks(block_global_slot); diff --git a/tools/heartbeats-processor/src/config.rs b/tools/heartbeats-processor/src/config.rs new file mode 100644 index 0000000000..17a6ac925f --- /dev/null +++ b/tools/heartbeats-processor/src/config.rs @@ -0,0 +1,116 @@ +use anyhow::{Context, Result}; +use chrono::{DateTime, Utc}; +use std::fmt; + +#[derive(Debug, Clone)] +pub struct Config { + pub google_cloud_project: String, + pub google_credentials_path: Option, + pub firestore_emulator_host: Option, + pub database_url: String, + pub window_range_start: DateTime, + pub window_range_end: DateTime, + pub disabled_windows: Vec<(DateTime, DateTime)>, +} + +impl Config { + pub fn from_env() -> Result { + dotenv::dotenv().ok(); + + let google_cloud_project = + std::env::var("GOOGLE_CLOUD_PROJECT").unwrap_or_else(|_| "local".to_string()); + + let google_credentials_path = std::env::var("GOOGLE_APPLICATION_CREDENTIALS").ok(); + let firestore_emulator_host = std::env::var("FIRESTORE_EMULATOR_HOST").ok(); + + let database_url = std::env::var("DATABASE_PATH") + .map(|path| format!("sqlite:{}", path)) + .unwrap_or_else(|_| format!("sqlite:heartbeats-{}.db", google_cloud_project)); + + let window_range_start = + std::env::var("WINDOW_RANGE_START").context("WINDOW_RANGE_START must be set")?; + let window_range_start = DateTime::parse_from_rfc3339(&window_range_start) + .context("Failed to parse WINDOW_RANGE_START as RFC3339")? + .with_timezone(&Utc); + + let window_range_end = + std::env::var("WINDOW_RANGE_END").context("WINDOW_RANGE_END must be set")?; + let window_range_end = DateTime::parse_from_rfc3339(&window_range_end) + .context("Failed to parse WINDOW_RANGE_END as RFC3339")? + .with_timezone(&Utc); + + if window_range_start >= window_range_end { + anyhow::bail!("WINDOW_RANGE_START must be before WINDOW_RANGE_END"); + } + + let disabled_windows = if let Ok(ranges) = std::env::var("DISABLED_WINDOWS") { + let mut windows = Vec::new(); + for range in ranges.split(',').filter(|s| !s.is_empty()) { + let mut parts = range.split('/'); + let start = parts.next().ok_or_else(|| { + anyhow::anyhow!("Missing start time in disabled window range") + })?; + let end = parts + .next() + .ok_or_else(|| anyhow::anyhow!("Missing end time in disabled window range"))?; + + let start = DateTime::parse_from_rfc3339(start) + .with_context(|| { + format!("Failed to parse disabled window start time: {}", start) + })? + .with_timezone(&Utc); + let end = DateTime::parse_from_rfc3339(end) + .with_context(|| format!("Failed to parse disabled window end time: {}", end))? + .with_timezone(&Utc); + + if start >= end { + anyhow::bail!( + "Disabled window start time must be before end time: {start} >= {end}" + ); + } + if end < window_range_start || start > window_range_end { + println!("Warning: Disabled window {start} to {end} is outside the main window range"); + } + + windows.push((start, end)); + } + windows + } else { + Vec::new() + }; + + Ok(Config { + google_cloud_project, + google_credentials_path, + firestore_emulator_host, + database_url, + window_range_start, + window_range_end, + disabled_windows, + }) + } +} + +impl fmt::Display for Config { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Configuration:")?; + writeln!(f, " Project: {}", self.google_cloud_project)?; + if let Some(creds) = &self.google_credentials_path { + writeln!(f, " Credentials: {}", creds)?; + } + if let Some(emu) = &self.firestore_emulator_host { + writeln!(f, " Firestore Emulator: {}", emu)?; + } + writeln!(f, " Database: {}", self.database_url)?; + writeln!(f, " Window Range:")?; + writeln!(f, " Start: {}", self.window_range_start)?; + writeln!(f, " End: {}", self.window_range_end)?; + if !self.disabled_windows.is_empty() { + writeln!(f, " Disabled Windows:")?; + for (start, end) in &self.disabled_windows { + writeln!(f, " {} to {}", start, end)?; + } + } + Ok(()) + } +} diff --git a/tools/heartbeats-processor/src/local_db.rs b/tools/heartbeats-processor/src/local_db.rs new file mode 100644 index 0000000000..5b29c65f45 --- /dev/null +++ b/tools/heartbeats-processor/src/local_db.rs @@ -0,0 +1,645 @@ +use anyhow::Result; +use chrono::{DateTime, Utc}; +use firestore::FirestoreDb; +use serde::Serialize; + +use sqlx::{Row, SqlitePool}; +use std::collections::{HashMap, HashSet}; +use std::fs; + +use crate::config::Config; +use crate::remote_db::BlockInfo; +use crate::time::*; + +#[derive(Debug)] +pub struct HeartbeatPresence { + pub window_id: i64, + pub public_key_id: i64, + pub best_tip: BlockInfo, + pub heartbeat_time: i64, +} + +#[derive(Debug)] +pub struct ProducedBlock { + pub window_id: i64, + pub public_key_id: i64, + pub block_hash: String, + pub block_height: u32, + pub block_global_slot: u32, + pub block_data: String, +} + +pub async fn get_last_processed_time(pool: &SqlitePool) -> Result> { + let record = sqlx::query!("SELECT last_processed_time FROM processing_state WHERE id = 1") + .fetch_one(pool) + .await?; + + Ok(from_unix_timestamp(record.last_processed_time)) +} + +pub async fn update_last_processed_time(pool: &SqlitePool, time: DateTime) -> Result<()> { + let current = get_last_processed_time(pool).await?; + let ts = to_unix_timestamp(time); + + println!("Updating last processed time: {} -> {}", current, time); + + sqlx::query!( + "UPDATE processing_state SET last_processed_time = ? WHERE id = 1", + ts + ) + .execute(pool) + .await?; + + Ok(()) +} + +pub async fn ensure_time_windows( + pool: &SqlitePool, + start: DateTime, + end: DateTime, +) -> Result> { + let windows = generate_fixed_time_windows(start, end); + let mut window_ids = Vec::new(); + + for window in windows { + let start_ts = to_unix_timestamp(window.start); + let end_ts = to_unix_timestamp(window.end); + + // Try to get existing window ID first + let existing_id = sqlx::query!( + "SELECT id FROM time_windows WHERE start_time = ?1 AND end_time = ?2", + start_ts, + end_ts, + ) + .fetch_optional(pool) + .await?; + + let id = if let Some(record) = existing_id { + record + .id + .expect("ID should not be None for an existing record") + } else { + sqlx::query!( + "INSERT INTO time_windows (start_time, end_time) VALUES (?1, ?2) RETURNING id", + start_ts, + end_ts, + ) + .fetch_one(pool) + .await? + .id + }; + + window_ids.push(id); + } + + Ok(window_ids) +} + +pub async fn ensure_public_keys( + pool: &SqlitePool, + public_keys: &[&str], +) -> Result> { + let mut map = HashMap::new(); + + // Create a single query with multiple values + let values = public_keys + .iter() + .map(|k| format!("('{}')", k)) + .collect::>() + .join(","); + + let query = format!( + r#" + INSERT INTO public_keys (public_key) + VALUES {} + ON CONFLICT (public_key) DO UPDATE SET + public_key = excluded.public_key + RETURNING id, public_key + "#, + values + ); + + let rows = sqlx::query(&query).fetch_all(pool).await?; + + for row in rows { + let id: i64 = row.get("id"); + let key: String = row.get("public_key"); + map.insert(key, id); + } + + Ok(map) +} + +pub async fn batch_insert_presence( + pool: &SqlitePool, + presences: &[HeartbeatPresence], +) -> Result<()> { + if presences.is_empty() { + return Ok(()); + } + + let values = presences + .iter() + .map(|p| { + format!( + "({}, {}, '{}', {}, {}, {})", + p.window_id, + p.public_key_id, + p.best_tip.hash, + p.best_tip.height, + p.best_tip.global_slot, + p.heartbeat_time + ) + }) + .collect::>() + .join(","); + + let query = format!( + r#" + INSERT INTO heartbeat_presence ( + window_id, public_key_id, + best_tip_hash, best_tip_height, best_tip_global_slot, + heartbeat_time + ) + VALUES {} + ON CONFLICT(window_id, public_key_id) + DO UPDATE SET + best_tip_hash = CASE + WHEN excluded.best_tip_global_slot >= best_tip_global_slot + THEN excluded.best_tip_hash + ELSE best_tip_hash + END, + best_tip_height = CASE + WHEN excluded.best_tip_global_slot >= best_tip_global_slot + THEN excluded.best_tip_height + ELSE best_tip_height + END, + best_tip_global_slot = CASE + WHEN excluded.best_tip_global_slot >= best_tip_global_slot + THEN excluded.best_tip_global_slot + ELSE best_tip_global_slot + END, + heartbeat_time = CASE + WHEN excluded.best_tip_global_slot >= best_tip_global_slot + THEN excluded.heartbeat_time + ELSE heartbeat_time + END + "#, + values + ); + + sqlx::query(&query).execute(pool).await?; + + Ok(()) +} + +async fn batch_insert_produced_blocks(pool: &SqlitePool, blocks: &[ProducedBlock]) -> Result<()> { + if blocks.is_empty() { + return Ok(()); + } + + let values = blocks + .iter() + .map(|b| { + format!( + "({}, {}, '{}', {}, {}, '{}')", + b.window_id, + b.public_key_id, + b.block_hash, + b.block_height, + b.block_global_slot, + b.block_data.replace('\'', "''") + ) + }) + .collect::>() + .join(","); + + let query = format!( + r#" + INSERT INTO produced_blocks ( + window_id, public_key_id, + block_hash, block_height, block_global_slot, + block_data_blob + ) + VALUES {} + ON CONFLICT(public_key_id, block_hash) DO NOTHING + "#, + values + ); + + sqlx::query(&query).execute(pool).await?; + + Ok(()) +} + +pub async fn process_heartbeats(db: &FirestoreDb, pool: &SqlitePool) -> Result<()> { + let last_processed_time = get_last_processed_time(pool).await?; + let now = Utc::now(); + + let heartbeats = + crate::remote_db::fetch_heartbeats_in_chunks(db, last_processed_time, now).await?; + println!("Fetched {} heartbeats", heartbeats.len()); + println!("heartbeat {:?}", heartbeats.first().map(|x| x.create_time)); + + if heartbeats.is_empty() { + return Ok(()); + } + + let mut latest_time = last_processed_time; + latest_time = heartbeats + .iter() + .map(|h| h.create_time) + .max() + .unwrap_or(latest_time); + + let start_ts = to_unix_timestamp(last_processed_time); + let end_ts = to_unix_timestamp(latest_time); + + let existing_windows = sqlx::query!( + r#" + SELECT id, start_time, end_time + FROM time_windows + WHERE start_time <= ?2 AND end_time >= ?1 AND disabled = FALSE + ORDER BY start_time ASC + "#, + start_ts, + end_ts + ) + .fetch_all(pool) + .await?; + + let unique_submitters: HashSet<&str> = heartbeats + .iter() + .map(|entry| entry.submitter.as_str()) + .collect(); + + let public_key_map = + ensure_public_keys(pool, &unique_submitters.into_iter().collect::>()).await?; + + let mut presence_count = 0; + let mut skipped_count = 0; + let mut blocks_recorded = 0; + let mut blocks_duplicate = 0; + let mut processed_heartbeats = HashSet::new(); + let mut produced_blocks_batch = Vec::new(); + let mut seen_blocks = HashSet::new(); + + for window in existing_windows { + let window_start = from_unix_timestamp(window.start_time); + let window_end = from_unix_timestamp(window.end_time); + let mut presence_batch = Vec::new(); + + for (idx, entry) in heartbeats.iter().enumerate() { + if entry.create_time >= window_start && entry.create_time < window_end { + processed_heartbeats.insert(idx); + + let best_tip = entry.best_tip_block(); + + if entry.is_synced() && best_tip.is_some() { + if let Some(&public_key_id) = public_key_map.get(&entry.submitter) { + presence_batch.push(HeartbeatPresence { + window_id: window.id.unwrap(), + public_key_id, + best_tip: best_tip.unwrap(), // Cannot fail due to the above check + heartbeat_time: to_unix_timestamp(entry.create_time), + }); + presence_count += 1; + + // Add produced block if it exists + if let Some(block) = entry.last_produced_block_decoded() { + let block_data = entry.last_produced_block_raw().unwrap(); // Cannot fail, we have the block + let key = (public_key_id, block.hash().to_string()); + + if !seen_blocks.insert(key.clone()) { + blocks_duplicate += 1; + println!( + "Duplicate block detected: {} (producer: {})", + key.1, entry.submitter + ); + continue; + } + + produced_blocks_batch.push(ProducedBlock { + window_id: window.id.unwrap(), + public_key_id, + block_hash: block.hash().to_string(), + block_height: block.height(), + block_global_slot: block.global_slot(), + block_data, + }); + } + } + } else { + skipped_count += 1; + } + } + } + + if !presence_batch.is_empty() { + batch_insert_presence(pool, &presence_batch).await?; + } + } + + if !produced_blocks_batch.is_empty() { + blocks_recorded = produced_blocks_batch.len(); + batch_insert_produced_blocks(pool, &produced_blocks_batch).await?; + } + + let outside_windows = heartbeats.len() - processed_heartbeats.len(); + + println!( + "Processed {} heartbeats ({} synced presences recorded, {} unique blocks recorded ({} duplicates skipped), {} unsynced skipped), {} outside of defined windows", + processed_heartbeats.len(), + presence_count, + blocks_recorded, + blocks_duplicate, + skipped_count, + outside_windows + ); + + // Update the last processed time + if latest_time > last_processed_time { + update_last_processed_time(pool, latest_time).await?; + } + + Ok(()) +} + +pub async fn create_tables_from_file(pool: &SqlitePool) -> Result<()> { + println!("Initializing SQLite database schema..."); + let schema = fs::read_to_string("schema.sql")?; + sqlx::query(&schema).execute(pool).await?; + Ok(()) +} + +pub async fn toggle_windows( + pool: &SqlitePool, + start: String, + end: String, + disabled: bool, +) -> Result<()> { + let start_time = parse_datetime(&start)?; + let end_time = parse_datetime(&end)?; + + if start_time >= end_time { + return Err(anyhow::anyhow!("Start time must be before end time")); + } + + let start_ts = to_unix_timestamp(start_time); + let end_ts = to_unix_timestamp(end_time); + + let affected = sqlx::query!( + r#" + UPDATE time_windows + SET disabled = ?1 + WHERE start_time >= ?2 AND end_time < ?3 + "#, + disabled, + start_ts, + end_ts + ) + .execute(pool) + .await?; + + if affected.rows_affected() > 0 { + println!( + "{} windows {} successfully between {} and {}", + affected.rows_affected(), + if disabled { "disabled" } else { "enabled" }, + start_time, + end_time + ); + } else { + println!("No windows found in the specified range"); + } + Ok(()) +} + +// TODO: multiple blocks for the same slot should be counted as one +// TODO: take into account the validated flag to count blocks +pub async fn update_scores(pool: &SqlitePool) -> Result<()> { + sqlx::query!( + r#" + INSERT INTO submitter_scores (public_key_id, score, blocks_produced) + SELECT + pk.id, + COUNT(DISTINCT hp.window_id) as score, + COUNT(DISTINCT pb.id) as blocks_produced + FROM public_keys pk + LEFT JOIN heartbeat_presence hp ON pk.id = hp.public_key_id + LEFT JOIN time_windows tw ON hp.window_id = tw.id + LEFT JOIN produced_blocks pb ON pk.id = pb.public_key_id + WHERE tw.disabled = FALSE + GROUP BY pk.id + ON CONFLICT(public_key_id) DO UPDATE SET + score = excluded.score, + blocks_produced = excluded.blocks_produced, + last_updated = strftime('%s', 'now') + "# + ) + .execute(pool) + .await?; + Ok(()) +} + +#[derive(Debug, Serialize)] +pub struct MaxScores { + pub total: i64, + pub current: i64, +} + +pub async fn get_max_scores(pool: &SqlitePool) -> Result { + let total = sqlx::query!("SELECT COUNT(*) as count FROM time_windows WHERE disabled = FALSE") + .fetch_one(pool) + .await? + .count as i64; + + let current = sqlx::query_as::<_, (i64,)>( + r#" + SELECT COUNT(*) as count + FROM time_windows + WHERE end_time <= strftime('%s', 'now') + AND disabled = FALSE + "#, + ) + .fetch_one(pool) + .await? + .0; + + Ok(MaxScores { total, current }) +} + +pub async fn view_scores(pool: &SqlitePool) -> Result<()> { + // Make sure scores are up to date + update_scores(pool).await?; + + let scores = sqlx::query!( + r#" + SELECT + pk.public_key, + ss.score, + ss.blocks_produced, + datetime(ss.last_updated, 'unixepoch') as last_updated + FROM submitter_scores ss + JOIN public_keys pk ON pk.id = ss.public_key_id + ORDER BY ss.score DESC, ss.blocks_produced DESC + "# + ) + .fetch_all(pool) + .await?; + + let max_scores = get_max_scores(pool).await?; + + println!("\nSubmitter Scores:"); + println!("----------------------------------------"); + println!( + "Public Key | Score | Blocks | Current Max | Total Max | Last Updated" + ); + println!("----------------------------------------"); + + for row in scores { + println!( + "{:<40} | {:>5} | {:>6} | {:>11} | {:>9} | {}", + row.public_key, + row.score, + row.blocks_produced, + max_scores.current, + max_scores.total, + row.last_updated.unwrap_or_default() + ); + } + + Ok(()) +} + +pub fn ensure_db_exists(db_path: &str) -> Result<()> { + let file_path = db_path.strip_prefix("sqlite:").unwrap_or(db_path); + + if !std::path::Path::new(file_path).exists() { + std::fs::File::create(file_path)?; + } + + Ok(()) +} + +pub async fn set_last_processed_time(pool: &SqlitePool, time_str: &str) -> Result<()> { + // Try parsing with different formats + let dt = if let Ok(dt) = DateTime::parse_from_str( + &format!("{} 00:00:00 +0000", time_str), + "%Y-%m-%d %H:%M:%S %z", + ) { + dt.with_timezone(&Utc) + } else if let Ok(dt) = + DateTime::parse_from_str(&format!("{} +0000", time_str), "%Y-%m-%d %H:%M:%S %z") + { + dt.with_timezone(&Utc) + } else { + return Err(anyhow::anyhow!( + "Invalid time format. Expected YYYY-MM-DD or YYYY-MM-DD HH:MM:SS" + )); + }; + + let ts = to_unix_timestamp(dt); + sqlx::query!( + "UPDATE processing_state SET last_processed_time = ? WHERE id = 1", + ts + ) + .execute(pool) + .await?; + + println!("Last processed time set to: {}", dt); + Ok(()) +} + +pub async fn create_windows(pool: &SqlitePool, start: String, end: String) -> Result<()> { + let start_time = parse_datetime(&start)?; + let end_time = parse_datetime(&end)?; + + if start_time >= end_time { + return Err(anyhow::anyhow!("Start time must be before end time")); + } + + let window_ids = ensure_time_windows(pool, start_time, end_time).await?; + println!("Created {} time windows", window_ids.len()); + Ok(()) +} + +/// Ensures time windows exist in the database for a configured time range. +/// +/// This function uses environment variables to determine the range of windows to create: +/// - `WINDOW_RANGE_START`: The start time for window creation (RFC3339 format) +/// If not set, defaults to the current time +/// - `WINDOW_RANGE_END`: The end time for window creation (RFC3339 format) +/// If not set, defaults to start + 28 days +/// +/// Time windows are created at 5-minute intervals within this range. +/// Windows that already exist will be preserved, new ones will be created. +/// Any windows outside this range will be disabled. +pub async fn ensure_initial_windows(pool: &SqlitePool, config: &Config) -> Result<()> { + let start = config.window_range_start; + let end = config.window_range_end; + + println!("Ensuring time windows exist from {} to {}", start, end); + let window_ids = ensure_time_windows(pool, start, end).await?; + println!("Created/verified {} time windows", window_ids.len()); + + // Disable windows outside the configured range + let start_ts = to_unix_timestamp(start); + let end_ts = to_unix_timestamp(end); + + let affected = sqlx::query!( + r#" + UPDATE time_windows + SET disabled = TRUE + WHERE (start_time < ?1 OR end_time > ?2) + AND disabled = FALSE + "#, + start_ts, + end_ts + ) + .execute(pool) + .await?; + + if affected.rows_affected() > 0 { + println!( + "Disabled {} windows outside the configured range", + affected.rows_affected() + ); + } + + Ok(()) +} + +pub async fn mark_disabled_windows(pool: &SqlitePool, config: &Config) -> Result<()> { + if !config.disabled_windows.is_empty() { + println!("Processing disabled window ranges:"); + let mut affected_total = 0; + + for (start, end) in &config.disabled_windows { + println!(" {} to {}", start, end); + let start_ts = to_unix_timestamp(*start); + let end_ts = to_unix_timestamp(*end); + + let result = sqlx::query!( + r#" + UPDATE time_windows + SET disabled = TRUE + WHERE start_time >= ? AND end_time <= ? + "#, + start_ts, + end_ts + ) + .execute(pool) + .await?; + + affected_total += result.rows_affected(); + } + + if affected_total > 0 { + println!("✓ Disabled {} windows in the above ranges", affected_total); + } else { + println!("! No windows found in the configured disabled ranges"); + } + } + Ok(()) +} diff --git a/tools/heartbeats-processor/src/main.rs b/tools/heartbeats-processor/src/main.rs new file mode 100644 index 0000000000..ef50eaefac --- /dev/null +++ b/tools/heartbeats-processor/src/main.rs @@ -0,0 +1,175 @@ +use anyhow::Result; +use clap::{Parser, Subcommand}; +use firestore::FirestoreDb; +use sqlx::SqlitePool; + +mod config; +mod local_db; +mod remote_db; +mod time; + +use config::Config; +use remote_db::ScoreDocument; + +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Create database schema + InitDb, + /// Process heartbeats from Firestore + Process, + /// Toggle windows disabled state for a time range + ToggleWindows { + /// Start time in UTC (format: YYYY-MM-DD HH:MM:SS) + #[arg(long)] + start: String, + /// End time in UTC (format: YYYY-MM-DD HH:MM:SS) + #[arg(long)] + end: String, + #[arg(long)] + disabled: bool, + }, + /// View scores for all submitters + ViewScores, + /// Post scores to Firestore + PostScores, + /// Set the last processing time + SetLastProcessed { + /// Time in UTC (format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS) + #[arg(long)] + time: String, + }, + /// Create time windows for a given time range + CreateWindows { + /// Start time in UTC (format: YYYY-MM-DD HH:MM:SS) + #[arg(long)] + start: String, + /// End time in UTC (format: YYYY-MM-DD HH:MM:SS) + #[arg(long)] + end: String, + }, + /// Run continuous processing loop + ProcessLoop { + #[arg(long, default_value = "300")] + interval_seconds: u64, + }, +} + +async fn post_scores_to_firestore(pool: &SqlitePool, db: &FirestoreDb) -> Result<()> { + // Make sure scores are up to date + local_db::update_scores(pool).await?; + + let scores = sqlx::query!( + r#" + SELECT + pk.public_key, + ss.score, + ss.blocks_produced, + ss.last_updated + FROM submitter_scores ss + JOIN public_keys pk ON pk.id = ss.public_key_id + ORDER BY ss.score DESC + "# + ) + .fetch_all(pool) + .await?; + + let scores: Vec = scores + .into_iter() + .map(|row| ScoreDocument { + public_key: row.public_key, + score: row.score, + blocks_produced: row.blocks_produced, + last_updated: row.last_updated, + }) + .collect(); + + let max_scores = local_db::get_max_scores(pool).await?; + remote_db::post_scores(db, scores, (max_scores.current, max_scores.total)).await?; + + Ok(()) +} + +async fn run_process_loop( + pool: &SqlitePool, + db: &FirestoreDb, + interval_seconds: u64, +) -> Result<()> { + let interval = std::time::Duration::from_secs(interval_seconds); + + loop { + println!("Processing heartbeats..."); + local_db::process_heartbeats(db, pool).await?; + + println!("Posting scores..."); + post_scores_to_firestore(pool, db).await?; + + println!("Sleeping for {} seconds...", interval_seconds); + tokio::time::sleep(interval).await; + } +} + +#[tokio::main] +async fn main() -> Result<()> { + let config = Config::from_env()?; + println!("\n{}\n", config); + + let cli = Cli::parse(); + + local_db::ensure_db_exists(&config.database_url)?; + let pool = SqlitePool::connect(&config.database_url).await?; + + match cli.command { + Commands::InitDb => { + local_db::create_tables_from_file(&pool).await?; + local_db::ensure_initial_windows(&pool, &config).await?; + local_db::mark_disabled_windows(&pool, &config).await?; + } + Commands::Process => { + println!("Initializing firestore connection..."); + let db = remote_db::get_db(&config).await?; + local_db::create_tables_from_file(&pool).await?; + local_db::ensure_initial_windows(&pool, &config).await?; + local_db::mark_disabled_windows(&pool, &config).await?; + local_db::process_heartbeats(&db, &pool).await?; + println!("Processing completed successfully!"); + } + Commands::ToggleWindows { + start, + end, + disabled, + } => { + local_db::toggle_windows(&pool, start, end, disabled).await?; + } + Commands::ViewScores => { + local_db::view_scores(&pool).await?; + } + Commands::PostScores => { + println!("Initializing firestore connection..."); + let db = remote_db::get_db(&config).await?; + post_scores_to_firestore(&pool, &db).await?; + } + Commands::SetLastProcessed { time } => { + local_db::set_last_processed_time(&pool, &time).await?; + } + Commands::CreateWindows { start, end } => { + local_db::create_windows(&pool, start, end).await?; + } + Commands::ProcessLoop { interval_seconds } => { + println!("Initializing firestore connection..."); + let db = remote_db::get_db(&config).await?; + local_db::create_tables_from_file(&pool).await?; + local_db::ensure_initial_windows(&pool, &config).await?; + local_db::mark_disabled_windows(&pool, &config).await?; + run_process_loop(&pool, &db, interval_seconds).await?; + } + } + + Ok(()) +} diff --git a/tools/heartbeats-processor/src/remote_db.rs b/tools/heartbeats-processor/src/remote_db.rs new file mode 100644 index 0000000000..e38870d575 --- /dev/null +++ b/tools/heartbeats-processor/src/remote_db.rs @@ -0,0 +1,268 @@ +use std::sync::Arc; + +use anyhow::Result; +use base64::{engine::general_purpose, Engine as _}; +use chrono::{DateTime, Duration, Utc}; +use firestore::*; +use mina_p2p_messages::v2; +use openmina_core::block::{ArcBlockWithHash, BlockWithHash}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use crate::config::Config; + +const FIRESTORE_BATCH_SIZE: u32 = 1000; // Number of documents per batch +const MAX_TIME_CHUNK_HOURS: i64 = 24; + +#[derive(Debug, Serialize, Deserialize)] +pub struct SignatureJson { + pub field: String, + pub scalar: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct HeartbeatEntry { + pub version: u8, + pub payload: String, + pub submitter: String, + pub signature: SignatureJson, + #[serde(rename = "createTime")] + pub create_time: DateTime, + #[serde(skip_deserializing)] + pub decoded_payload: Option, +} + +#[derive(Debug)] +pub struct BlockInfo { + pub hash: String, + pub height: u64, + pub global_slot: u64, +} + +fn base64_decode_block( + encoded: &str, +) -> Result { + use base64::{engine::general_purpose::URL_SAFE, Engine as _}; + use mina_p2p_messages::binprot::BinProtRead; + + let decoded = URL_SAFE + .decode(encoded) + .map_err(|_| "Could not decode base64".to_string())?; + let block = v2::MinaBlockBlockStableV2::binprot_read(&mut &decoded[..]) + .map_err(|e| format!("Could not decode block: {:?}", e))?; + + Ok(block) +} + +impl HeartbeatEntry { + pub fn decode_payload(&mut self) -> Result<(), anyhow::Error> { + let decoded = general_purpose::URL_SAFE.decode(&self.payload)?; + let json_str = String::from_utf8(decoded)?; + self.decoded_payload = Some(serde_json::from_str(&json_str)?); + Ok(()) + } + + pub fn last_produced_block_raw(&self) -> Option { + self.decoded_payload + .as_ref() + .and_then(|status| status.get("last_produced_block")) + .and_then(|block| block.as_str()) + .map(|s| s.to_string()) + } + + pub fn last_produced_block_decoded(&self) -> Option { + let block = self + .last_produced_block_raw() + .map(|encoded| base64_decode_block(&encoded)) + .transpose() + .ok() + .flatten()?; + let block = BlockWithHash::try_new(Arc::new(block)).ok()?; + + Some(block) + } + + fn transition_frontier(&self) -> Option<&Value> { + self.decoded_payload + .as_ref() + .and_then(|decoded| decoded.get("status")) + .and_then(|status| status.get("transition_frontier")) + } + + fn best_tip(&self) -> Option<&Value> { + self.transition_frontier() + .and_then(|tf| tf.get("best_tip")) + .filter(|v| !v.is_null()) + } + + pub fn best_tip_block(&self) -> Option { + self.best_tip().map(|best_tip| BlockInfo { + hash: best_tip.get("hash").unwrap().as_str().unwrap().to_string(), + height: best_tip.get("height").unwrap().as_u64().unwrap(), + global_slot: best_tip.get("global_slot").unwrap().as_u64().unwrap(), + }) + } + + pub fn sync_status(&self) -> Option { + self.transition_frontier() + .and_then(|tf| tf.get("sync")) + .and_then(|sync| sync.get("status")) + .map(|status| status.as_str().unwrap().to_string()) + } + + pub fn is_synced(&self) -> bool { + self.sync_status() + .as_ref() + .map(|status| status == "Synced") + .unwrap_or(false) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ScoreDocument { + #[serde(rename = "publicKey")] + pub public_key: String, + pub score: i64, + #[serde(rename = "blocksProduced")] + pub blocks_produced: i64, + #[serde(rename = "lastUpdated")] + pub last_updated: i64, +} + +pub async fn get_db(config: &Config) -> Result { + if let Some(emulator_host) = &config.firestore_emulator_host { + // Using emulator + std::env::set_var("GOOGLE_CLOUD_PROJECT", "staging"); + let emulator_url = format!("http://{}", emulator_host); + let token_source = gcloud_sdk::TokenSourceType::Default; + Ok(FirestoreDb::with_options_token_source( + FirestoreDbOptions::new("staging".to_string()).with_firebase_api_url(emulator_url), + vec!["http://127.0.0.1:9099".to_string()], + token_source, + ) + .await?) + } else { + // Production mode - requires auth + Ok(FirestoreDb::new(&config.google_cloud_project).await?) + } +} + +pub async fn fetch_heartbeats_in_chunks( + db: &FirestoreDb, + start_time: DateTime, + end_time: DateTime, +) -> Result> { + let mut all_heartbeats = Vec::new(); + let chunk_duration = Duration::try_hours(MAX_TIME_CHUNK_HOURS).unwrap(); + let mut chunk_start = start_time; + + while chunk_start < end_time { + println!("Fetching heartbeat chunk... {chunk_start}"); + let chunk_end = (chunk_start + chunk_duration).min(end_time); + let mut last_timestamp = None; + + loop { + let query = db + .fluent() + .select() + .from("heartbeats") + .filter(|q| { + let mut conditions = vec![ + q.field("createTime").greater_than_or_equal( + firestore::FirestoreTimestamp::from(chunk_start), + ), + q.field("createTime") + .less_than(firestore::FirestoreTimestamp::from(chunk_end)), + ]; + + if let Some(ts) = &last_timestamp { + conditions.push( + q.field("createTime") + .greater_than(firestore::FirestoreTimestamp::from(*ts)), + ); + } + + q.for_all(conditions) + }) + .order_by([("createTime", FirestoreQueryDirection::Descending)]) + .limit(FIRESTORE_BATCH_SIZE); + + let batch: Vec = query.obj().query().await?; + let completed = batch.len() < FIRESTORE_BATCH_SIZE as usize; + + if batch.is_empty() { + break; + } + + last_timestamp = batch.last().map(|doc| doc.create_time); + all_heartbeats.extend(batch); + + if completed { + break; + } + } + + chunk_start = chunk_end; + } + + // Decode payloads after fetching + for heartbeat in &mut all_heartbeats { + if let Err(e) = heartbeat.decode_payload() { + eprintln!("Failed to decode payload: {:?}", e); + } + } + + Ok(all_heartbeats) +} + +pub async fn post_scores( + db: &FirestoreDb, + scores: Vec, + max_scores: (i64, i64), +) -> Result<()> { + let scores_count = scores.len(); + let now = FirestoreTimestamp::from(Utc::now()); + let (current_max, total_max) = max_scores; + + let mut transaction = db.begin_transaction().await?; + + // Store max scores in separate documents + db.fluent() + .update() + .in_col("maxScore") + .document_id("current") + .object(&serde_json::json!({ + "value": current_max, + "lastUpdated": now, + })) + .add_to_transaction(&mut transaction)?; + + db.fluent() + .update() + .in_col("maxScore") + .document_id("total") + .object(&serde_json::json!({ + "value": total_max, + "lastUpdated": now, + })) + .add_to_transaction(&mut transaction)?; + + // Per-key scores + for doc in scores { + db.fluent() + .update() + .in_col("scores") + .document_id(&doc.public_key) + .object(&doc) + .add_to_transaction(&mut transaction)?; + } + + println!( + "Successfully posted {scores_count} scores and max scores (current: {}, total: {}) to Firestore", + current_max, total_max + ); + + transaction.commit().await?; + + Ok(()) +} diff --git a/tools/heartbeats-processor/src/time.rs b/tools/heartbeats-processor/src/time.rs new file mode 100644 index 0000000000..25314d104e --- /dev/null +++ b/tools/heartbeats-processor/src/time.rs @@ -0,0 +1,51 @@ +use chrono::{DateTime, Duration, Utc}; + +const WINDOW_SIZE_MINUTES: i64 = 5; + +#[derive(Debug)] +pub struct TimeWindow { + pub start: DateTime, + pub end: DateTime, +} + +// Helper function to convert DateTime to Unix timestamp +pub fn to_unix_timestamp(dt: DateTime) -> i64 { + dt.timestamp() +} + +// Helper function to convert Unix timestamp to DateTime +pub fn from_unix_timestamp(ts: i64) -> DateTime { + DateTime::from_timestamp(ts, 0).unwrap() +} + +pub fn parse_datetime(s: &str) -> anyhow::Result> { + // Try parsing with different formats + if let Ok(dt) = DateTime::parse_from_str(&format!("{} +0000", s), "%Y-%m-%d %H:%M:%S %z") { + return Ok(dt.with_timezone(&Utc)); + } + + if let Ok(dt) = DateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%SZ") { + return Ok(dt.with_timezone(&Utc)); + } + + Err(anyhow::anyhow!( + "Invalid datetime format. Expected YYYY-MM-DD HH:MM:SS or YYYY-MM-DDThh:mm:ssZ" + )) +} + +pub fn generate_fixed_time_windows(start: DateTime, end: DateTime) -> Vec { + let window_duration = Duration::try_minutes(WINDOW_SIZE_MINUTES).unwrap(); + let mut windows = Vec::new(); + let mut current = start; + + while current < end { + let window_end = current + window_duration; + windows.push(TimeWindow { + start: current, + end: window_end, + }); + current = window_end; + } + + windows +}